Skip to content

Commit

Permalink
Merge pull request #2339 from Agenta-AI/AGE-1407/feat-add-not-optimiz…
Browse files Browse the repository at this point in the history
…ed-warning-for-small-screens

[Feature]: Display a "Not Optimized for Mobile" overlay to users trying to access Agenta app from browsers with narrow width
  • Loading branch information
mmabrouk authored Dec 6, 2024
2 parents 2b518d2 + 76e931e commit 9140212
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 8 deletions.
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
5 changes: 5 additions & 0 deletions agenta-web/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ 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"

import "@/styles/globals.css"
import Layout from "@/components/Layout/Layout"
import {dynamicComponent} from "@/lib/helpers/dynamic"
import ThemeContextProvider from "@/components/Layout/ThemeContextProvider"
import AppContextProvider from "@/contexts/app.context"
import ProfileContextProvider from "@/contexts/profile.context"
Expand All @@ -16,6 +18,8 @@ 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 = dynamicComponent("NoMobilePageWrapper/NoMobilePageWrapper")

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

0 comments on commit 9140212

Please sign in to comment.