From a27e1498b8d009ff650357c3550a5b4204b64ee2 Mon Sep 17 00:00:00 2001 From: Nizzy <140507264+nizzyabi@users.noreply.github.com> Date: Fri, 27 Dec 2024 13:42:36 -0500 Subject: [PATCH] fix: removed plain chat from app.cal.com/video (#18385) * fix: removed plain chat from app.cal.com/video * take user session and open plain out of use effect * move undefined check and screen size check above useeffect * turned restrcited paths into env var * undefeined window below hooks * chore: add window check * fix: infinite re rendering * remove console.log --------- Co-authored-by: Udit Takkar --- .env.example | 1 + apps/web/lib/plain/plainChat.tsx | 308 ++++++++++++++++--------------- turbo.json | 3 +- 3 files changed, 167 insertions(+), 145 deletions(-) diff --git a/.env.example b/.env.example index 8d501dd126..4397eb9d29 100644 --- a/.env.example +++ b/.env.example @@ -114,6 +114,7 @@ PLAIN_API_URL=https://api.plain.com/v1 PLAIN_HMAC_SECRET_KEY= PLAIN_CHAT_ID= PLAIN_CHAT_HMAC_SECRET_KEY= +NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS= # Zendesk Config NEXT_PUBLIC_ZENDESK_KEY= diff --git a/apps/web/lib/plain/plainChat.tsx b/apps/web/lib/plain/plainChat.tsx index f63b93f0d8..d80699c363 100644 --- a/apps/web/lib/plain/plainChat.tsx +++ b/apps/web/lib/plain/plainChat.tsx @@ -3,7 +3,7 @@ import { useSession } from "next-auth/react"; import { usePathname, useSearchParams } from "next/navigation"; import Script from "next/script"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useCallback, useMemo } from "react"; declare global { interface Window { @@ -75,157 +75,177 @@ const PlainChat = () => { const pathname = usePathname(); const searchParams = useSearchParams(); - const isAppDomain = typeof window !== "undefined" && window.location.hostname === "app.cal.com"; + const shouldOpenPlain = pathname === "/event-types" && searchParams?.has("openPlain"); + const userEmail = session?.user?.email; + + const isAppDomain = useMemo(() => { + const restrictedPaths = process.env.NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS?.split(",") || []; + return ( + typeof window !== "undefined" && + window.location.origin === process.env.NEXT_PUBLIC_WEBAPP_URL && + !restrictedPaths.some((path) => pathname?.startsWith(path.trim())) + ); + }, [pathname]); + + const checkScreenSize = useCallback(() => { + if (typeof window === "undefined") return; + + const isSmall = window.innerWidth < 768; + setIsSmallScreen(isSmall); + + if (isSmall && window.Plain) { + const plainElement = document.querySelector("#plain-container"); + plainElement?.remove(); + window.Plain = undefined; + } else if (!isSmall && window.Plain === undefined) { + window.plainScriptLoaded?.(); + } + }, []); + + const initConfig = useCallback(async () => { + if (!userEmail) return; + + try { + const response = await fetch("/api/plain-hash", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Failed to generate hash: ${errorText}`); + } + + const data = await response.json(); + + if (!data.hash || !data.email || !data.appId) { + throw new Error("Missing required fields in API response"); + } + + const plainChatConfig: PlainChatConfig = { + appId: data.appId, + customerDetails: { + email: data.email, + shortName: data.shortName, + fullName: data.fullName, + emailHash: data.hash, + chatAvatarUrl: data.chatAvatarUrl, + }, + links: [ + { + icon: "book", + text: "Documentation", + url: "https://cal.com/docs", + }, + { + icon: "chat", + text: "Ask the community", + url: "https://github.com/calcom/cal.com/discussions", + }, + ], + chatButtons: [ + { + icon: "chat", + text: "Ask a question", + type: "primary", + }, + { + icon: "bulb", + text: "Send feedback", + type: "default", + }, + { + icon: "error", + text: "Report an issue", + type: "default", + form: { + fields: [ + { + type: "dropdown", + placeholder: "Select severity...", + options: [ + { + icon: "support", + text: "I'm unable to use the app", + threadDetails: { + severity: "critical", + issueType: "critical", + labelTypeIds: ["lt_01JFJWNWAC464N8DZ6YE71YJRF"], + priority: "u", + }, + }, + { + icon: "error", + text: "Major functionality degraded", + threadDetails: { + severity: "major", + issueType: "major", + labelTypeIds: ["lt_01JFJWP3KECF1YQES6XF212RFW"], + priority: "h", + }, + }, + { + icon: "bug", + text: "Minor annoyance", + threadDetails: { + severity: "minor", + issueType: "minor", + labelTypeIds: ["lt_01JFJWPC8ADW0PK28JHMJR6NSS"], + priority: "l", + }, + }, + ], + }, + ], + }, + }, + ], + entryPoint: { + type: "chat", + }, + hideBranding: true, + theme: "auto", + style: { + brandColor: "#FFFFFF", + launcherBackgroundColor: "#262626", + launcherIconColor: "#FFFFFF", + }, + position: { + bottom: "20px", + right: "20px", + }, + }; + + if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") { + window.__PLAIN_CONFIG__ = plainChatConfig; + } + + setConfig(plainChatConfig); + + if (shouldOpenPlain) { + const timer = setTimeout(() => { + if (window.Plain) { + window.Plain.open(); + } + }, 100); + return () => clearTimeout(timer); + } + } catch (error) { + console.error("Failed to initialize Plain Chat:", error); + } + }, [userEmail, shouldOpenPlain]); useEffect(() => { if (!isAppDomain) return; - const checkScreenSize = () => { - setIsSmallScreen(window.innerWidth < 768); - }; - checkScreenSize(); window.addEventListener("resize", checkScreenSize); - - const initConfig = async () => { - if (!session?.user?.email) return; - - try { - const response = await fetch("/api/plain-hash", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`Failed to generate hash: ${errorText}`); - } - - const data = await response.json(); - - if (!data.hash || !data.email || !data.appId) { - throw new Error("Missing required fields in API response"); - } - - const plainChatConfig: PlainChatConfig = { - appId: data.appId, - customerDetails: { - email: data.email, - shortName: data.shortName, - fullName: data.fullName, - emailHash: data.hash, - chatAvatarUrl: data.chatAvatarUrl, - }, - links: [ - { - icon: "book", - text: "Documentation", - url: "https://cal.com/docs", - }, - { - icon: "chat", - text: "Ask the community", - url: "https://github.com/calcom/cal.com/discussions", - }, - ], - chatButtons: [ - { - icon: "chat", - text: "Ask a question", - type: "primary", - }, - { - icon: "bulb", - text: "Send feedback", - type: "default", - }, - { - icon: "error", - text: "Report an issue", - type: "default", - form: { - fields: [ - { - type: "dropdown", - placeholder: "Select severity...", - options: [ - { - icon: "support", - text: "I'm unable to use the app", - threadDetails: { - severity: "critical", - issueType: "critical", - labelTypeIds: ["lt_01JFJWNWAC464N8DZ6YE71YJRF"], - priority: "u", - }, - }, - { - icon: "error", - text: "Major functionality degraded", - threadDetails: { - severity: "major", - issueType: "major", - labelTypeIds: ["lt_01JFJWP3KECF1YQES6XF212RFW"], - priority: "h", - }, - }, - { - icon: "bug", - text: "Minor annoyance", - threadDetails: { - severity: "minor", - issueType: "minor", - labelTypeIds: ["lt_01JFJWPC8ADW0PK28JHMJR6NSS"], - priority: "l", - }, - }, - ], - }, - ], - }, - }, - ], - entryPoint: { - type: "chat", - }, - hideBranding: true, - theme: "auto", - style: { - brandColor: "#FFFFFF", - launcherBackgroundColor: "#262626", - launcherIconColor: "#FFFFFF", - }, - position: { - bottom: "20px", - right: "20px", - }, - }; - - if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") { - window.__PLAIN_CONFIG__ = plainChatConfig; - } - - setConfig(plainChatConfig); - - if (pathname === "/event-types" && searchParams?.has("openPlain")) { - const timer = setTimeout(() => { - if (window.Plain) { - window.Plain.open(); - } - }, 100); - return () => clearTimeout(timer); - } - } catch (error) { - console.error("Failed to initialize Plain Chat:", error); - } - }; - initConfig(); return () => window.removeEventListener("resize", checkScreenSize); - }, [session, pathname, searchParams, isAppDomain]); + }, [isAppDomain, checkScreenSize, initConfig, userEmail]); const plainChatScript = ` window.plainScriptLoaded = function() { @@ -239,7 +259,7 @@ const PlainChat = () => { } `; - if (!isAppDomain || isSmallScreen || !config) return null; + if (!isAppDomain || isSmallScreen || !config || typeof window === "undefined") return null; return ( <> diff --git a/turbo.json b/turbo.json index 0cc8e703e4..9bc3910d87 100644 --- a/turbo.json +++ b/turbo.json @@ -119,7 +119,8 @@ "PLAIN_API_KEY", "PLAIN_API_URL", "PLAIN_CHAT_ID", - "PLAIN_CHAT_HMAC_SECRET_KEY" + "PLAIN_CHAT_HMAC_SECRET_KEY", + "NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS" ] }, "build": {