diff --git a/package.json b/package.json index 9293f3f7..9dbbcd4d 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,11 @@ "import": "./app-bridge/index.mjs", "require": "./app-bridge/index.js" }, + "./util": { + "types": "./util/index.d.ts", + "import": "./util/index.mjs", + "require": "./util/index.js" + }, "./handlers/next": { "types": "./handlers/next/index.d.ts", "import": "./handlers/next/index.mjs", diff --git a/src/app-bridge/with-authorization.tsx b/src/app-bridge/with-authorization.tsx new file mode 100644 index 00000000..8eaae69e --- /dev/null +++ b/src/app-bridge/with-authorization.tsx @@ -0,0 +1,57 @@ +import { NextPage } from "next"; +import * as React from "react"; +import { PropsWithChildren, ReactNode } from "react"; + +import { isInIframe, useIsMounted } from "../util"; +import { useDashboardToken } from "./use-dashboard-token"; + +function SimpleError({ children }: PropsWithChildren<{}>) { + return ( +
+

{children}

+
+ ); +} + +type Props = { + unmounted?: ReactNode; + notIframe?: ReactNode; + noDashboardToken?: ReactNode; + dashboardTokenInvalid?: ReactNode; +}; + +export const withAuthorization = + ({ + dashboardTokenInvalid = Dashboard token is invalid, + noDashboardToken = Dashboard token doesn"t exist, + notIframe = The view can only be displayed inside iframe., + unmounted =

Loading

, + }: Props) => + >( + BaseComponent: React.FunctionComponent + ) => { + function AuthorizedPage(props: BaseProps) { + const mounted = useIsMounted(); + const { isTokenValid, hasAppToken } = useDashboardToken(); + + if (!mounted) { + return unmounted; + } + + if (!isInIframe()) { + return notIframe; + } + + if (!hasAppToken) { + return noDashboardToken; + } + + if (!isTokenValid) { + return dashboardTokenInvalid; + } + + return ; + } + + return AuthorizedPage; + }; diff --git a/src/util/index.ts b/src/util/index.ts new file mode 100644 index 00000000..deacb276 --- /dev/null +++ b/src/util/index.ts @@ -0,0 +1,2 @@ +export * from "./is-in-iframe"; +export * from "./use-is-mounted"; diff --git a/src/util/is-in-iframe.ts b/src/util/is-in-iframe.ts new file mode 100644 index 00000000..6e900d17 --- /dev/null +++ b/src/util/is-in-iframe.ts @@ -0,0 +1,11 @@ +export const isInIframe = () => { + if (!document || !window) { + throw new Error("isInIframe should be called only in browser"); + } + + try { + return document.location !== window.parent.location; + } catch (e) { + return false; + } +}; diff --git a/src/util/use-is-mounted.ts b/src/util/use-is-mounted.ts new file mode 100644 index 00000000..dfb473b7 --- /dev/null +++ b/src/util/use-is-mounted.ts @@ -0,0 +1,8 @@ +import { useEffect, useState } from "react"; + +export const useIsMounted = (): boolean => { + const [mounted, setMounted] = useState(false); + useEffect(() => setMounted(true), []); + + return mounted; +};