diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json
index c93507b7..fea0d97f 100644
--- a/src/frontend/package-lock.json
+++ b/src/frontend/package-lock.json
@@ -23,6 +23,7 @@
"react-aria-components": "1.2.1",
"react-dom": "18.2.0",
"react-i18next": "14.1.3",
+ "valtio": "1.13.2",
"wouter": "3.3.0"
},
"devDependencies": {
@@ -3643,13 +3644,13 @@
"version": "15.7.12",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz",
"integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/react": {
"version": "18.3.3",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz",
"integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.0.2"
@@ -5134,7 +5135,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
- "dev": true
+ "devOptional": true
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
@@ -5282,6 +5283,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/derive-valtio": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/derive-valtio/-/derive-valtio-0.1.0.tgz",
+ "integrity": "sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==",
+ "peerDependencies": {
+ "valtio": "*"
+ }
+ },
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@@ -8937,6 +8946,11 @@
"node": "10.* || >= 12.*"
}
},
+ "node_modules/proxy-compare": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.6.0.tgz",
+ "integrity": "sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw=="
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -10101,6 +10115,39 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
+ "node_modules/valtio": {
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/valtio/-/valtio-1.13.2.tgz",
+ "integrity": "sha512-Qik0o+DSy741TmkqmRfjq+0xpZBXi/Y6+fXZLn0xNF1z/waFMbE3rkivv5Zcf9RrMUp6zswf2J7sbh2KBlba5A==",
+ "dependencies": {
+ "derive-valtio": "0.1.0",
+ "proxy-compare": "2.6.0",
+ "use-sync-external-store": "1.2.0"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/valtio/node_modules/use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/value-or-function": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz",
diff --git a/src/frontend/package.json b/src/frontend/package.json
index 7e068a7f..8495a491 100644
--- a/src/frontend/package.json
+++ b/src/frontend/package.json
@@ -26,6 +26,7 @@
"react-aria-components": "1.2.1",
"react-dom": "18.2.0",
"react-i18next": "14.1.3",
+ "valtio": "1.13.2",
"wouter": "3.3.0"
},
"devDependencies": {
diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx
index 9a055d34..4c61e898 100644
--- a/src/frontend/src/App.tsx
+++ b/src/frontend/src/App.tsx
@@ -6,8 +6,8 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { useLang } from 'hoofd'
import { Switch, Route } from 'wouter'
-import { NotFoundScreen } from './layout/NotFoundScreen'
-import { RenderIfUserFetched } from './features/auth'
+import { Layout } from './layout/Layout'
+import { NotFoundScreen } from './components/NotFoundScreen'
import { routes } from './routes'
import './i18n/init'
@@ -19,14 +19,14 @@ function App() {
return (
-
+
{Object.entries(routes).map(([, route], i) => (
))}
-
+
diff --git a/src/frontend/src/components/BackToHome.tsx b/src/frontend/src/components/BackToHome.tsx
new file mode 100644
index 00000000..4c6fc287
--- /dev/null
+++ b/src/frontend/src/components/BackToHome.tsx
@@ -0,0 +1,14 @@
+import { Link } from '@/primitives'
+import { AProps } from '@/primitives/A'
+import { useTranslation } from 'react-i18next'
+
+export const BackToHome = ({ size }: { size?: AProps['size'] }) => {
+ const { t } = useTranslation()
+ return (
+
+
+ {t('backToHome')}
+
+
+ )
+}
diff --git a/src/frontend/src/components/DelayedRender.ts b/src/frontend/src/components/DelayedRender.ts
new file mode 100644
index 00000000..e16f3bbe
--- /dev/null
+++ b/src/frontend/src/components/DelayedRender.ts
@@ -0,0 +1,24 @@
+import { useState, useEffect, type ReactNode } from 'react'
+
+export const DelayedRender = ({
+ children,
+ delay = 500,
+}: {
+ delay?: number
+ children: ReactNode
+}) => {
+ const [show, setShow] = useState(false)
+ useEffect(() => {
+ if (delay === 0) {
+ setShow(true)
+ return
+ }
+ const timeout = setTimeout(() => setShow(true), delay)
+ return () => clearTimeout(timeout)
+ }, [delay])
+ if (delay !== 0 && !show) {
+ return null
+ }
+
+ return children
+}
diff --git a/src/frontend/src/components/ErrorScreen.tsx b/src/frontend/src/components/ErrorScreen.tsx
new file mode 100644
index 00000000..e33d89f9
--- /dev/null
+++ b/src/frontend/src/components/ErrorScreen.tsx
@@ -0,0 +1,12 @@
+import { CenteredContent } from '@/layout/CenteredContent'
+import { Screen } from '@/layout/Screen'
+import { useTranslation } from 'react-i18next'
+
+export const ErrorScreen = () => {
+ const { t } = useTranslation()
+ return (
+
+
+
+ )
+}
diff --git a/src/frontend/src/components/LoadingScreen.tsx b/src/frontend/src/components/LoadingScreen.tsx
new file mode 100644
index 00000000..ded2c21b
--- /dev/null
+++ b/src/frontend/src/components/LoadingScreen.tsx
@@ -0,0 +1,27 @@
+import { Screen, type ScreenProps } from '@/layout/Screen'
+import { DelayedRender } from './DelayedRender'
+import { CenteredContent } from '@/layout/CenteredContent'
+import { useTranslation } from 'react-i18next'
+import { Center } from '@/styled-system/jsx'
+
+export const LoadingScreen = ({
+ delay = 500,
+ header = undefined,
+ layout = 'centered',
+}: {
+ delay?: number
+} & Omit) => {
+ const { t } = useTranslation()
+
+ return (
+
+
+
+
+ {t('loading')}
+
+
+
+
+ )
+}
diff --git a/src/frontend/src/components/NotFoundScreen.tsx b/src/frontend/src/components/NotFoundScreen.tsx
new file mode 100644
index 00000000..56caf247
--- /dev/null
+++ b/src/frontend/src/components/NotFoundScreen.tsx
@@ -0,0 +1,12 @@
+import { CenteredContent } from '@/layout/CenteredContent'
+import { Screen } from '@/layout/Screen'
+import { useTranslation } from 'react-i18next'
+
+export const NotFoundScreen = () => {
+ const { t } = useTranslation()
+ return (
+
+
+
+ )
+}
diff --git a/src/frontend/src/components/QueryAware.tsx b/src/frontend/src/components/QueryAware.tsx
new file mode 100644
index 00000000..54946b56
--- /dev/null
+++ b/src/frontend/src/components/QueryAware.tsx
@@ -0,0 +1,28 @@
+import { ErrorScreen } from '@/components/ErrorScreen'
+import { LoadingScreen } from '@/components/LoadingScreen'
+
+/**
+ * Render an error or loading Screen while a given `status` is not a success,
+ * otherwise directly render children.
+ *
+ * `status` matches react query statuses.
+ *
+ * Children usually contain a Screen at some point in the render tree.
+ */
+export const QueryAware = ({
+ status,
+ children,
+}: {
+ status: 'error' | 'pending' | 'success'
+ children: React.ReactNode
+}) => {
+ if (status === 'error') {
+ return
+ }
+
+ if (status === 'pending') {
+ return
+ }
+
+ return children
+}
diff --git a/src/frontend/src/features/auth/components/RenderIfUserFetched.tsx b/src/frontend/src/features/auth/components/RenderIfUserFetched.tsx
deleted file mode 100644
index 276a0133..00000000
--- a/src/frontend/src/features/auth/components/RenderIfUserFetched.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { type ReactNode } from 'react'
-import { useUser } from '@/features/auth'
-import { LoadingScreen } from '@/layout/LoadingScreen'
-
-/**
- * wrapper that renders children only when user info has been actually fetched
- *
- * this is helpful to prevent flash of logged-out content for a few milliseconds when user is actually logged in
- */
-export const RenderIfUserFetched = ({ children }: { children: ReactNode }) => {
- const { isLoggedIn } = useUser()
- return isLoggedIn !== undefined ? (
- children
- ) : (
-
- )
-}
diff --git a/src/frontend/src/features/auth/components/UserAware.tsx b/src/frontend/src/features/auth/components/UserAware.tsx
new file mode 100644
index 00000000..3a4b84cb
--- /dev/null
+++ b/src/frontend/src/features/auth/components/UserAware.tsx
@@ -0,0 +1,20 @@
+import { useUser } from '@/features/auth'
+import { LoadingScreen } from '@/components/LoadingScreen'
+
+/**
+ * Renders a loading Screen while user info has not been fetched yet,
+ * otherwise directly render children.
+ *
+ * Children usually contain a Screen at some point in the render tree.
+ *
+ * This is helpful to prevent flash of logged-out content for a few milliseconds when user is actually logged in
+ */
+export const UserAware = ({ children }: { children: React.ReactNode }) => {
+ const { isLoggedIn } = useUser()
+
+ return isLoggedIn !== undefined ? (
+ children
+ ) : (
+
+ )
+}
diff --git a/src/frontend/src/features/auth/index.ts b/src/frontend/src/features/auth/index.ts
index 0b533fea..c08b819e 100644
--- a/src/frontend/src/features/auth/index.ts
+++ b/src/frontend/src/features/auth/index.ts
@@ -1,4 +1,4 @@
export { useUser } from './api/useUser'
export { authUrl } from './utils/authUrl'
export { logoutUrl } from './utils/logoutUrl'
-export { RenderIfUserFetched } from './components/RenderIfUserFetched'
+export { UserAware } from './components/UserAware'
diff --git a/src/frontend/src/features/home/routes/Home.tsx b/src/frontend/src/features/home/routes/Home.tsx
index 32130928..69b5e234 100644
--- a/src/frontend/src/features/home/routes/Home.tsx
+++ b/src/frontend/src/features/home/routes/Home.tsx
@@ -1,20 +1,21 @@
import { useTranslation } from 'react-i18next'
import { DialogTrigger } from 'react-aria-components'
-import { Button, Div, Text, VerticallyOffCenter } from '@/primitives'
+import { Button, Text } from '@/primitives'
import { HStack } from '@/styled-system/jsx'
import { navigateTo } from '@/navigation/navigateTo'
-import { generateRoomId } from '@/features/rooms'
-import { authUrl, useUser } from '@/features/auth'
import { Screen } from '@/layout/Screen'
+import { Centered } from '@/layout/Centered'
+import { generateRoomId } from '@/features/rooms'
+import { authUrl, useUser, UserAware } from '@/features/auth'
import { JoinMeetingDialog } from '../components/JoinMeetingDialog'
export const Home = () => {
const { t } = useTranslation('home')
const { isLoggedIn } = useUser()
return (
-
-
-
+
+
+
{t('heading')}
@@ -49,8 +50,8 @@ export const Home = () => {
-
-
-
+
+
+
)
}
diff --git a/src/frontend/src/features/rooms/components/Conference.tsx b/src/frontend/src/features/rooms/components/Conference.tsx
index 0e820338..5f0dd4ae 100644
--- a/src/frontend/src/features/rooms/components/Conference.tsx
+++ b/src/frontend/src/features/rooms/components/Conference.tsx
@@ -8,7 +8,8 @@ import {
import { Room, RoomOptions } from 'livekit-client'
import { keys } from '@/api/queryKeys'
import { navigateTo } from '@/navigation/navigateTo'
-import { QueryAware } from '@/layout/QueryAware'
+import { Screen } from '@/layout/Screen'
+import { QueryAware } from '@/components/QueryAware'
import { fetchRoom } from '../api/fetchRoom'
import { InviteDialog } from './InviteDialog'
@@ -67,24 +68,26 @@ export const Conference = ({
return (
-
-
- {showInviteDialog && (
- setShowInviteDialog(false)}
- />
- )}
-
+
+
+
+ {showInviteDialog && (
+ setShowInviteDialog(false)}
+ />
+ )}
+
+
)
}
diff --git a/src/frontend/src/features/rooms/components/Join.tsx b/src/frontend/src/features/rooms/components/Join.tsx
index 03da5a4e..0657ec97 100644
--- a/src/frontend/src/features/rooms/components/Join.tsx
+++ b/src/frontend/src/features/rooms/components/Join.tsx
@@ -1,6 +1,7 @@
import { useTranslation } from 'react-i18next'
-import { Box } from '@/layout/Box'
import { PreJoin, type LocalUserChoices } from '@livekit/components-react'
+import { Screen } from '@/layout/Screen'
+import { CenteredContent } from '@/layout/CenteredContent'
export const Join = ({
onSubmit,
@@ -10,15 +11,17 @@ export const Join = ({
const { t } = useTranslation('rooms')
return (
-
-
-
+
+
+
+
+
)
}
diff --git a/src/frontend/src/features/rooms/routes/Feedback.tsx b/src/frontend/src/features/rooms/routes/Feedback.tsx
index 468c73fb..bb4fa34a 100644
--- a/src/frontend/src/features/rooms/routes/Feedback.tsx
+++ b/src/frontend/src/features/rooms/routes/Feedback.tsx
@@ -1,19 +1,15 @@
import { useTranslation } from 'react-i18next'
-import { BoxScreen } from '@/layout/BoxScreen'
-import { Div, Link, P } from '@/primitives'
+import { P } from '@/primitives'
+import { Screen } from '@/layout/Screen'
+import { CenteredContent } from '@/layout/CenteredContent'
export const FeedbackRoute = () => {
const { t } = useTranslation('rooms')
return (
-
-
+
+
{t('feedback.body')}
-
-
-
- {t('backToHome', { ns: 'global' })}
-
-
-
+
+
)
}
diff --git a/src/frontend/src/features/rooms/routes/Room.tsx b/src/frontend/src/features/rooms/routes/Room.tsx
index 83de6cbf..a0e5e6f8 100644
--- a/src/frontend/src/features/rooms/routes/Room.tsx
+++ b/src/frontend/src/features/rooms/routes/Room.tsx
@@ -4,9 +4,8 @@ import {
type LocalUserChoices,
} from '@livekit/components-react'
import { useParams } from 'wouter'
-import { Screen } from '@/layout/Screen'
-import { ErrorScreen } from '@/layout/ErrorScreen'
-import { useUser } from '@/features/auth'
+import { ErrorScreen } from '@/components/ErrorScreen'
+import { useUser, UserAware } from '@/features/auth'
import { Conference } from '../components/Conference'
import { Join } from '../components/Join'
@@ -25,21 +24,23 @@ export const Room = () => {
if (!userConfig && !skipJoinScreen) {
return (
-
+
-
+
)
}
return (
-
+
+
+
)
}
diff --git a/src/frontend/src/layout/Box.tsx b/src/frontend/src/layout/Box.tsx
deleted file mode 100644
index 90fc2b44..00000000
--- a/src/frontend/src/layout/Box.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import type { ReactNode } from 'react'
-import { useTranslation } from 'react-i18next'
-import { Box as BoxDiv, H, Link, VerticallyOffCenter } from '@/primitives'
-
-export type BoxProps = {
- children?: ReactNode
- title?: ReactNode
- withBackButton?: boolean
-}
-
-export const Box = ({
- children,
- title = '',
- withBackButton = false,
-}: BoxProps) => {
- const { t } = useTranslation()
- return (
-
-
- {!!title && {title}}
- {children}
- {!!withBackButton && (
-
-
- {t('backToHome')}
-
-
- )}
-
-
- )
-}
diff --git a/src/frontend/src/layout/BoxScreen.tsx b/src/frontend/src/layout/BoxScreen.tsx
deleted file mode 100644
index 147639b2..00000000
--- a/src/frontend/src/layout/BoxScreen.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import { Screen } from './Screen'
-import { Box, type BoxProps } from './Box'
-
-export const BoxScreen = (props: BoxProps) => {
- return (
-
-
-
- )
-}
diff --git a/src/frontend/src/layout/Centered.tsx b/src/frontend/src/layout/Centered.tsx
new file mode 100644
index 00000000..5a403152
--- /dev/null
+++ b/src/frontend/src/layout/Centered.tsx
@@ -0,0 +1,19 @@
+import type { ReactNode } from 'react'
+import { Div, VerticallyOffCenter } from '@/primitives'
+import type { SystemStyleObject } from '../styled-system/types'
+
+export const Centered = ({
+ width = '38rem',
+ children,
+}: {
+ width?: SystemStyleObject['width']
+ children?: ReactNode
+}) => {
+ return (
+
+
+ {children}
+
+
+ )
+}
diff --git a/src/frontend/src/layout/CenteredContent.tsx b/src/frontend/src/layout/CenteredContent.tsx
new file mode 100644
index 00000000..c4a3fbf8
--- /dev/null
+++ b/src/frontend/src/layout/CenteredContent.tsx
@@ -0,0 +1,29 @@
+import { BackToHome } from '@/components/BackToHome'
+import { H } from '@/primitives'
+import { Center } from '@/styled-system/jsx'
+
+export const CenteredContent = ({
+ title,
+ children,
+ withBackButton,
+}: {
+ title?: string
+ children?: React.ReactNode
+ withBackButton?: boolean
+}) => {
+ return (
+ <>
+ {!!title && (
+
+ {title}
+
+ )}
+ {children}
+ {!!withBackButton && (
+
+
+
+ )}
+ >
+ )
+}
diff --git a/src/frontend/src/layout/ErrorScreen.tsx b/src/frontend/src/layout/ErrorScreen.tsx
deleted file mode 100644
index 6f820ad0..00000000
--- a/src/frontend/src/layout/ErrorScreen.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { BoxScreen } from './BoxScreen'
-import { useTranslation } from 'react-i18next'
-
-export const ErrorScreen = () => {
- const { t } = useTranslation()
- return
-}
diff --git a/src/frontend/src/layout/ForbiddenScreen.tsx b/src/frontend/src/layout/ForbiddenScreen.tsx
deleted file mode 100644
index fefd0555..00000000
--- a/src/frontend/src/layout/ForbiddenScreen.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { BoxScreen } from './BoxScreen'
-import { useTranslation } from 'react-i18next'
-
-export const ForbiddenScreen = () => {
- const { t } = useTranslation()
- return
-}
diff --git a/src/frontend/src/layout/Layout.tsx b/src/frontend/src/layout/Layout.tsx
new file mode 100644
index 00000000..9159a0c7
--- /dev/null
+++ b/src/frontend/src/layout/Layout.tsx
@@ -0,0 +1,42 @@
+import { type ReactNode } from 'react'
+import { css } from '@/styled-system/css'
+import { Header } from './Header'
+import { layoutStore } from '@/stores/layout'
+import { useSnapshot } from 'valtio'
+
+export type Layout = 'fullpage' | 'centered'
+
+/**
+ * Layout component for the app.
+ *
+ * This component is meant to be used as a wrapper around the whole app.
+ * In a specific page, use the `Screen` component and change its props to change global page layout.
+ */
+export const Layout = ({ children }: { children: ReactNode }) => {
+ const layoutSnap = useSnapshot(layoutStore)
+ const showHeader = layoutSnap.showHeader
+
+ return (
+
+ {showHeader && }
+
+ {children}
+
+
+ )
+}
diff --git a/src/frontend/src/layout/LoadingScreen.tsx b/src/frontend/src/layout/LoadingScreen.tsx
deleted file mode 100644
index 167c6205..00000000
--- a/src/frontend/src/layout/LoadingScreen.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { useState, useEffect } from 'react'
-import { useTranslation } from 'react-i18next'
-import { BoxScreen } from './BoxScreen'
-import { Screen } from './Screen'
-import { VerticallyOffCenter } from '@/primitives'
-import { Center } from '@/styled-system/jsx'
-
-export const LoadingScreen = ({
- asBox = false,
- renderTimeout = 500,
-}: {
- asBox?: boolean
- renderTimeout?: number
-}) => {
- const { t } = useTranslation()
- // show the loading screen only after a little while to prevent flash of texts
- const [show, setShow] = useState(false)
- useEffect(() => {
- const timeout = setTimeout(() => setShow(true), renderTimeout)
- return () => clearTimeout(timeout)
- }, [renderTimeout])
- if (!show) {
- return null
- }
- const Container = asBox ? BoxScreen : Screen
- return (
-
-
-
- {t('loading')}
-
-
-
- )
-}
diff --git a/src/frontend/src/layout/NotFoundScreen.tsx b/src/frontend/src/layout/NotFoundScreen.tsx
deleted file mode 100644
index 7d4283a0..00000000
--- a/src/frontend/src/layout/NotFoundScreen.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-import { useTranslation } from 'react-i18next'
-import { BoxScreen } from './BoxScreen'
-
-export const NotFoundScreen = () => {
- const { t } = useTranslation()
- return
-}
diff --git a/src/frontend/src/layout/QueryAware.tsx b/src/frontend/src/layout/QueryAware.tsx
deleted file mode 100644
index b396d697..00000000
--- a/src/frontend/src/layout/QueryAware.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import { ErrorScreen } from './ErrorScreen'
-import { LoadingScreen } from './LoadingScreen'
-import { Screen } from './Screen'
-
-export const QueryAware = ({
- status,
- children,
-}: {
- status: 'error' | 'pending' | 'success'
- children: React.ReactNode
-}) => {
- if (status === 'error') {
- return
- }
-
- if (status === 'pending') {
- return
- }
-
- return {children}
-}
diff --git a/src/frontend/src/layout/Screen.tsx b/src/frontend/src/layout/Screen.tsx
index d5c144dc..189dca38 100644
--- a/src/frontend/src/layout/Screen.tsx
+++ b/src/frontend/src/layout/Screen.tsx
@@ -1,35 +1,30 @@
-import type { ReactNode } from 'react'
-import { css } from '@/styled-system/css'
-import { Header } from './Header'
+import { layoutStore } from '@/stores/layout'
+import { Layout } from './Layout'
+import { useEffect } from 'react'
+import { Centered } from './Centered'
+
+export type ScreenProps = {
+ /**
+ * 'fullpage' by default.
+ */
+ layout?: Layout
+ /**
+ * Show header or not.
+ * True by default. Pass undefined to render the screen without modifying current header visibility
+ */
+ header?: boolean
+ children: React.ReactNode
+}
export const Screen = ({
- type,
+ layout = 'fullpage',
+ header = true,
children,
-}: {
- type?: 'splash'
- children?: ReactNode
-}) => {
- return (
-
- {type !== 'splash' && }
-
- {children}
-
-
- )
+}: ScreenProps) => {
+ useEffect(() => {
+ if (header !== undefined) {
+ layoutStore.showHeader = header
+ }
+ }, [header])
+ return layout === 'centered' ? {children} : children
}
diff --git a/src/frontend/src/stores/layout.ts b/src/frontend/src/stores/layout.ts
new file mode 100644
index 00000000..bee6baed
--- /dev/null
+++ b/src/frontend/src/stores/layout.ts
@@ -0,0 +1,9 @@
+import { proxy } from 'valtio'
+
+type State = {
+ showHeader: boolean
+}
+
+export const layoutStore = proxy({
+ showHeader: false,
+})
diff --git a/src/frontend/src/styles/livekit.css b/src/frontend/src/styles/livekit.css
index 11b940b0..07164ac0 100644
--- a/src/frontend/src/styles/livekit.css
+++ b/src/frontend/src/styles/livekit.css
@@ -145,6 +145,7 @@
[data-lk-theme] .lk-prejoin {
padding-top: 0;
+ width: 100%;
}
[data-lk-theme] .lk-participant-tile {