diff --git a/apps/www/components/MaintenanceMode.tsx b/apps/www/components/MaintenanceMode.tsx new file mode 100644 index 000000000..d9655a880 --- /dev/null +++ b/apps/www/components/MaintenanceMode.tsx @@ -0,0 +1,108 @@ +import { ReactNode, useEffect, useRef, useState } from 'react' +import { light, styled } from '~styles' + +const CONTROL_SERVER = process.env.NEXT_PUBLIC_CONTROL_SERVER + ? process.env.NEXT_PUBLIC_CONTROL_SERVER + : process.env.NEXT_PUBLIC_ENABLE_DEV_CONTROL_SERVER + ? 'http://localhost:3001' + : null + +type MaintenanceModeConfig = + | { + enabled: false + ttlSeconds: number + } + | { + enabled: true + ttlSeconds: number + titleMessage: string + bodyMessageHtml: string + } + +type ControlResponse = { + maintenance: MaintenanceModeConfig +} + +export function MaintenanceMode({ children }: { children: ReactNode }) { + const [maintenanceMode, setMaintenanceMode] = useState(null) + + useEffect(() => { + if (!CONTROL_SERVER) return + let isCancelled = false + const updateMaintenanceMode = async () => { + try { + const response = await fetch(`${CONTROL_SERVER}/control.json`) + const data: ControlResponse = await response.json() + if (isCancelled) return + setMaintenanceMode(data.maintenance) + } catch { + setMaintenanceMode({ + enabled: false, + ttlSeconds: 60, + }) + } + } + + if (maintenanceMode) { + const refreshTimeout = setTimeout(updateMaintenanceMode, maintenanceMode.ttlSeconds * 1000) + return () => { + isCancelled = true + clearTimeout(refreshTimeout) + } + } else { + updateMaintenanceMode() + return () => { + isCancelled = true + } + } + }, [maintenanceMode]) + + const isMaintenanceModeEnabled = maintenanceMode?.enabled ?? false + const wasInMaintenanceModeRef = useRef(isMaintenanceModeEnabled) + useEffect(() => { + // If we were in maintenance mode and now we're not, reload the page + if (wasInMaintenanceModeRef.current && !isMaintenanceModeEnabled) { + window.location.reload() + } + wasInMaintenanceModeRef.current = isMaintenanceModeEnabled + }, [isMaintenanceModeEnabled]) + + if (maintenanceMode?.enabled) { + return ( +
+ + + {maintenanceMode.titleMessage} +
+ + +
+ ) + } + + return <>{children} +} + +const Container = styled('div', { + backgroundColor: '$canvas', + position: 'absolute', + inset: 0, + padding: '1rem', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}) + +const Modal = styled('div', { + backgroundColor: '$panel', + padding: '1rem 1.5rem', + borderRadius: '$2', + boxShadow: '$8', + maxWidth: '400px', + width: '100%', +}) + +const Heading = styled('h2', { + margin: 0, + marginTop: '1rem', +}) diff --git a/apps/www/pages/_app.tsx b/apps/www/pages/_app.tsx index 9afe0b9bc..4510a30e6 100644 --- a/apps/www/pages/_app.tsx +++ b/apps/www/pages/_app.tsx @@ -1,6 +1,7 @@ import '@fontsource/recursive' import Head from 'next/head' import type React from 'react' +import { MaintenanceMode } from '~components/MaintenanceMode' import '~styles/globals.css' import useGtag from '~utils/useGtag' @@ -50,7 +51,9 @@ function MyApp({ Component, pageProps }: any) { tldraw - + + + ) }