Apps: Riverside/Whereby/Around (#3053)

* Add Whereby Boilerplate

* Whereby App

* around boilerplate

* AroundCo App

* Add Riverside App

* Add Riverside to details

* Fix URL validation

* Remove Icons

* Remove console.log

* revert merge

* Fix images

* Update Iconds to be SVG

* Add Disconnect button

* Fix type

* Fixing i18n merge

* Refactor app installations

* Update varient

* Update default responder + riverside logo

* Update apps/web/pages/event-types/[type].tsx

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
This commit is contained in:
sean-brydon
2022-06-27 12:17:38 +02:00
committed by GitHub
co-authored by Peer Richelsen kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Omar López
parent 44f0893674
commit 3449ad1a9f
50 changed files with 677 additions and 56 deletions
@@ -6,6 +6,7 @@ import { useEffect } from "react";
import { Controller, useForm, useWatch } from "react-hook-form";
import { z } from "zod";
import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui";
import { Dialog, DialogContent } from "@calcom/ui/Dialog";
@@ -79,7 +80,23 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
const locationFormSchema = z.object({
locationType: z.string(),
locationAddress: z.string().optional(),
locationLink: z.string().url().optional(),
locationLink:
selection?.value === LocationType.Whereby
? z
.string()
.regex(/^http(s)?:\/\/www.whereby.com\/[a-zA-Z0-9]*/)
.optional()
: selection?.value === LocationType.Around
? z
.string()
.regex(/^http(s)?:\/\/www.around.co\/[a-zA-Z0-9]*/)
.optional()
: selection?.value === LocationType.Riverside
? z
.string()
.regex(/^http(s)?:\/\/www.riverside.fm\/studio\/[a-zA-Z0-9]*/)
.optional()
: z.string().url().optional(),
displayLocationPublicly: z.boolean().optional(),
locationPhoneNumber: z
.string()
@@ -153,8 +170,8 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
<input
type="text"
{...locationFormMethods.register("locationLink")}
id="link"
required
id="link"
className="block w-full rounded-sm border-gray-300 sm:text-sm"
defaultValue={
defaultValues
@@ -215,6 +232,147 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
)}
</div>
</div>
) : selectedLocation === LocationType.Whereby ? (
<>
<div>
<label htmlFor="address" className="block text-sm font-medium text-gray-700">
{t("set_whereby_link")}
</label>
<div className="mt-1">
<input
type="text"
{...locationFormMethods.register("locationLink")}
id="wherebylink"
placeholder="https://www.whereby.com/cal"
required
className={"block w-full rounded-sm border-gray-300 text-sm"}
defaultValue={
defaultValues
? defaultValues.find(
(location: { type: LocationType }) => location.type === LocationType.Whereby
)?.address
: undefined
}
/>
</div>
{!booking && (
<div className="mt-3">
<Controller
name="displayLocationPublicly"
control={locationFormMethods.control}
render={() => (
<CheckboxField
defaultChecked={
defaultValues
? defaultValues.find((location) => location.type === LocationType.Whereby)
?.displayLocationPublicly
: undefined
}
description={t("display_location_label")}
onChange={(e) =>
locationFormMethods.setValue("displayLocationPublicly", e.target.checked)
}
informationIconText={t("display_location_info_badge")}></CheckboxField>
)}
/>
</div>
)}
</div>
</>
) : selectedLocation === LocationType.Around ? (
<>
<div>
<label htmlFor="address" className="block text-sm font-medium text-gray-700">
{t("set_around_link")}
</label>
<div className="mt-1">
<input
type="text"
{...locationFormMethods.register("locationLink")}
id="aroundlink"
placeholder="https://www.around.com/rick"
required
className={"block w-full rounded-sm border-gray-300 text-sm"}
defaultValue={
defaultValues
? defaultValues.find(
(location: { type: LocationType }) => location.type === LocationType.Around
)?.address
: undefined
}
/>
</div>
{!booking && (
<div className="mt-3">
<Controller
name="displayLocationPublicly"
control={locationFormMethods.control}
render={() => (
<CheckboxField
defaultChecked={
defaultValues
? defaultValues.find((location) => location.type === LocationType.Around)
?.displayLocationPublicly
: undefined
}
description={t("display_location_label")}
onChange={(e) =>
locationFormMethods.setValue("displayLocationPublicly", e.target.checked)
}
informationIconText={t("display_location_info_badge")}></CheckboxField>
)}
/>
</div>
)}
</div>
</>
) : selectedLocation === LocationType.Riverside ? (
<>
<div>
<label htmlFor="address" className="block text-sm font-medium text-gray-700">
{t("set_around_link")}
</label>
<div className="mt-1">
<input
type="text"
{...locationFormMethods.register("locationLink")}
id="aroundlink"
placeholder="https://www.riverside.fm/studio/rick"
required
className={"block w-full rounded-sm border-gray-300 text-sm"}
defaultValue={
defaultValues
? defaultValues.find(
(location: { type: LocationType }) => location.type === LocationType.Riverside
)?.address
: undefined
}
/>
</div>
{!booking && (
<div className="mt-3">
<Controller
name="displayLocationPublicly"
control={locationFormMethods.control}
render={() => (
<CheckboxField
defaultChecked={
defaultValues
? defaultValues.find((location) => location.type === LocationType.Riverside)
?.displayLocationPublicly
: undefined
}
description={t("display_location_label")}
onChange={(e) =>
locationFormMethods.setValue("displayLocationPublicly", e.target.checked)
}
informationIconText={t("display_location_info_badge")}></CheckboxField>
)}
/>
</div>
)}
</div>
</>
) : (
<p className="text-sm">{LocationOptionsToString(selectedLocation, t)}</p>
);
@@ -255,9 +413,15 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
displayLocationPublicly,
};
}
if (newLocation === LocationType.Link) {
if (
newLocation === LocationType.Link ||
newLocation === LocationType.Whereby ||
newLocation === LocationType.Around ||
newLocation === LocationType.Riverside
) {
details = { link: values.locationLink, displayLocationPublicly };
}
if (newLocation === LocationType.UserPhone) {
details = { hostPhoneNumber: values.locationPhoneNumber };
}
-1
View File
@@ -100,7 +100,6 @@ interface IntegrationsContainerProps {
const IntegrationsContainer = ({ variant, className = "" }: IntegrationsContainerProps): JSX.Element => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.integrations", { variant, onlyInstalled: true }], { suspense: true });
return (
<QueryCell
query={query}
+26 -1
View File
@@ -12,6 +12,7 @@ import {
TrashIcon,
UserAddIcon,
UsersIcon,
VideoCameraIcon,
} from "@heroicons/react/solid";
import { zodResolver } from "@hookform/resolvers/zod";
import { EventTypeCustomInput, MembershipRole, PeriodType, Prisma, SchedulingType } from "@prisma/client";
@@ -524,7 +525,10 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
if (
newLocationType === LocationType.InPerson ||
newLocationType === LocationType.Link ||
newLocationType === LocationType.UserPhone
newLocationType === LocationType.UserPhone ||
newLocationType === LocationType.Whereby ||
newLocationType === LocationType.Around ||
newLocationType === LocationType.Riverside
) {
openLocationModal(newLocationType);
} else {
@@ -570,6 +574,27 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
<span className="text-sm ltr:ml-2 rtl:mr-2">{t("phone_call")}</span>
</div>
)}
{location.type === LocationType.Whereby && (
<div className="flex flex-grow items-center">
<img src="/api/app-store/whereby/icon.svg" className="h-6 w-6" alt="whereby logo"></img>
<span className="text-sm ltr:ml-2 rtl:mr-2">{location.link}</span>
</div>
)}
{location.type === LocationType.Around && (
<div className="flex flex-grow items-center">
<img src="/api/app-store/around/icon.svg" className="h-6 w-6" alt="whereby logo"></img>
<span className="text-sm ltr:ml-2 rtl:mr-2">{location.link}</span>
</div>
)}
{location.type === LocationType.Riverside && (
<div className="flex flex-grow items-center">
<img
src="/api/app-store/riverside/icon.svg"
className="h-6 w-6"
alt="whereby logo"></img>
<span className="text-sm ltr:ml-2 rtl:mr-2">{location.link}</span>
</div>
)}
{location.type === LocationType.GoogleMeet && (
<div className="flex flex-grow items-center">
<svg
@@ -907,6 +907,12 @@
"are_you_sure_you_want_to_remove_this_app": "Are you sure you want to remove this app?",
"web_conference": "Web conference",
"requires_confirmation": "Requires confirmation",
"set_whereby_link":"Set Whereby link",
"invalid_whereby_link": "Please enter a valid Whereby Link",
"set_around_link":"Set Around.Co link",
"invalid_around_link": "Please enter a valid Around Link",
"set_riverside_link":"Set Riverside link",
"invalid_riverside_link": "Please enter a valid Riverside Link",
"add_exchange2013": "Connect Exchange 2013 Server",
"add_exchange2016": "Connect Exchange 2016 Server",
"specific_issue": "Have a specific issue",
@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { defaultResponder } from "@calcom/lib/server";
import checkSession from "../../_utils/auth";
import { checkInstalled, createDefaultInstallation } from "../../_utils/installation";
import appConfig from "../config.json";
export async function getHandler(req: NextApiRequest, res: NextApiResponse) {
const session = checkSession(req);
const slug = appConfig.slug;
const appType = appConfig.type;
await checkInstalled(slug, session.user.id);
await createDefaultInstallation({
appType,
userId: session.user.id,
slug,
key: {},
});
return { url: "/apps/installed" };
}
export default defaultResponder(getHandler);
+4 -48
View File
@@ -1,49 +1,5 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { defaultHandler } from "@calcom/lib/server";
import prisma from "@calcom/prisma";
import appConfig from "../config.json";
// TODO: There is a lot of code here that would be used by almost all apps
// - Login Validation
// - Looking up credential.
// - Creating credential would be specific to app, so there can be just createCredential method that app can expose
// - Redirection after successful installation can also be configured by app
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (!req.session?.user?.id) {
return res.status(401).json({ message: "You must be logged in to do this" });
}
// TODO: Define appType once and import everywhere
const slug = appConfig.slug;
const appType = appConfig.type;
try {
const alreadyInstalled = await prisma.credential.findFirst({
where: {
appId: slug,
userId: req.session.user.id,
},
});
if (alreadyInstalled) {
throw new Error("Already installed");
}
const installation = await prisma.credential.create({
data: {
type: appType,
key: {},
userId: req.session.user.id,
appId: slug,
},
});
if (!installation) {
throw new Error(`Unable to create user credential for type ${type}`);
}
} catch (error: unknown) {
if (error instanceof Error) {
console.error(error.message);
return res.status(500).json({ message: error.message });
}
return res.status(500);
}
return res.status(200).json({ url: "/apps/installed" });
}
export default defaultHandler({
GET: import("./_getAdd"),
});
+10
View File
@@ -0,0 +1,10 @@
import type { NextApiRequest } from "next";
import { HttpError } from "@calcom/lib/http-error";
export default function checkSession(req: NextApiRequest) {
if (!req.session?.user?.id) {
throw new HttpError({ statusCode: 401, message: "Unauthorized" });
}
return req.session;
}
+41
View File
@@ -0,0 +1,41 @@
import type { Prisma } from "@prisma/client";
import { HttpError } from "@calcom/lib/http-error";
import prisma from "@calcom/prisma";
export async function checkInstalled(slug: string, userId: number) {
const alreadyInstalled = await prisma.credential.findFirst({
where: {
appId: slug,
userId: userId,
},
});
if (alreadyInstalled) {
throw new HttpError({ statusCode: 422, message: "Already installed" });
}
}
export async function createDefaultInstallation({
appType,
userId,
slug,
key = {},
}: {
appType: string;
userId: number;
slug: string;
key?: Prisma.InputJsonValue;
}) {
const installation = await prisma.credential.create({
data: {
type: appType,
key,
userId,
appId: slug,
},
});
if (!installation) {
throw new Error(`Unable to create user credential for type ${appType}`);
}
return installation;
}
+8
View File
@@ -0,0 +1,8 @@
---
items:
- /api/app-store/around/around1.webp
---
<Slider items={items} />
Discover radically unique video calls designed to help hybrid-remote teams create, collaborate and celebrate together.
+19
View File
@@ -0,0 +1,19 @@
import { LocationType } from "@calcom/core/location";
import type { App } from "@calcom/types/App";
import config from "./config.json";
export const metadata = {
category: "other",
// FIXME: Currently for an app to be shown as installed, it must have this variable set. Either hardcoded or if it depends on some env variable, that should be checked here
installed: true,
rating: 0,
reviews: 0,
trending: true,
verified: true,
locationType: LocationType.Around,
locationLabel: "Around Video",
...config,
} as App;
export default metadata;
+25
View File
@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { defaultResponder } from "@calcom/lib/server";
import checkSession from "../../_utils/auth";
import { checkInstalled, createDefaultInstallation } from "../../_utils/installation";
import appConfig from "../config.json";
export async function getHandler(req: NextApiRequest, res: NextApiResponse) {
const session = checkSession(req);
const slug = appConfig.slug;
const appType = appConfig.type;
await checkInstalled(slug, session.user.id);
await createDefaultInstallation({
appType,
userId: session.user.id,
slug,
key: {},
});
return { url: "/apps/installed" };
}
export default defaultResponder(getHandler);
+5
View File
@@ -0,0 +1,5 @@
import { defaultHandler } from "@calcom/lib/server";
export default defaultHandler({
GET: import("./_getAdd"),
});
+1
View File
@@ -0,0 +1 @@
export { default as add } from "./add";
@@ -0,0 +1,19 @@
import type { InstallAppButtonProps } from "@calcom/app-store/types";
import useAddAppMutation from "../../_utils/useAddAppMutation";
import appConfig from "../config.json";
export default function InstallAppButton(props: InstallAppButtonProps) {
const mutation = useAddAppMutation("around_video");
return (
<>
{props.render({
onClick() {
mutation.mutate("");
},
loading: mutation.isLoading,
})}
</>
);
}
@@ -0,0 +1 @@
export { default as InstallAppButton } from "./InstallAppButton";
+16
View File
@@ -0,0 +1,16 @@
{
"/*": "Don't modify slug - If required, do it using cli edit command",
"name": "Around",
"title": "Around",
"slug": "around",
"type": "around_video",
"imageSrc": "/api/app-store/around/icon.svg",
"logo": "/api/app-store/around/icon.svg",
"url": "https://cal.com/apps/around",
"variant": "conferencing",
"categories": ["video"],
"publisher": "Cal.com",
"email": "help@cal.com",
"description": "Discover radically unique video calls designed to help hybrid-remote teams create, collaborate and celebrate together.",
"__createdUsingCli": true
}
+3
View File
@@ -0,0 +1,3 @@
export * as api from "./api";
export * as components from "./components";
export { metadata } from "./_metadata";
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"name": "@calcom/around",
"version": "0.0.0",
"main": "./index.ts",
"description": "Discover radically unique video calls designed to help hybrid-remote teams create, collaborate and celebrate together.",
"dependencies": {
"@calcom/lib": "*"
},
"devDependencies": {
"@calcom/types": "*"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

+15
View File
@@ -0,0 +1,15 @@
<svg width="326" height="218" viewBox="0 0 326 218" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M254.422 100.655L251.422 95.6551L249.422 94.6551H248.422L247.422 96.6551L245.422 93.6551L249.422 88.6551H246.422V87.6551L251.422 85.6551C251.012 65.8501 275.185 49.8181 275.422 29.6551C281.938 27.4911 287.784 11.9091 289.422 5.6551L305.422 11.6551C285.484 -7.4233 249.601 -1.6615 232.89 18.6591C217.567 37.2901 217.397 65.6011 232.103 84.6551C237.927 92.2021 245.735 97.0381 254.422 100.655Z" fill="#BC45B0"/>
<path d="M30.4224 95.6551C33.1044 99.2261 37.2596 99.4561 41.4224 98.6551L45.4224 100.655V99.6551H43.4224V98.6551L61.4224 94.6551V93.6551H53.4224V92.6551H59.4224L58.4224 88.6551C65.0064 83.9361 70.3954 69.5191 70.4224 61.6551H73.4224C75.3534 53.9071 81.5524 42.0621 87.4224 36.6551L86.4224 33.6551L89.4224 35.6551H90.4224L86.4224 29.6551L88.4224 27.6551L86.4224 22.6551L88.4224 23.6551C61.5744 -11.6715 4.90777 3.88079 0.592173 48.6551C-0.979227 64.9571 10.6359 95.7931 30.4224 95.6551ZM154.422 3.65509L155.422 4.65509L154.422 3.65509Z" fill="#2D99EA"/>
<path d="M159.422 100.655C156.838 94.9811 157.606 91.4601 159.422 85.6551H160.422V100.655C165.409 99.0001 164.422 94.1951 164.422 89.6551C164.422 82.5361 160.982 62.5531 166.164 57.2581C169.725 53.6211 180.677 55.6551 185.422 55.6551V53.6551L178.422 52.6551C183.113 42.0121 189.805 32.3381 194.738 21.6551C196.932 16.9031 198.013 11.3471 203.422 9.65507V5.65507C210.27 7.73377 208.422 15.7791 208.422 21.6551C209.946 18.0551 211.985 8.41717 208.818 5.25767C205.545 1.99317 196.667 3.65507 192.422 3.65507C177.325 3.65507 159.88 1.25047 145.422 6.35417C114.318 17.3341 113.422 45.9141 113.422 73.6551C113.422 78.8941 110.673 96.1801 115.025 99.6271C117.807 101.83 124.094 100.655 127.422 100.655H159.422Z" fill="#5663D9"/>
<path d="M203.422 5.65509V9.65509C197.804 10.6211 196.887 15.9991 194.738 20.6551C189.836 31.2701 181.722 41.6131 178.422 52.6551C185.79 54.2491 201.255 59.9031 207.988 54.0521C211.089 51.3571 210.421 46.3221 210.422 42.6551V13.6551H209.422V21.6551C207.744 15.9791 210.865 6.89729 203.422 5.65509Z" fill="#BC45B0"/>
<path d="M289.422 5.65509C286.546 12.5581 282.218 25.9801 275.422 29.6551C275.061 41.0371 264.753 56.4611 258.968 66.0791C255.724 71.4721 250.414 79.2761 251.422 85.6551L246.422 88.6551H249.422V89.6551L245.422 93.6551L248.422 94.6551H249.422L251.422 95.6551L254.422 101.655L257.422 100.655V102.655L261.422 101.655C261.422 104.281 263.185 103.446 265.422 102.655C267.971 105.572 272.746 104.731 276.422 104.64C287.422 104.37 299.16 99.7151 307.422 92.4701C335.781 67.6031 330.738 12.8261 289.422 5.65509ZM297.422 5.65509L298.422 6.65509L297.422 5.65509ZM246.422 6.65509L247.422 7.65509L246.422 6.65509ZM299.422 6.65509L300.422 7.65509L299.422 6.65509ZM300.422 7.65509L301.422 8.65509L300.422 7.65509Z" fill="#F55857"/>
<path d="M86.4224 22.6551V29.6551L90.4224 35.6551H89.4224L86.4224 33.6551L87.4224 36.6551C81.3674 40.4521 74.4264 54.5951 73.4224 61.6551H70.4224C70.3284 70.0711 64.2364 82.5501 58.4224 88.6551L59.4224 92.6551H53.4224V93.6551H61.4224V94.6551L43.4224 98.6551V99.6551C55.6197 102.559 69.9164 100.655 82.4224 100.655C85.9534 100.655 92.8794 101.956 95.8194 99.6271C99.5734 96.6541 97.4224 84.0361 97.4224 79.6551C97.4224 61.9341 101.676 35.6351 86.4224 22.6551ZM84.7554 24.3221L85.0894 24.9881L84.7554 24.3221Z" fill="#5663D9"/>
<path d="M111.422 202.655H112.422C112.422 179.81 122.625 164.69 132.129 144.67C135.116 138.376 141.815 130.34 142.422 123.655L148.625 116.78L159.422 112.655C152.105 114.014 145.21 114.531 138.422 117.925C105.299 134.487 111.422 171.736 111.422 202.655Z" fill="#5663D9"/>
<path d="M159.422 112.655L147.777 116.815L142.422 123.655C140.044 136.739 127.321 150.579 121.866 162.651C114.209 179.595 112.411 193.837 111.422 212.655L141.422 213.655H202.422V212.655C198.838 212.655 191.089 214.207 190.422 209.655H185.422V208.655L189.422 207.655L205.422 173.655H206.422L207.422 198.655C210.123 192.219 208.422 182.606 208.422 175.655C208.422 151.554 204.299 127.728 179.422 116.993C172.952 114.201 166.253 113.833 159.422 112.655Z" fill="#BC45B0"/>
<path d="M0.422375 175.655H1.42238V166.655H2.42238V184.655H3.42238V182.655H4.42238V189.655H5.42238L4.42238 185.655L8.42238 190.655H9.42238L8.42238 181.655H13.4224C13.7901 176.059 16.7554 167.374 22.4224 165.655V157.655H25.4224C28.2315 146.85 35.2776 132.41 43.4224 124.655L41.4224 120.655L45.4224 119.655V118.655H33.4224V117.655H57.4224C46.2045 112.948 26.6283 116.655 14.4224 116.655C10.7318 116.655 3.85378 115.371 1.45018 118.973C-1.02942 122.689 0.422375 130.37 0.422375 134.655V175.655Z" fill="#2D99EA"/>
<path d="M33.4224 117.655V118.655H45.4224V119.655L41.4224 120.655L43.4224 124.655C34.9301 129.237 33.6138 140.337 29.7835 148.433C28.1052 151.98 25.7223 153.456 25.4224 157.655H22.4224V165.655C16.1837 166.695 13.5917 175.943 13.4224 181.655H8.42237L9.42237 190.655H8.42237L4.42237 185.655C12.612 220.117 58.4578 227.134 82.4184 205.481C98.2224 191.2 97.4224 172.14 97.4224 152.655V126.655C97.4214 123.753 98.2434 118.998 95.1044 117.397C90.5954 115.099 82.3794 116.655 77.4224 116.655C62.7084 116.655 48.1453 117.655 33.4224 117.655Z" fill="#5663D9"/>
<path d="M224.422 156.655H225.422L228.422 126.655C231.665 126.173 231.729 122.576 231.422 119.655L243.422 118.655C239.842 117.153 227.144 115.411 224.677 119.173C222.746 122.116 224.276 127.37 224.387 130.655C224.676 139.304 224.422 148.001 224.422 156.655Z" fill="#BC45B0"/>
<path d="M229.422 116.655V117.655H243.422V118.655L231.422 119.655C231.9 122.043 231.161 127.34 228.422 126.655L225.422 156.655H224.422V125.655C220.114 135.921 223.422 153.497 223.422 164.655V198.655C223.422 202.186 222.121 209.112 224.45 212.052C226.313 214.405 230.805 213.654 233.422 213.655H258.422C276.419 213.655 293.757 213.835 307.133 199.565C334.867 169.975 316.732 122.715 277.422 117.083C261.938 114.865 245.061 116.655 229.422 116.655ZM223.422 118.655L224.422 119.655L223.422 118.655Z" fill="#F55857"/>
<path d="M205.422 173.655L189.422 207.655L185.422 208.655V209.655H190.422C191.022 214.433 203.028 212.989 207.422 213.655C209.751 204.66 208.983 182.141 205.422 173.655Z" fill="#F55857"/>
</svg>

After

Width:  |  Height:  |  Size: 6.0 KiB

+3
View File
@@ -14,6 +14,9 @@ export enum AppStoreLocationType {
Huddle01 = "integrations:huddle01",
Tandem = "integrations:tandem",
Teams = "integrations:office365_video",
Whereby = "integrations:whereby_video",
Around = "integrations:around_video",
Riverside = "integrations:riverside_video",
}
export type LocationObject = {
+7
View File
@@ -0,0 +1,7 @@
---
items:
- /api/app-store/riverside/riverside1.png
---
<Slider items={items} />
Your online recording studio.The easiest way to record podcasts and videos in studio quality from anywhere. All from the browser.
+19
View File
@@ -0,0 +1,19 @@
import { LocationType } from "@calcom/core/location";
import type { App } from "@calcom/types/App";
import config from "./config.json";
export const metadata = {
category: "other",
// FIXME: Currently for an app to be shown as installed, it must have this variable set. Either hardcoded or if it depends on some env variable, that should be checked here
installed: true,
rating: 0,
reviews: 0,
trending: true,
verified: true,
locationType: LocationType.Riverside,
locationLabel: "Riverside Video",
...config,
} as App;
export default metadata;
@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { defaultResponder } from "@calcom/lib/server";
import checkSession from "../../_utils/auth";
import { checkInstalled, createDefaultInstallation } from "../../_utils/installation";
import appConfig from "../config.json";
export async function getHandler(req: NextApiRequest, res: NextApiResponse) {
const session = checkSession(req);
const slug = appConfig.slug;
const appType = appConfig.type;
await checkInstalled(slug, session.user.id);
await createDefaultInstallation({
appType,
userId: session.user.id,
slug,
key: {},
});
return { url: "/apps/installed" };
}
export default defaultResponder(getHandler);
+5
View File
@@ -0,0 +1,5 @@
import { defaultHandler } from "@calcom/lib/server";
export default defaultHandler({
GET: import("./_getAdd"),
});
@@ -0,0 +1 @@
export { default as add } from "./add";
@@ -0,0 +1,19 @@
import type { InstallAppButtonProps } from "@calcom/app-store/types";
import useAddAppMutation from "../../_utils/useAddAppMutation";
import appConfig from "../config.json";
export default function InstallAppButton(props: InstallAppButtonProps) {
const mutation = useAddAppMutation("riverside_video");
return (
<>
{props.render({
onClick() {
mutation.mutate("");
},
loading: mutation.isLoading,
})}
</>
);
}
@@ -0,0 +1 @@
export { default as InstallAppButton } from "./InstallAppButton";
+16
View File
@@ -0,0 +1,16 @@
{
"/*": "Don't modify slug - If required, do it using cli edit command",
"name": "riverside",
"title": "Riverside",
"slug": "riverside",
"type": "riverside_video",
"imageSrc": "/api/app-store/riverside/icon.svg",
"logo": "/api/app-store/riverside/icon.svg",
"url": "https://cal.com/apps/riverside",
"variant": "conferencing",
"categories": ["video"],
"publisher": "Cal.com",
"email": "help@cal.com",
"description": "Your online recording studio.The easiest way to record podcasts and videos in studio quality from anywhere. All from the browser.",
"__createdUsingCli": true
}
+3
View File
@@ -0,0 +1,3 @@
export * as api from "./api";
export * as components from "./components";
export { metadata } from "./_metadata";
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"name": "@calcom/riverside",
"version": "0.0.0",
"main": "./index.ts",
"description": "Your online recording studio.The easiest way to record podcasts and videos in studio quality from anywhere. All from the browser.",
"dependencies": {
"@calcom/lib": "*"
},
"devDependencies": {
"@calcom/types": "*"
}
}
@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?>
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="300.000000pt" height="300.000000pt" viewBox="0 0 300.000000 300.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,300.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M203 2982 c-100 -35 -171 -115 -193 -216 -8 -36 -10 -416 -8 -1296
l3 -1245 28 -57 c32 -64 80 -111 146 -142 l46 -21 1275 0 1275 0 57 28 c64 32
111 80 142 146 l21 46 0 1275 0 1275 -21 46 c-31 66 -78 114 -142 146 l-57 28
-1260 2 c-1189 2 -1263 1 -1312 -15z m1108 -1008 c101 -51 195 -203 284 -464
78 -225 156 -350 220 -350 61 0 163 141 239 329 14 35 34 70 43 77 28 21 79
17 108 -9 38 -32 35 -74 -13 -185 -112 -259 -233 -382 -377 -382 -159 0 -274
141 -391 480 -78 228 -163 363 -227 363 -34 0 -79 -34 -123 -91 -36 -47 -144
-256 -144 -279 0 -19 -51 -53 -80 -53 -34 0 -76 32 -84 64 -7 28 23 114 74
216 86 170 197 283 305 311 44 11 113 0 166 -27z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 973 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

+9
View File
@@ -0,0 +1,9 @@
---
items:
- /api/app-store/whereby/whereby1.webp
- /api/app-store/whereby/whereby2.webp
---
<Slider items={items} />
Whereby's the easiest way to connect over video with no app or software download required. Connect with anyone, anywhere with zero hassle.
+19
View File
@@ -0,0 +1,19 @@
import { LocationType } from "@calcom/core/location";
import type { App } from "@calcom/types/App";
import config from "./config.json";
export const metadata = {
category: "other",
// FIXME: Currently for an app to be shown as installed, it must have this variable set. Either hardcoded or if it depends on some env variable, that should be checked here
installed: true,
rating: 0,
reviews: 0,
trending: true,
verified: true,
locationType: LocationType.Whereby,
locationLabel: "Whereby Video",
...config,
} as App;
export default metadata;
+25
View File
@@ -0,0 +1,25 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { defaultResponder } from "@calcom/lib/server";
import checkSession from "../../_utils/auth";
import { checkInstalled, createDefaultInstallation } from "../../_utils/installation";
import appConfig from "../config.json";
export async function getHandler(req: NextApiRequest, res: NextApiResponse) {
const session = checkSession(req);
const slug = appConfig.slug;
const appType = appConfig.type;
await checkInstalled(slug, session.user.id);
await createDefaultInstallation({
appType,
userId: session.user.id,
slug,
key: {},
});
return { url: "/apps/installed" };
}
export default defaultResponder(getHandler);
+5
View File
@@ -0,0 +1,5 @@
import { defaultHandler } from "@calcom/lib/server";
export default defaultHandler({
GET: import("./_getAdd"),
});
+1
View File
@@ -0,0 +1 @@
export { default as add } from "./add";
@@ -0,0 +1,18 @@
import type { InstallAppButtonProps } from "@calcom/app-store/types";
import useAddAppMutation from "../../_utils/useAddAppMutation";
export default function InstallAppButton(props: InstallAppButtonProps) {
const mutation = useAddAppMutation("whereby_video");
return (
<>
{props.render({
onClick() {
mutation.mutate("");
},
loading: mutation.isLoading,
})}
</>
);
}
@@ -0,0 +1 @@
export { default as InstallAppButton } from "./InstallAppButton";
+16
View File
@@ -0,0 +1,16 @@
{
"/*": "Don't modify slug - If required, do it using cli edit command",
"name": "Whereby",
"title": "Whereby",
"slug": "whereby",
"type": "whereby_video",
"imageSrc": "/api/app-store/whereby/icon.svg",
"logo": "/api/app-store/whereby/icon.svg",
"url": "https://cal.com/apps/whereby",
"variant": "conferencing",
"categories": ["video"],
"publisher": "Cal.com",
"email": "help@cal.com",
"description": "Whereby makes it super simple for collaborating teams to jump on a video call.",
"__createdUsingCli": true
}
+3
View File
@@ -0,0 +1,3 @@
export * as api from "./api";
export * as components from "./components";
export { metadata } from "./_metadata";
+14
View File
@@ -0,0 +1,14 @@
{
"$schema": "https://json.schemastore.org/package.json",
"private": true,
"name": "@calcom/whereby",
"version": "0.0.0",
"main": "./index.ts",
"description": "Whereby makes it super simple for collaborating teams to jump on a video call.",
"dependencies": {
"@calcom/lib": "*"
},
"devDependencies": {
"@calcom/types": "*"
}
}
@@ -0,0 +1,5 @@
<svg width="225" height="194" viewBox="0 0 225 194" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M122 0C111.165 12.6969 97.216 11 82 11H0L15.3596 31L55.2253 126L83 194C89.6476 189.977 93.162 175.16 95.939 168C102.189 151.888 109.18 136.05 115.6 120C118.55 112.627 121.059 102.427 127 97L139.397 129L166 194H168C179.451 160.152 194.728 127.184 208 94C215.76 74.5976 225.481 54.4307 223.91 33C222.153 9.039 200.34 -5.05637 178 4.0941C162.109 10.603 147.708 28.6561 143 45H141C137.245 31.2703 130.585 11.3441 122 0Z" fill="#020202"/>
<path d="M99 140C106.153 129.228 109.609 114.887 114.719 103C116.824 98.105 120.848 91.4555 120.436 86C119.51 73.7292 109.69 58.3931 104.989 47C98.526 31.337 83.6322 6.45451 63.0432 19.9946C51.9447 27.2934 58.4592 42.5507 62.4244 52C74.4729 80.7126 83.6101 112.957 99 140Z" fill="#F7F7F7"/>
<path d="M181 139C185.865 132.978 187.766 124.144 190.601 117C198.802 96.331 213.176 64.1847 198.892 43.0147C188.503 27.618 150.597 31.2788 145.789 51C144.199 57.521 152.287 69.9822 154.85 76C163.748 96.89 172.79 117.831 181 139Z" fill="#F7F7F7"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1 @@
<svg width="100%" height="100%" viewBox="0 0 86 22" xmlns="http://www.w3.org/2000/svg" class="index--Wordmark--eqdmz index--Logo--large--1dGR7" aria-label="Whereby"><g><path d="m16.8473.816156c-1.5003 0-3.1075 1.154764-4.3984 3.875224l-1.6591-3.980828h-.1058c-.275.633628-1.12295.972248-1.92717.972248h-8.72558v.16874c.698369.29615 1.31275 1.30972 1.88456 2.68373l5.42242 13.10073h.16913l3.52404-8.5276 3.5287 8.5276h.1691l3.0075-7.4807c1.1643-2.93745 1.9271-4.56399 1.9271-6.02179 0-1.9445-1.0792-3.317354-2.8165-3.317354zm-8.13419 12.289144-3.51485-8.54937c-.57181-1.39467-.04257-2.38758 1.39789-2.38758 1.27018 0 1.90642.84484 2.54151 2.36692l1.55324 3.75125zm8.43109-3.20372-1.2265 3.11072-3.2583-7.817c.7985-1.15362 1.8316-1.59096 3.0224-1.59096 1.4186 0 2.2666.97225 2.2666 2.7262 0 .97225-.2543 2.17752-.8042 3.57104z"></path><path d="m62.7222 9.28982c0-1.73214-1.7787-3.00054-4.0453-3.00054-3.4308 0-5.7607 2.95807-5.7607 6.06422 0 3.3805 2.5841 5.4099 5.1464 5.4099 2.0548 0 4.0452-1.2466 4.6596-3.4229l-.1484-.1263c-.6984 1.0354-1.9064 1.6059-3.1134 1.6059-1.5888 0-2.8809-.9091-3.4941-2.5357 3.2825-.5291 6.7559-1.4176 6.7559-3.99458zm-6.9043 3.48608c-.1266-.5073-.191-1.056-.191-1.6265 0-2.74688 1.1643-4.28963 2.6048-4.28963 1.1229 0 1.4612.95044 1.4612 1.96516 0 2.11207-1.9053 3.52857-3.875 3.95097z"></path><path d="m83.6263 5.97128c-1.46 0-2.859 1.37401-3.7208 3.22782l-1.4474-3.03842h-.1691c-.275 1.03538-1.567 1.62654-2.9868 1.60588v.16873c.8894.21121 1.2702 1.05605 1.8213 2.17641l3.7485 7.7125-.2543.4649c-.8054 1.5852-1.3553 2.1557-1.9271 2.1557-1.0378 0-1.123-1.5003-2.3724-1.5003-.741 0-1.3554.5074-1.3554 1.3947 0 .9929 1.1011 1.6265 2.2447 1.6265 1.6729 0 2.7107-1.3097 3.8336-3.4229l2.9016-5.4099c1.1644-2.1764 1.8006-3.1268 1.8006-4.75453.0012-1.30742-.7616-2.40709-2.117-2.40709zm-.1909 6.88952-.932 1.7964-2.3919-5.0219c.4579-.81155 1.2633-1.46584 2.4552-1.46584 1.1229 0 1.7154.69676 1.7154 1.83894.0012.7817-.2749 1.7528-.8467 2.8524z"></path><path d="m31.4821 15.2484v-6.52915c0-1.4578-.9112-2.43005-2.2665-2.43005-1.6303 0-3.6851 1.47961-4.7862 2.55631v-8.8110862h-.1484c-.6569 1.0353862-2.3505 1.6690062-3.982 1.6265362v.16874c.932.25368 1.2069.90912 1.2069 2.17637v11.24113c0 .9505-.4234 1.7115-1.1861 2.0283v.1687h5.3372v-.1687c-.7835-.4442-1.2287-1.0778-1.2287-1.9858v-5.8955c1.2494-.90912 2.2032-1.3522 2.9016-1.3522.7835 0 1.2288.57049 1.2288 1.73214v5.51556c0 .9091-.4027 1.5209-1.2288 1.9858v.1687h5.3373v-.1687c-.7617-.3157-1.1851-1.0767-1.1851-2.0271z"></path><path d="m71.1303 6.2892c-1.5244 0-2.9868 1.12033-3.791 1.98583v-8.2406062h-.1265c-.7628 1.0353862-2.5208 1.6690162-4.0879 1.6265362v.16874c.8687.25368 1.2702.92978 1.2702 2.17637v13.65053h.191l1.3979-1.5634c.6144.9723 1.7787 1.5634 3.1766 1.5634 3.516 0 5.9298-3.0005 5.9298-6.2754 0-3.00057-1.8845-5.092-3.9601-5.092zm-2.0548 10.7969c-1.0378 0-1.7362-.8448-1.7362-2.0076v-6.21232c.6984-.61297 1.4831-1.18346 2.3506-1.16165 1.483.02066 2.3298 1.69082 2.3505 4.24717.0218 2.8949-1.1643 5.1344-2.9649 5.1344z"></path><path d="m51.2641 6.28927c-1.1643 0-2.4356 1.2466-3.0282 2.43006v-2.55632h-.1691c-.7202 1.03538-2.4149 1.64835-4.0027 1.62654v.16874c.8687.25368 1.2921.95043 1.2921 2.17641v5.0288c0 .9722-.4234 1.7321-1.2702 2.1132v.1688h5.4638v-.1688c-.8686-.3799-1.2494-1.141-1.2494-2.1132v-5.91615c.3175-.40176.6776-.82418 1.101-.82418.7835 0 1.1011.95044 2.0756.95044.6776 0 1.3771-.54983 1.3771-1.47961-.0023-.92863-.7432-1.60473-1.59-1.60473z"></path><path d="m43.0884 9.28982c0-1.73214-1.7788-3.00054-4.0453-3.00054-3.4309 0-5.7607 2.95807-5.7607 6.06422 0 3.3805 2.5841 5.4099 5.1463 5.4099 2.0549 0 4.0453-1.2466 4.6597-3.4229l-.1485-.1263c-.6983 1.0354-1.9064 1.6059-3.1133 1.6059-1.5889 0-2.8809-.9091-3.4941-2.5357 3.2824-.5291 6.7559-1.4176 6.7559-3.99458zm-6.9043 3.48608c-.1266-.5073-.191-1.056-.191-1.6265 0-2.74688 1.1643-4.28963 2.6048-4.28963 1.1229 0 1.483.95044 1.483 1.96516 0 2.11207-1.9271 3.52857-3.8968 3.95097z"></path></g></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

-1
View File
@@ -4,7 +4,6 @@ declare global {
// eslint-disable-next-line no-var
var perfObserver: PerformanceObserver | undefined;
}
export const perfObserver =
globalThis.perfObserver ||
new PerformanceObserver((items) => {
+24 -1
View File
@@ -1 +1,24 @@
[]
[
{
"/*": "This file is auto-generated and managed by `yarn app-store`. Don't edit manually but it is to be committed",
"dirName": "whereby",
"categories": ["video"],
"slug": "whereby",
"type": "whereby_video"
},
{
"/*": "This file is auto-generated and managed by `yarn app-store`. Don't edit manually but it is to be committed",
"dirName": "around",
"categories": ["video"],
"slug": "around",
"type": "around_video"
},
{
"/*": "This file is auto-generated and managed by `yarn app-store`. Don't edit manually but it is to be committed",
"dirName": "riverside",
"categories": ["video"],
"slug": "riverside",
"type": "riverside_video"
}
]
+1 -1
View File
@@ -31,7 +31,7 @@ export interface App {
/** The icon to display in /apps/installed */
imageSrc: string;
/** TODO determine if we should use this instead of category */
variant: "calendar" | "payment" | "conferencing" | "other" | "other_calendar";
variant: "calendar" | "payment" | "conferencing" | "video" | "other" | "other_calendar";
/** The slug for the app store public page inside `/apps/[slug] */
slug: string;
/** The category to which this app belongs, currently we have `calendar`, `payment` or `video` */