feat: jelly seamless signup (#15220)

* feat: jelly seamless signup

* fix: wrong import

* add credentialId id to defaultLocation

* add CredentialId and correct appType

* fix: type error
This commit is contained in:
Somay Chauhan authored and GitHub committed 2024-05-28 17:54:59 +00:00
1 parent 889ff177c1
commit 33c1d49c52
8 files changed
+136 -35

No files matched your search

+8
View File
@@ -7,6 +7,7 @@ import { AppDependencyComponent, InstallAppButton } from "@calcom/app-store/comp
import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration";
import classNames from "@calcom/lib/classNames";
import { APP_NAME, COMPANY_NAME, SUPPORT_MAIL_ADDRESS } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { App as AppType } from "@calcom/types/App";
@@ -72,6 +73,8 @@ export const AppPage = ({
dirName,
}: AppPageProps) => {
const { t, i18n } = useLocale();
const searchParams = useCompatSearchParams();
const hasDescriptionItems = descriptionItems && descriptionItems.length > 0;
const mutation = useAddAppMutation(null, {
@@ -120,6 +123,11 @@ export const AppPage = ({
// variant not other allows, an app to be shown in calendar category without requiring an actual calendar connection e.g. vimcal
// Such apps, can only be installed once.
const allowedMultipleInstalls = categories.indexOf("calendar") > -1 && variant !== "other";
useEffect(() => {
if (searchParams?.get("defaultInstall") === "true") {
mutation.mutate({ type, variant, slug, defaultInstall: true });
}
}, []);
return (
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
@@ -0,0 +1,57 @@
import type { LocationObject } from "@calcom/app-store/locations";
import { getAppFromSlug } from "@calcom/app-store/utils";
import getBulkEventTypes from "@calcom/lib/event-types/getBulkEventTypes";
import prisma from "@calcom/prisma";
import { userMetadata } from "@calcom/prisma/zod-utils";
const setDefaultConferencingApp = async (userId: number, appSlug: string) => {
const eventTypes = await getBulkEventTypes(userId);
const eventTypeIds = eventTypes.eventTypes.map((item) => item.id);
const foundApp = getAppFromSlug(appSlug);
const appType = foundApp?.appData?.location?.type;
if (!appType) {
return;
}
const user = await prisma.user.findUnique({
where: {
id: userId,
},
select: {
metadata: true,
credentials: true,
},
});
const currentMetadata = userMetadata.parse(user?.metadata);
const credentialId = user?.credentials.find((item) => item.appId == appSlug)?.id;
//Update the default conferencing app for the user.
await prisma.user.update({
where: {
id: userId,
},
data: {
metadata: {
...currentMetadata,
defaultConferencingApp: {
appSlug,
},
},
},
});
await prisma.eventType.updateMany({
where: {
id: {
in: eventTypeIds,
},
userId,
},
data: {
locations: [{ type: appType, credentialId }] as LocationObject[],
},
});
};
export default setDefaultConferencingApp;
+11 -1
View File
@@ -44,13 +44,22 @@ function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMuta
const mutation = useMutation<
AddAppMutationData,
Error,
{ type?: App["type"]; variant?: string; slug?: string; isOmniInstall?: boolean; teamId?: number } | ""
| {
type?: App["type"];
variant?: string;
slug?: string;
isOmniInstall?: boolean;
teamId?: number;
defaultInstall?: boolean;
}
| ""
>({
...options,
mutationFn: async (variables) => {
let type: string | null | undefined;
let isOmniInstall;
const teamId = variables && variables.teamId ? variables.teamId : undefined;
const defaultInstall = variables && variables.defaultInstall ? variables.defaultInstall : undefined;
if (variables === "") {
type = _type;
} else {
@@ -76,6 +85,7 @@ function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMuta
fromApp: true,
...(type === "google_calendar" && { installGoogleVideo: options?.installGoogleVideo }),
...(teamId && { teamId }),
...(defaultInstall && { defaultInstall }),
};
const stateStr = encodeURIComponent(JSON.stringify(state));
+6
View File
@@ -5,8 +5,11 @@ import prisma from "@calcom/prisma";
import getAppKeysFromSlug from "../../_utils/getAppKeysFromSlug";
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
import createOAuthAppCredential from "../../_utils/oauth/createOAuthAppCredential";
import { decodeOAuthState } from "../../_utils/oauth/decodeOAuthState";
import setDefaultConferencingApp from "../../_utils/setDefaultConferencingApp";
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const state = decodeOAuthState(req);
const userId = req.session?.user.id;
if (!userId) {
return res.status(404).json({ message: "No user found" });
@@ -53,6 +56,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
{ access_token: responseBody.access_token },
req
);
if (state?.defaultInstall) {
setDefaultConferencingApp(userId, "jelly");
}
res.redirect(getInstalledAppPath({ variant: "conferencing", slug: "jelly" }));
}
+1
View File
@@ -12,6 +12,7 @@ export type IntegrationOAuthCallbackState = {
installGoogleVideo?: boolean;
appOnboardingRedirectUrl?: string;
teamId?: number;
defaultInstall?: boolean;
};
export type CredentialOwner = {
@@ -0,0 +1,38 @@
import { getAppFromLocationValue } from "@calcom/app-store/utils";
import { prisma } from "@calcom/prisma";
import { eventTypeLocations as eventTypeLocationsSchema } from "@calcom/prisma/zod-utils";
const getBulkEventTypes = async (userId: number) => {
const eventTypes = await prisma.eventType.findMany({
where: {
userId,
team: null,
},
select: {
id: true,
title: true,
locations: true,
},
});
const eventTypesWithLogo = eventTypes.map((eventType) => {
const locationParsed = eventTypeLocationsSchema.safeParse(eventType.locations);
// some events has null as location for legacy reasons, so this fallbacks to daily video
const app = getAppFromLocationValue(
locationParsed.success && locationParsed.data?.[0]?.type
? locationParsed.data[0].type
: "integrations:daily"
);
return {
...eventType,
logo: app?.logo,
};
});
return {
eventTypes: eventTypesWithLogo,
};
};
export default getBulkEventTypes;
@@ -1,6 +1,4 @@
import { getAppFromLocationValue } from "@calcom/app-store/utils";
import { prisma } from "@calcom/prisma";
import { eventTypeLocations as eventTypeLocationsSchema } from "@calcom/prisma/zod-utils";
import getBulkEventTypes from "@calcom/lib/event-types/getBulkEventTypes";
import type { TrpcSessionUser } from "../../../trpc";
@@ -11,34 +9,5 @@ type BulkEventFetchOptions = {
};
export const bulkEventFetchHandler = async ({ ctx }: BulkEventFetchOptions) => {
const eventTypes = await prisma.eventType.findMany({
where: {
userId: ctx.user.id,
team: null,
},
select: {
id: true,
title: true,
locations: true,
},
});
const eventTypesWithLogo = eventTypes.map((eventType) => {
const locationParsed = eventTypeLocationsSchema.safeParse(eventType.locations);
// some events has null as location for legacy reasons, so this fallbacks to daily video
const app = getAppFromLocationValue(
locationParsed.success && locationParsed.data?.[0]?.type
? locationParsed.data[0].type
: "integrations:daily"
);
return {
...eventType,
logo: app?.logo,
};
});
return {
eventTypes: eventTypesWithLogo,
};
return getBulkEventTypes(ctx.user.id);
};
@@ -38,6 +38,16 @@ export const bulkUpdateToDefaultLocationHandler = async ({
});
}
const credential = await prisma.credential.findFirst({
where: {
userId: ctx.user.id,
appId: foundApp.slug,
},
select: {
id: true,
},
});
return await prisma.eventType.updateMany({
where: {
id: {
@@ -46,7 +56,9 @@ export const bulkUpdateToDefaultLocationHandler = async ({
userId: ctx.user.id,
},
data: {
locations: [{ type: appType, link: defaultApp.appLink }] as LocationObject[],
locations: [
{ type: appType, link: defaultApp.appLink, credentialId: credential?.id },
] as LocationObject[],
},
});
};