Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature]: Display a "Not Optimized for Mobile" overlay to users trying to access Agenta app from browsers with narrow width #2339

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {type PropsWithChildren, useState, useCallback} from "react"
import {Typography, Button, theme} from "antd"
import clsx from "clsx"
import useResizeObserver from "@/hooks/useResizeObserver"
import {createUseStyles} from "react-jss"
import {JSSTheme} from "@/lib/Types"
import {Transition} from "@headlessui/react"
import {useRouter} from "next/router"
import {MOBILE_UNOPTIMIZED_APP_ROUTES} from "./assets/constants"

const useStyles = createUseStyles((theme: JSSTheme) => ({
overlay: {
background: `${theme.colorBgContainer}`,
},
}))

const {useToken} = theme

const NoMobilePageWrapper: React.FC<PropsWithChildren> = ({children}) => {
const [dismissed, setDismissed] = useState(false)
const [shouldDisplay, setShouldDisplay] = useState(false)
const {overlay} = useStyles()
const {pathname} = useRouter()
const {token} = useToken()

const observerCallback = useCallback(
(bounds: DOMRectReadOnly) => {
setShouldDisplay(() => {
if (dismissed) return false // keep hidden if already dismissed by the user
if (!MOBILE_UNOPTIMIZED_APP_ROUTES.some((route) => pathname.startsWith(route)))
return false

return bounds.width < token.screenMD
})
},
[dismissed, pathname, token.screenMD],
)

useResizeObserver(observerCallback, typeof window !== "undefined" ? document.body : undefined)

const handleDismiss = () => {
setDismissed(true)
}

return (
<Transition
show={!dismissed && shouldDisplay}
enter="transition-opacity duration-75"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity duration-150"
leaveFrom="opacity-100"
leaveTo="opacity-0"
className={clsx([
"fixed top-0 left-0 right-0 bottom-0", // overlay the entire screen
"flex flex-col items-center justify-center gap-4", // flex config
"z-[9999]",
overlay, // TODO: better theme connected tailwind color classes
])}
unmount
>
<Typography.Text className="w-8/12 text-center leading-1 text-lg">
Agenta works better in larger laptop or desktop screens.
</Typography.Text>
<Button type="primary" size="large" onClick={handleDismiss}>
View anyway
</Button>
</Transition>
)
}

export default NoMobilePageWrapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// List of routes where the component should be displayed
export const MOBILE_UNOPTIMIZED_APP_ROUTES = [
"/apps",
"/observability",
"/settings",
"/testsets",
"/evaluations",
"/workspaces",
]
17 changes: 9 additions & 8 deletions agenta-web/src/hooks/useResizeObserver.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import {useLayoutEffect, useRef} from "react"

function useResizeObserver<T extends HTMLDivElement>(
callback: (entry: ResizeObserverEntry["contentRect"]) => void,
) {
const useResizeObserver = <T extends HTMLDivElement>(
callback?: (entry: ResizeObserverEntry["contentRect"]) => void,
element?: HTMLElement,
) => {
const ref = useRef<T>(null)

useLayoutEffect(() => {
const element = ref?.current
const _element = ref?.current || element

if (!element) {
if (!_element) {
return
}

const observer = new ResizeObserver((entries) => {
callback(entries[0].contentRect)
callback?.(entries[0].contentRect)
})

observer.observe(element)
observer.observe(_element)
return () => {
observer.disconnect()
}
}, [callback, ref])
}, [callback, element, ref])

return ref
}
Expand Down
9 changes: 9 additions & 0 deletions agenta-web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {useEffect} from "react"
import type {AppProps} from "next/app"
import {useRouter} from "next/router"
import Head from "next/head"
import dynamic from "next/dynamic"

import posthog from "posthog-js"
import {PostHogProvider} from "posthog-js/react"
Expand All @@ -16,6 +17,13 @@ import "ag-grid-community/styles/ag-grid.css"
import "ag-grid-community/styles/ag-theme-alpine.css"
import {Inter} from "next/font/google"

const NoMobilePageWrapper = dynamic(
bekossy marked this conversation as resolved.
Show resolved Hide resolved
() => import("@/components/NoMobilePageWrapper/NoMobilePageWrapper"),
{
ssr: false,
},
)

const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
Expand Down Expand Up @@ -60,6 +68,7 @@ export default function App({Component, pageProps}: AppProps) {
<AppContextProvider>
<Layout>
<Component {...pageProps} />
<NoMobilePageWrapper />
</Layout>
</AppContextProvider>
</ProjectContextProvider>
Expand Down
Loading