diff --git a/apps/rr7/app/content.tsx b/apps/rr7/app/content.tsx index a8483813d9..cfc6134824 100644 --- a/apps/rr7/app/content.tsx +++ b/apps/rr7/app/content.tsx @@ -1,39 +1,20 @@ -import type { LoaderArgs } from './routes/+types.home'; -import { - dehydrate, - QueryClient, - HydrationBoundary, - useQuery, - useQueryClient, -} from '@tanstack/react-query'; -import { useLoaderData, useLocation } from 'react-router'; -import type { MetaFunction } from 'react-router'; -import { usePloneClient } from '@plone/providers'; +import type { Route } from './+types/content'; +import { data, useLoaderData, useLocation } from 'react-router'; import PloneClient from '@plone/client'; import App from '@plone/slots/components/App'; import config from '@plone/registry'; -export const meta: MetaFunction = () => { +export const meta: Route.MetaFunction = ({ data }) => { return [ - { title: 'Plone on React Router 7' }, - { name: 'description', content: 'Welcome to Plone!' }, + { title: data?.title }, + { name: 'description', content: data?.description }, ]; }; const expand = ['navroot', 'breadcrumbs', 'navigation']; // eslint-disable-next-line @typescript-eslint/no-unused-vars -export async function loader({ params, request }: LoaderArgs) { - const queryClient = new QueryClient({ - defaultOptions: { - queries: { - // With SSR, we usually want to set some default staleTime - // above 0 to avoid refetching immediately on the client - staleTime: 60 * 1000, - }, - }, - }); - +export async function loader({ params, request }: Route.LoaderArgs) { const ploneClient = config .getUtility({ name: 'ploneClient', @@ -41,7 +22,7 @@ export async function loader({ params, request }: LoaderArgs) { }) .method(); - const { getContentQuery } = ploneClient as PloneClient; + const { getContent } = ploneClient as PloneClient; const path = new URL(request.url).pathname; @@ -57,30 +38,20 @@ export async function loader({ params, request }: LoaderArgs) { ) ) { console.log('prefetching', path); - await queryClient.prefetchQuery(getContentQuery({ path, expand })); + try { + return await getContent({ path, expand }); + } catch (error) { + throw data('Content Not Found', { status: 404 }); + } } else { console.log('path not prefetched', path); + throw data('Content Not Found', { status: 404 }); } - - return { dehydratedState: dehydrate(queryClient) }; -} - -function Page() { - const { getContentQuery } = usePloneClient(); - const pathname = useLocation().pathname; - const { data } = useQuery(getContentQuery({ path: pathname, expand })); - - if (!data) return 'Loading...'; - return ; } export default function Content() { - const { dehydratedState } = useLoaderData(); - const queryClient = useQueryClient(); + const data = useLoaderData(); + const pathname = useLocation().pathname; - return ( - - - - ); + return ; } diff --git a/apps/rr7/app/root.tsx b/apps/rr7/app/root.tsx index d1c4bb5bae..7340c7627a 100644 --- a/apps/rr7/app/root.tsx +++ b/apps/rr7/app/root.tsx @@ -1,3 +1,5 @@ +import type { LinksFunction } from 'react-router'; +import type { Route } from './+types/root'; import { useState } from 'react'; import { Links, @@ -10,8 +12,8 @@ import { useNavigate as useRRNavigate, useParams, useLoaderData, + isRouteErrorResponse, } from 'react-router'; -import type { LinksFunction } from 'react-router'; import { QueryClient } from '@tanstack/react-query'; import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; @@ -24,8 +26,8 @@ import installSSR from './config.server'; install(); -import '@plone/theming/styles/main.css'; -import '@plone/slots/main.css'; +import themingMain from '@plone/theming/styles/main.css?url'; +import slotsMain from '@plone/slots/main.css?url'; function useNavigate() { const navigate = useRRNavigate(); @@ -37,6 +39,8 @@ function useHrefLocal(to: string) { } export const links: LinksFunction = () => [ + { rel: 'stylesheet', href: themingMain }, + { rel: 'stylesheet', href: slotsMain }, { rel: 'preconnect', href: 'https://fonts.googleapis.com' }, { rel: 'preconnect', @@ -85,6 +89,34 @@ export function Layout({ children }: { children: React.ReactNode }) { ); } +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = 'Oops!'; + let details = 'An unexpected error occurred.'; + let stack: string | undefined; + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? '404' : 'Error'; + details = + error.status === 404 + ? 'The requested page could not be found.' + : error.statusText || details; + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message; + stack = error.stack; + } + + return ( +
+

{message}

+

{details}

+ {stack && ( +
+          {stack}
+        
+ )} +
+ ); +} + export default function App() { if (!import.meta.env.SSR) { config.settings.apiPath = window.env.PLONE_API_PATH; diff --git a/apps/rr7/app/routes.ts b/apps/rr7/app/routes.ts index a4981be3dc..4d6b9ec32f 100644 --- a/apps/rr7/app/routes.ts +++ b/apps/rr7/app/routes.ts @@ -4,7 +4,7 @@ import { index, route } from '@react-router/dev/routes'; const routes: RouteConfig = [ index('content.tsx', { id: 'index' }), route('ok', 'okroute.tsx', { id: 'ok' }), - route('*', 'content.tsx', { id: 'splat' }), + route('*', 'content.tsx', { id: 'content' }), ]; export default routes; diff --git a/packages/client/news/6594.feature b/packages/client/news/6594.feature new file mode 100644 index 0000000000..7ba9fa94c3 --- /dev/null +++ b/packages/client/news/6594.feature @@ -0,0 +1 @@ +Import `getContent` bare fetcher. @sneridagh diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index b0d5e5289a..a14033d7f2 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -4,6 +4,7 @@ import { } from './restapi/login/post'; import type { LoginArgs } from './restapi/login/post'; +import { getContent as _getContent } from './restapi/content/get'; import { getContentQuery as _getContentQuery } from './restapi/content/get'; import { createContentMutation as _createContentMutation } from './restapi/content/add'; import { updateContentMutation as _updateContentMutation } from './restapi/content/update'; @@ -150,6 +151,7 @@ export default class PloneClient { /* Content queries */ + getContent = queryWithConfig(_getContent, this.getConfig); getContentQuery = queryWithConfig(_getContentQuery, this.getConfig); createContentMutation = mutationWithConfig( _createContentMutation,