diff --git a/.env.example b/.env.example index dae85a0ed9..cebebfa0d4 100644 --- a/.env.example +++ b/.env.example @@ -25,6 +25,7 @@ NEXT_PUBLIC_LICENSE_CONSENT='' NEXT_PUBLIC_WEBAPP_URL='http://localhost:3000' # Change to 'http://localhost:3001' if running the website simultaneously NEXT_PUBLIC_WEBSITE_URL='http://localhost:3000' +NEXT_PUBLIC_EMBED_LIB_URL='http://localhost:3000/embed/embed.js' # To enable SAML login, set both these variables # @see https://github.com/calcom/cal.com/tree/main/packages/ee#setting-up-saml-login diff --git a/apps/web/.gitignore b/apps/web/.gitignore index abc84320a8..46f3800f7b 100644 --- a/apps/web/.gitignore +++ b/apps/web/.gitignore @@ -61,3 +61,6 @@ yarn-error.log* # Typescript tsconfig.tsbuildinfo + +# Autogenerated embed content +public/embed diff --git a/apps/web/components/Embed.tsx b/apps/web/components/Embed.tsx index 31ab4a3991..1885747c18 100644 --- a/apps/web/components/Embed.tsx +++ b/apps/web/components/Embed.tsx @@ -12,6 +12,7 @@ import { Button, Switch } from "@calcom/ui"; import { Dialog, DialogContent, DialogClose } from "@calcom/ui/Dialog"; import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields"; +import { WEBAPP_URL, EMBED_LIB_URL } from "@lib/config/constants"; import { trpc } from "@lib/trpc"; import NavTabs from "@components/NavTabs"; @@ -216,16 +217,10 @@ const embeds: { ]; function getEmbedSnippetString() { - let embedJsUrl = "https://cal.com/embed.js"; - let isLocal = false; - if (location.hostname === "localhost") { - embedJsUrl = "http://localhost:3100/dist/embed.umd.js"; - isLocal = true; - } // TODO: Import this string from @calcom/embed-snippet return ` -(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); }; })(window, "${embedJsUrl}", "init"); -Cal("init"${isLocal ? ', {origin:"http://localhost:3000/"}' : ""}); +(function (C, A, L) { let p = function (a, ar) { a.q.push(ar); }; let d = C.document; C.Cal = C.Cal || function () { let cal = C.Cal; let ar = arguments; if (!cal.loaded) { cal.ns = {}; cal.q = cal.q || []; d.head.appendChild(d.createElement("script")).src = A; cal.loaded = true; } if (ar[0] === L) { const api = function () { p(api, arguments); }; const namespace = ar[1]; api.q = api.q || []; typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); return; } p(cal, ar); }; })(window, "${EMBED_LIB_URL}", "init"); +Cal("init", {origin:"${WEBAPP_URL}"}); `; } @@ -815,7 +810,7 @@ ${getEmbedTypeSpecificString().trim()} className="border-1 h-[75vh] border" width="100%" height="100%" - src={`http://localhost:3100/preview.html?embedType=${embedType}&calLink=${calLink}`} + src={`${WEBAPP_URL}/embed/preview.html?embedType=${embedType}&calLink=${calLink}`} /> diff --git a/packages/embeds/embed-core/index.html b/packages/embeds/embed-core/index.html index 016f88a672..cf631379f0 100644 --- a/packages/embeds/embed-core/index.html +++ b/packages/embeds/embed-core/index.html @@ -44,7 +44,7 @@ } p(cal, ar); }; - })(window, "//localhost:3100/dist/embed.umd.js", "init"); + })(window, "//localhost:3000/embed/embed.js", "init"); - - -
- diff --git a/packages/embeds/embed-core/src/embed-iframe.ts b/packages/embeds/embed-core/src/embed-iframe.ts index b09b58b92a..04b328fdbc 100644 --- a/packages/embeds/embed-core/src/embed-iframe.ts +++ b/packages/embeds/embed-core/src/embed-iframe.ts @@ -7,6 +7,16 @@ export interface UiConfig { theme?: "dark" | "light" | "auto"; styles?: EmbedStyles; } +declare global { + interface Window { + CalEmbed: { + __logQueue?: any[]; + embedStore: any; + }; + CalComPageStatus: string; + CalComPlan: string; + } +} const embedStore = { // Store all embed styles here so that as and when new elements are mounted, styles can be applied to it. @@ -37,6 +47,9 @@ if (isBrowser) { if (isSafariBrowser) { log("Safari Detected: Using setTimeout instead of rAF"); } + window.CalEmbed = window.CalEmbed || {}; + //TODO: Send postMessage to parent to get all log messages in the same queue. + window.CalEmbed.embedStore = embedStore; } function runAsap(fn: (...arg: any) => void) { @@ -47,23 +60,11 @@ function runAsap(fn: (...arg: any) => void) { return requestAnimationFrame(fn); } -declare global { - interface Window { - CalEmbed: { - __logQueue?: any[]; - }; - CalComPageStatus: string; - CalComPlan: string; - } -} - function log(...args: any[]) { if (isBrowser) { const namespace = getNamespace(); const searchParams = new URL(document.URL).searchParams; - //TODO: Send postMessage to parent to get all log messages in the same queue. - window.CalEmbed = window.CalEmbed || {}; const logQueue = (window.CalEmbed.__logQueue = window.CalEmbed.__logQueue || []); args.push({ ns: namespace, diff --git a/packages/embeds/embed-core/src/embed.ts b/packages/embeds/embed-core/src/embed.ts index af1564fe2b..0b945cea51 100644 --- a/packages/embeds/embed-core/src/embed.ts +++ b/packages/embeds/embed-core/src/embed.ts @@ -8,6 +8,12 @@ import css from "./embed.css"; import { SdkActionManager } from "./sdk-action-manager"; import allCss from "./tailwind.generated.css"; +// HACK: Redefine and don't import WEBAPP_URL as it causes import statement to be present in built file. +// This is happening because we are not able to generate an App and a lib using single Vite Config. +const WEBAPP_URL = + (import.meta.env.NEXT_PUBLIC_WEBAPP_URL_TYPO as string) || + `https://${import.meta.env.NEXT_PUBLIC_VERCEL_URL}`; + customElements.define("cal-modal-box", ModalBox); customElements.define("cal-floating-button", FloatingButton); customElements.define("cal-inline", Inline); @@ -414,8 +420,8 @@ export class Cal { constructor(namespace: string, q: InstructionQueue) { this.__config = { - // Keep cal.com hardcoded till the time embed.js deployment to cal.com/embed.js is automated. This is to prevent accidentally pushing of localhost domain to production - origin: /*import.meta.env.NEXT_PUBLIC_WEBSITE_URL || */ "https://app.cal.com", + // Use WEBAPP_URL till full page reload problem with website URL is solved + origin: WEBAPP_URL, }; this.namespace = namespace; this.actionManager = new SdkActionManager(namespace); @@ -507,7 +513,11 @@ window.addEventListener("message", (e) => { if (!parsedAction) { return; } + const actionManager = Cal.actionsManagers[parsedAction.ns]; + globalCal.__logQueue = globalCal.__logQueue || []; + globalCal.__logQueue.push({ ...parsedAction, data: detail.data }); + if (!actionManager) { throw new Error("Unhandled Action" + parsedAction); } diff --git a/packages/embeds/embed-core/src/preview.ts b/packages/embeds/embed-core/src/preview.ts index fcbdbeee8f..9d671ca2ce 100644 --- a/packages/embeds/embed-core/src/preview.ts +++ b/packages/embeds/embed-core/src/preview.ts @@ -1,6 +1,76 @@ import { CalWindow } from "@calcom/embed-snippet"; -window.addEventListener("message", (e) => { +const WEBAPP_URL = + import.meta.env.NEXT_PUBLIC_WEBAPP_URL || `https://${import.meta.env.NEXT_PUBLIC_VERCEL_URL}`; +const EMBED_LIB_URL = import.meta.env.NEXT_PUBLIC_EMBED_LIB_URL || `${WEBAPP_URL}/embed/embed.js`; + +(window as any).fingerprint = import.meta.env.NEXT_PUBLIC_EMBED_FINGER_PRINT as string; + +// Install Cal Embed Code Snippet +(function (C, A, L) { + // @ts-ignore + let p = function (a, ar) { + a.q.push(ar); + }; + let d = C.document; + // @ts-ignore + C.Cal = + // @ts-ignore + C.Cal || + function () { + // @ts-ignore + let cal = C.Cal; + let ar = arguments; + if (!cal.loaded) { + cal.ns = {}; + cal.q = cal.q || []; + // @ts-ignore + d.head.appendChild(d.createElement("script")).src = A; + cal.loaded = true; + } + if (ar[0] === L) { + const api = function () { + p(api, arguments); + }; + const namespace = ar[1]; + // @ts-ignore + api.q = api.q || []; + // @ts-ignore + typeof namespace === "string" ? (cal.ns[namespace] = api) && p(api, ar) : p(cal, ar); + return; + } + p(cal, ar); + }; +})(window, EMBED_LIB_URL, "init"); + +const previewWindow: CalWindow = window; + +previewWindow.Cal!("init", { + origin: WEBAPP_URL, +}); +const searchParams = new URL(document.URL).searchParams; +const embedType = searchParams.get("embedType"); +const calLink = searchParams.get("calLink"); +if (embedType! === "inline") { + previewWindow.Cal!("inline", { + elementOrSelector: "#my-embed", + calLink: calLink, + }); +} else if (embedType === "floating-popup") { + previewWindow.Cal!("floatingButton", { + calLink: calLink, + attributes: { + id: "my-floating-button", + }, + }); +} else if (embedType === "element-click") { + const button = document.createElement("button"); + button.setAttribute("data-cal-link", calLink!); + button.innerHTML = "I am a button that exists on your website"; + document.body.appendChild(button); +} + +previewWindow.addEventListener("message", (e) => { const data = e.data; if (data.mode !== "cal:preview") { return; diff --git a/packages/embeds/embed-core/vite.config.js b/packages/embeds/embed-core/vite.config.js index b6a5563ff5..cfe4c9cd1e 100644 --- a/packages/embeds/embed-core/vite.config.js +++ b/packages/embeds/embed-core/vite.config.js @@ -1,23 +1,39 @@ require("dotenv").config({ path: "../../../.env" }); +process.env.NEXT_PUBLIC_VERCEL_URL = process.env.VERCEL_URL; + const path = require("path"); const { defineConfig } = require("vite"); -module.exports = defineConfig({ - envPrefix: "NEXT_PUBLIC_", - build: { - minify: "terser", - watch: { - include: ["src/**"], - }, - terserOptions: { - format: { - comments: false, +module.exports = defineConfig((configEnv) => { + const config = { + envPrefix: "NEXT_PUBLIC_", + base: "/embed/", + build: { + minify: "terser", + terserOptions: { + format: { + comments: false, + }, + }, + rollupOptions: { + input: { + preview: path.resolve(__dirname, "preview.html"), + embed: path.resolve(__dirname, "src/embed.ts"), + }, + output: { + entryFileNames: "[name].js", + //FIXME: Can't specify UMD as import because preview is an app which doesn't support `format` and this setting apply to both input + //format: "umd", + dir: "../../../apps/web/public/embed", + }, }, }, - lib: { - entry: path.resolve(__dirname, "src/embed.ts"), - name: "embed", - fileName: (format) => `embed.${format}.js`, - }, - }, + }; + + if (configEnv.mode === "development") { + config.build.watch = { + include: ["src/**"], + }; + } + return config; }); diff --git a/packages/embeds/embed-react/playwright/tests/basic.test.ts-snapshots/react-component-inline-chromium-darwin.png b/packages/embeds/embed-react/playwright/tests/basic.test.ts-snapshots/react-component-inline-chromium-darwin.png index 249377ff61..d17880cf33 100644 Binary files a/packages/embeds/embed-react/playwright/tests/basic.test.ts-snapshots/react-component-inline-chromium-darwin.png and b/packages/embeds/embed-react/playwright/tests/basic.test.ts-snapshots/react-component-inline-chromium-darwin.png differ diff --git a/packages/embeds/embed-react/test-cal.tsx b/packages/embeds/embed-react/test-cal.tsx index c5d8ae29f4..b1483d5b5d 100644 --- a/packages/embeds/embed-react/test-cal.tsx +++ b/packages/embeds/embed-react/test-cal.tsx @@ -17,7 +17,7 @@ function App() {