diff --git a/apps/builder/app/builder/features/topbar/entri.tsx b/apps/builder/app/builder/features/topbar/entri.tsx index cffbf3c01f5f..d56033b9d64a 100644 --- a/apps/builder/app/builder/features/topbar/entri.tsx +++ b/apps/builder/app/builder/features/topbar/entri.tsx @@ -1,20 +1,89 @@ -import { Button, Text } from "@webstudio-is/design-system"; -import { - useEntri, - entriGlobalStyles, - type DnsRecord, - type EntriCloseDetail, -} from "~/shared/entri/entri"; - -export const Entri = ({ - dnsRecords, - domain, - onClose, -}: { - dnsRecords: DnsRecord[]; +import * as entri from "entrijs"; +import { useEffect, useState } from "react"; +import { globalCss, Button, Text } from "@webstudio-is/design-system"; +import { trpcClient } from "~/shared/trpc/trpc-client"; + +// https://developers.entri.com/docs/install +type DnsRecord = { + type: "CNAME" | "TXT"; + host: string; + value: string; + ttl: number; +}; + +type EntriCloseEvent = CustomEvent; + +declare global { + // https://developers.entri.com/docs/integrate-with-dns-providers + interface WindowEventMap { + onEntriClose: EntriCloseEvent; + } +} + +/** + * Our FloatingPanelPopover adds pointerEvents: "none" to the body. + * We open the entry dialog from the popover, so we need to allow pointer events on the entri dialog. + */ +const entriGlobalStyles = globalCss({ + body: { + "&>#entriApp": { + pointerEvents: "all", + }, + }, +}); + +type EntriProps = { domain: string; - onClose: (detail: EntriCloseDetail) => void; -}) => { + dnsRecords: DnsRecord[]; + onClose: (detail: entri.EntriCloseEventDetail) => void; +}; + +const useEntri = ({ domain, dnsRecords, onClose }: EntriProps) => { + const [isOpen, setIsOpen] = useState(false); + + const { + load: entriTokenLoad, + data: entriTokenData, + error: entriTokenSystemError, + } = trpcClient.domain.getEntriToken.useQuery(); + + useEffect(() => { + const handleOnEntriClose = (event: EntriCloseEvent) => { + if (event.detail.domain === domain) { + onClose(event.detail); + setIsOpen(false); + } + }; + window.addEventListener("onEntriClose", handleOnEntriClose, false); + return () => { + window.removeEventListener("onEntriClose", handleOnEntriClose, false); + }; + }, [domain, onClose]); + + const showDialog = () => { + setIsOpen(true); + entriTokenLoad(undefined, async (data) => { + if (data.success) { + await entri.showEntri({ + applicationId: data.applicationId, + token: data.token, + dnsRecords, + prefilledDomain: domain, + }); + } + }); + }; + + return { + isOpen, + showDialog, + error: + entriTokenSystemError ?? + (entriTokenData?.success === false ? entriTokenData.error : undefined), + }; +}; + +export const Entri = ({ domain, dnsRecords, onClose }: EntriProps) => { entriGlobalStyles(); const { error, isOpen, showDialog } = useEntri({ onClose, @@ -31,9 +100,7 @@ export const Entri = ({ color="neutral" css={{ width: "100%", flexShrink: 0 }} type="button" - onClick={() => { - showDialog(); - }} + onClick={showDialog} > Configure automatically diff --git a/apps/builder/app/builder/features/topbar/publish.tsx b/apps/builder/app/builder/features/topbar/publish.tsx index 1b301e5795a3..63ec90737d6c 100644 --- a/apps/builder/app/builder/features/topbar/publish.tsx +++ b/apps/builder/app/builder/features/topbar/publish.tsx @@ -184,7 +184,7 @@ const ChangeProjectDomain = ({ - Proceed to ${pageUrl.toString()} + Proceed to {pageUrl.toString()} } variant="wrapped" diff --git a/apps/builder/app/shared/entri/entri.ts b/apps/builder/app/shared/entri/entri.ts deleted file mode 100644 index eac003e12fd0..000000000000 --- a/apps/builder/app/shared/entri/entri.ts +++ /dev/null @@ -1,158 +0,0 @@ -import useScript from "react-script-hook"; -import { globalCss } from "@webstudio-is/design-system"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { trpcClient } from "../trpc/trpc-client"; - -const scriptAttributes = { - async: true, - defer: true, - importance: "low", - src: "https://cdn.goentri.com/entri.js", -}; - -// https://developers.entri.com/docs/install -export type DnsRecord = { - type: "CNAME" | "TXT"; - host: string; - value: string; - ttl: number; -}; - -// https://developers.entri.com/docs/integrate-with-dns-providers -export type EntriCloseDetail = { - domain: string; - lastStatus: - | "FINISHED_SUCCESSFULLY" - | "IN_PROGRESS" - | "EXISTING_RECORDS" - | "LOGIN" - | "MANUAL_CONFIGURATION" - | "EXIT_WITH_ERROR" - | "DKIM_SETUP"; - - provider: string; - setupType: "automatic" | "manual" | "sharedLogin" | null; - success: boolean; -}; - -declare global { - interface Window { - // https://developers.entri.com/docs/api-reference - entri?: { - showEntri: (options: { - applicationId: string; - token: string; - dnsRecords: DnsRecord[]; - prefilledDomain: string; - }) => void; - }; - } - - // https://developers.entri.com/docs/integrate-with-dns-providers - interface WindowEventMap { - onEntriClose: CustomEvent; - } -} - -/** - * Our FloatingPanelPopover adds pointerEvents: "none" to the body. - * We open the entry dialog from the popover, so we need to allow pointer events on the entri dialog. - */ -export const entriGlobalStyles = globalCss({ - body: { - "&>#entriApp": { - pointerEvents: "all", - }, - }, -}); - -type UseEntriProps = { - domain: string; - onClose: (detail: EntriCloseDetail) => void; - dnsRecords: DnsRecord[]; -}; - -export const useEntri = ({ domain, dnsRecords, onClose }: UseEntriProps) => { - const [isScriptLoading, scriptLoadingError] = useScript(scriptAttributes); - const [error, setError] = useState(undefined); - const [isOpen, setIsOpen] = useState(false); - const showDialogInternalRef = useRef< - undefined | ((token: string, applicationId: string) => void) - >(undefined); - - const { - load: entriTokenLoad, - data: entriTokenData, - error: entriTokenSystemError, - } = trpcClient.domain.getEntriToken.useQuery(); - - useEffect(() => { - const handleOnEntriClose = (event: CustomEvent) => { - if (event.detail.domain !== domain) { - return; - } - - onClose(event.detail); - setIsOpen(false); - }; - - window.addEventListener("onEntriClose", handleOnEntriClose, false); - - return () => { - window.removeEventListener("onEntriClose", handleOnEntriClose); - }; - }, [domain, onClose]); - - const showDialogInternal = useCallback( - (token: string, applicationId: string) => { - const entri = window.entri; - - if (entri === undefined) { - if (isScriptLoading) { - setError("Entri is not loaded, try again later"); - return; - } - - setError("Entri is not loaded"); - return; - } - - entri.showEntri({ - applicationId, - token, - dnsRecords, - prefilledDomain: domain, - }); - }, - [dnsRecords, domain, isScriptLoading] - ); - - showDialogInternalRef.current = showDialogInternal; - - const showDialog = useCallback(() => { - setIsOpen(true); - entriTokenLoad(undefined, (data) => { - if (data.success === false) { - return; - } - - if (showDialogInternalRef.current === undefined) { - return; - } - - const { token, applicationId } = data; - - showDialogInternalRef.current(token, applicationId); - }); - }, [entriTokenLoad]); - - return { - isOpen, - showDialog, - error: - error ?? - entriTokenSystemError ?? - scriptLoadingError?.message ?? - (entriTokenData?.success === false ? entriTokenData.error : undefined), - }; -}; diff --git a/apps/builder/package.json b/apps/builder/package.json index 6dc3d55362a9..471bf9273a83 100644 --- a/apps/builder/package.json +++ b/apps/builder/package.json @@ -91,6 +91,7 @@ "css-tree": "^2.3.1", "debug": "^4.3.7", "downshift": "^6.1.7", + "entrijs": "^0.0.11", "fast-deep-equal": "^3.1.3", "immer": "^10.1.1", "immerhin": "^0.10.0", @@ -109,7 +110,6 @@ "react-colorful": "^5.6.1", "react-dom": "18.3.0-canary-14898b6a9-20240318", "react-error-boundary": "^5.0.0", - "react-script-hook": "^1.7.2", "remix-auth": "^3.7.0", "remix-auth-form": "^1.5.0", "remix-auth-github": "^1.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8bb0be8fc4b3..45ade6760091 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -334,6 +334,9 @@ importers: downshift: specifier: ^6.1.7 version: 6.1.7(react@18.3.0-canary-14898b6a9-20240318) + entrijs: + specifier: ^0.0.11 + version: 0.0.11 fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 @@ -388,9 +391,6 @@ importers: react-error-boundary: specifier: ^5.0.0 version: 5.0.0(react@18.3.0-canary-14898b6a9-20240318) - react-script-hook: - specifier: ^1.7.2 - version: 1.7.2(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318) remix-auth: specifier: ^3.7.0 version: 3.7.0(@remix-run/react@2.16.5(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318)(typescript@5.8.2))(@remix-run/server-runtime@2.16.5(typescript@5.8.2)) @@ -6483,6 +6483,9 @@ packages: resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} engines: {node: '>=0.12'} + entrijs@0.0.11: + resolution: {integrity: sha512-ufH4h0JdumikGIHuVQjP6uKJMG00Yv7Av0ZsBvM7PicejO8e9eRAxXKUdIHqoo1ZqogFrquDmmP63x3T0djHlQ==} + env-paths@3.0.0: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -8445,12 +8448,6 @@ packages: react-dom: optional: true - react-script-hook@1.7.2: - resolution: {integrity: sha512-fhyCEfXb94fag34UPRF0zry1XGwmVY+79iibWwTqAoOiCzYJQOYTiWJ7CnqglA9tMSV8g45cQpHCMcBwr7dwhA==} - peerDependencies: - react: 18.3.0-canary-14898b6a9-20240318 - react-dom: 18.3.0-canary-14898b6a9-20240318 - react-shallow-renderer@16.15.0: resolution: {integrity: sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==} peerDependencies: @@ -14107,6 +14104,10 @@ snapshots: entities@6.0.0: {} + entrijs@0.0.11: + dependencies: + react: 18.3.0-canary-14898b6a9-20240318 + env-paths@3.0.0: {} err-code@2.0.3: {} @@ -16605,11 +16606,6 @@ snapshots: optionalDependencies: react-dom: 18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318) - react-script-hook@1.7.2(react-dom@18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318))(react@18.3.0-canary-14898b6a9-20240318): - dependencies: - react: 18.3.0-canary-14898b6a9-20240318 - react-dom: 18.3.0-canary-14898b6a9-20240318(react@18.3.0-canary-14898b6a9-20240318) - react-shallow-renderer@16.15.0(react@18.3.0-canary-14898b6a9-20240318): dependencies: object-assign: 4.1.1