mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
NextJS page router layout function - faster, lower flicker.
This commit is contained in:
+10
-3
@@ -24,8 +24,14 @@ import { hasGoogleAnalytics, OptionalGoogleAnalytics } from '~/common/components
|
||||
import { isVercelFromFrontend } from '~/common/util/pwaUtils';
|
||||
|
||||
|
||||
const MyApp = ({ Component, emotionCache, pageProps }: MyAppProps) =>
|
||||
<>
|
||||
const MyApp = ({ Component, emotionCache, pageProps }: MyAppProps) => {
|
||||
|
||||
// We are using a nextjs per-page layout pattern to bring the (Optima) layout creation to a shared place
|
||||
// This reduces the flicker and the time switching between apps, and seems to not have impact on
|
||||
// the build. This is a good trade-off for now.
|
||||
const getLayout = Component.getLayout ?? ((page: any) => page);
|
||||
|
||||
return <>
|
||||
|
||||
<Head>
|
||||
<title>{Brand.Title.Common}</title>
|
||||
@@ -39,7 +45,7 @@ const MyApp = ({ Component, emotionCache, pageProps }: MyAppProps) =>
|
||||
{/* ^ SSR boundary */}
|
||||
<ProviderBootstrapLogic>
|
||||
<ProviderSnacks>
|
||||
<Component {...pageProps} />
|
||||
{getLayout(<Component {...pageProps} />)}
|
||||
</ProviderSnacks>
|
||||
</ProviderBootstrapLogic>
|
||||
</ProviderBackendCapabilities>
|
||||
@@ -52,6 +58,7 @@ const MyApp = ({ Component, emotionCache, pageProps }: MyAppProps) =>
|
||||
{hasGoogleAnalytics && <OptionalGoogleAnalytics />}
|
||||
|
||||
</>;
|
||||
};
|
||||
|
||||
// enables the React Query API invocation
|
||||
export default apiQuery.withTRPC(MyApp);
|
||||
+2
-4
@@ -2,9 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { AppCall } from '../src/apps/call/AppCall';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function CallPage() {
|
||||
return withLayout({ type: 'optima' }, <AppCall />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => <AppCall />);
|
||||
|
||||
+2
-4
@@ -2,9 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { AppBeam } from '../../src/apps/beam/AppBeam';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function BeamPage() {
|
||||
return withLayout({ type: 'optima' }, <AppBeam />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => <AppBeam />);
|
||||
|
||||
+2
-4
@@ -2,9 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { AppDiff } from '../src/apps/diff/AppDiff';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function DiffPage() {
|
||||
return withLayout({ type: 'optima' }, <AppDiff />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => <AppDiff />);
|
||||
|
||||
+2
-4
@@ -2,9 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { AppDraw } from '../src/apps/draw/AppDraw';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function DrawPage() {
|
||||
return withLayout({ type: 'optima' }, <AppDraw />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => <AppDraw />);
|
||||
|
||||
+7
-4
@@ -2,13 +2,16 @@ import * as React from 'react';
|
||||
|
||||
import { AppChat } from '../src/apps/chat/AppChat';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
import { useDebugHook } from '~/common/components/useDebugHook';
|
||||
|
||||
|
||||
export default function IndexPage() {
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => {
|
||||
|
||||
useDebugHook('IndexPage');
|
||||
|
||||
// TODO: This Index page will point to the Dashboard (or a landing page)
|
||||
// For now it offers the chat experience, but this will change. #299
|
||||
|
||||
return withLayout({ type: 'optima' }, <AppChat />);
|
||||
}
|
||||
return <AppChat />;
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import { AppPlaceholder } from '../../src/apps/AppPlaceholder';
|
||||
import { getBackendCapabilities } from '~/modules/backend/store-backend-capabilities';
|
||||
import { getPlantUmlServerUrl } from '~/modules/blocks/code/RenderCode';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
// app config
|
||||
@@ -164,6 +164,4 @@ function AppDebug() {
|
||||
}
|
||||
|
||||
|
||||
export default function DebugPage() {
|
||||
return withLayout({ type: 'plain' }, <AppDebug />);
|
||||
};
|
||||
export default withNextJSPerPageLayout({ type: 'plain' }, () => <AppDebug />);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useModelsStore } from '~/modules/llms/store-llms';
|
||||
import { InlineError } from '~/common/components/InlineError';
|
||||
import { apiQuery } from '~/common/util/trpc.client';
|
||||
import { navigateToIndex, useRouterQuery } from '~/common/app.routes';
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
function CallbackOpenRouterPage(props: { openRouterCode: string | undefined }) {
|
||||
@@ -81,10 +81,11 @@ function CallbackOpenRouterPage(props: { openRouterCode: string | undefined }) {
|
||||
* Docs: https://openrouter.ai/docs#oauth
|
||||
* Example URL: https://localhost:3000/link/callback_openrouter?code=SomeCode
|
||||
*/
|
||||
export default function CallbackPage() {
|
||||
export default withNextJSPerPageLayout({ type: 'plain' }, () => {
|
||||
|
||||
// external state - get the 'code=...' from the URL
|
||||
const { code } = useRouterQuery<{ code: string | undefined }>();
|
||||
|
||||
return withLayout({ type: 'plain' }, <CallbackOpenRouterPage openRouterCode={code} />);
|
||||
}
|
||||
return <CallbackOpenRouterPage openRouterCode={code} />;
|
||||
|
||||
});
|
||||
|
||||
@@ -3,13 +3,14 @@ import * as React from 'react';
|
||||
import { AppLinkChat } from '../../../src/apps/link-chat/AppLinkChat';
|
||||
|
||||
import { useRouterQuery } from '~/common/app.routes';
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function ChatLinkPage() {
|
||||
export default withNextJSPerPageLayout({ type: 'optima', suspendAutoModelsSetup: true }, () => {
|
||||
|
||||
// external state
|
||||
const { chatLinkId } = useRouterQuery<{ chatLinkId: string | undefined }>();
|
||||
|
||||
return withLayout({ type: 'optima', suspendAutoModelsSetup: true }, <AppLinkChat chatLinkId={chatLinkId || null} />);
|
||||
}
|
||||
return <AppLinkChat chatLinkId={chatLinkId || null} />;
|
||||
|
||||
});
|
||||
@@ -10,7 +10,7 @@ import { callBrowseFetchPage } from '~/modules/browse/browse.client';
|
||||
import { LogoProgress } from '~/common/components/LogoProgress';
|
||||
import { asValidURL } from '~/common/util/urlUtils';
|
||||
import { navigateToIndex, useRouterQuery } from '~/common/app.routes';
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
/**
|
||||
@@ -135,6 +135,4 @@ function AppShareTarget() {
|
||||
* This page will be invoked on mobile when sharing Text/URLs/Files from other APPs
|
||||
* Example URL: https://localhost:3000/link/share_target?title=This+Title&text=https%3A%2F%2Fexample.com%2Fapp%2Fpath
|
||||
*/
|
||||
export default function ShareTargetPage() {
|
||||
return withLayout({ type: 'plain' }, <AppShareTarget />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'plain' }, () => <AppShareTarget />);
|
||||
|
||||
+5
-4
@@ -3,12 +3,13 @@ import * as React from 'react';
|
||||
import { AppNews } from '../src/apps/news/AppNews';
|
||||
import { markNewsAsSeen } from '../src/apps/news/news.version';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function NewsPage() {
|
||||
export default withNextJSPerPageLayout({ type: 'optima', suspendAutoModelsSetup: true }, () => {
|
||||
|
||||
// 'touch' the last seen news version
|
||||
React.useEffect(() => markNewsAsSeen(), []);
|
||||
|
||||
return withLayout({ type: 'optima', suspendAutoModelsSetup: true }, <AppNews />);
|
||||
}
|
||||
return <AppNews />;
|
||||
});
|
||||
+2
-4
@@ -2,9 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { AppPersonas } from '../src/apps/personas/AppPersonas';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function PersonasPage() {
|
||||
return withLayout({ type: 'optima' }, <AppPersonas />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => <AppPersonas />);
|
||||
|
||||
+2
-4
@@ -2,9 +2,7 @@ import * as React from 'react';
|
||||
|
||||
import { AppTokens } from '../src/apps/tokens/AppTokens';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function TokenizerPage() {
|
||||
return withLayout({ type: 'optima' }, <AppTokens />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => <AppTokens />);
|
||||
|
||||
+3
-7
@@ -1,12 +1,8 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Box } from '@mui/joy';
|
||||
import { AppPlaceholder } from '../src/apps/AppPlaceholder';
|
||||
|
||||
// import { AppWorkspace } from '../src/apps/personas/AppWorkspace';
|
||||
|
||||
import { withLayout } from '~/common/layout/withLayout';
|
||||
import { withNextJSPerPageLayout } from '~/common/layout/withLayout';
|
||||
|
||||
|
||||
export default function PersonasPage() {
|
||||
return withLayout({ type: 'optima' }, <Box />);
|
||||
}
|
||||
export default withNextJSPerPageLayout({ type: 'optima' }, () => <AppPlaceholder />);
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import type { NextPageWithLayout } from '~/common/types/next.page';
|
||||
|
||||
import { OptimaLayout } from './optima/OptimaLayout';
|
||||
import { PlainLayout } from './plain/PlainLayout';
|
||||
|
||||
|
||||
type WithLayout = {
|
||||
type PerPageLayoutOptions = {
|
||||
type: 'optima';
|
||||
suspendAutoModelsSetup?: boolean;
|
||||
} | {
|
||||
@@ -13,23 +15,49 @@ type WithLayout = {
|
||||
|
||||
|
||||
/**
|
||||
* Dynamic page-level layouting: a wrapper that adds the layout around the children.
|
||||
* Next.js page-level layouting: a wrapper that adds the layout around the page as a layout function.
|
||||
*/
|
||||
export function withLayout(layoutOptions: WithLayout, children: React.ReactNode): React.ReactElement {
|
||||
export function withNextJSPerPageLayout(options: PerPageLayoutOptions, page: NextPageWithLayout): NextPageWithLayout {
|
||||
|
||||
const { type, ...rest } = layoutOptions;
|
||||
const { type, ...rest } = options;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case 'optima':
|
||||
return <OptimaLayout {...rest}>{children}</OptimaLayout>;
|
||||
page.getLayout = (page: React.ReactElement) => <OptimaLayout {...rest}>{page}</OptimaLayout>;
|
||||
return page;
|
||||
|
||||
case 'plain':
|
||||
return <PlainLayout {...rest}>{children}</PlainLayout>;
|
||||
page.getLayout = (page: React.ReactElement) => <PlainLayout {...rest}>{page}</PlainLayout>;
|
||||
return page;
|
||||
|
||||
default:
|
||||
console.error('No layout specified for this top-level page');
|
||||
return <>{children}</>;
|
||||
return page;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * Dynamic page-level layouting: a wrapper that adds the layout around the children.
|
||||
// */
|
||||
// export function withLayout(layoutOptions: LayoutOptions, children: React.ReactNode): React.ReactElement {
|
||||
//
|
||||
// const { type, ...rest } = layoutOptions;
|
||||
//
|
||||
// switch (type) {
|
||||
//
|
||||
// case 'optima':
|
||||
// return <OptimaLayout {...rest}>{children}</OptimaLayout>;
|
||||
//
|
||||
// case 'plain':
|
||||
// return <PlainLayout {...rest}>{children}</PlainLayout>;
|
||||
//
|
||||
// default:
|
||||
// console.error('No layout specified for this top-level page');
|
||||
// return <>{children}</>;
|
||||
//
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
Vendored
+9
-6
@@ -1,17 +1,20 @@
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import type { NextPage } from 'next';
|
||||
import type { EmotionCache } from '@emotion/react';
|
||||
|
||||
|
||||
// export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
// // require .layoutOptions on the page component
|
||||
// layoutOptions: LayoutOptions;
|
||||
// };
|
||||
export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
|
||||
// definition of the per-page layout function, as per:
|
||||
// https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts#per-page-layouts
|
||||
getLayout?: (page: ReactElement) => ReactNode;
|
||||
}
|
||||
|
||||
// Extend the AppProps type with the custom page component type
|
||||
declare module 'next/app' {
|
||||
import { AppProps } from 'next/app';
|
||||
|
||||
type MyAppProps = AppProps & {
|
||||
// Component: NextPageWithLayout;
|
||||
Component: NextPageWithLayout
|
||||
emotionCache?: EmotionCache;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user