diff --git a/src/app/(note-topic)/notes/topics/page.tsx b/src/app/(note-topic)/notes/topics/page.tsx index 0d9a9bcda7..39e5f66f40 100644 --- a/src/app/(note-topic)/notes/topics/page.tsx +++ b/src/app/(note-topic)/notes/topics/page.tsx @@ -30,6 +30,7 @@ export default function Page() { return ( ) => {
- + - +
- + {props.children}
diff --git a/src/app/feed/route.tsx b/src/app/feed/route.tsx index 7331bb9b4c..360d5fd117 100644 --- a/src/app/feed/route.tsx +++ b/src/app/feed/route.tsx @@ -15,7 +15,7 @@ export const revalidate = 60 * 60 // 1 hour export async function GET() { const ReactDOM = (await import('react-dom/server')).default - const queryClient = await getQueryClient() + const queryClient = getQueryClient() const { author, data, url } = await queryClient.fetchQuery({ queryKey: ['rss'], diff --git a/src/app/layout.tsx b/src/app/layout.tsx index bf9924592c..32236b1ce2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -8,6 +8,7 @@ import type { AppThemeConfig } from './config' import { ClerkProvider } from '@clerk/nextjs' import PKG from '~/../package.json' +import { HydrationEndDetector } from '~/components/common/HydrationEndDetector' import { Root } from '~/components/layout/root/Root' import { TocAutoScroll } from '~/components/widgets/toc/TocAutoScroll' import { attachUA } from '~/lib/attach-ua' @@ -27,7 +28,7 @@ export const revalidate = 60 let aggregationData: (AggregateRoot & { theme: AppThemeConfig }) | null = null export const generateMetadata = async () => { - const queryClient = await getQueryClient() + const queryClient = getQueryClient() const fetchedData = aggregationData ?? @@ -121,6 +122,7 @@ export default async function RootLayout(props: Props) { + - - {props.children} + + + {props.children} + ) => { <>
- + {props.children} diff --git a/src/app/posts/page.tsx b/src/app/posts/page.tsx index 57a943dc52..f1a0f67e27 100644 --- a/src/app/posts/page.tsx +++ b/src/app/posts/page.tsx @@ -34,7 +34,12 @@ export default async (props: Props) => {
    {data.map((item, index) => { return ( - + ) diff --git a/src/app/sitemap/route.tsx b/src/app/sitemap/route.tsx index c18d491e28..00ac3d9d72 100644 --- a/src/app/sitemap/route.tsx +++ b/src/app/sitemap/route.tsx @@ -5,7 +5,7 @@ export const runtime = 'edge' export const revalidate = 60 * 60 // 1 hour export const GET = async () => { - const queryClient = await getQueryClient() + const queryClient = getQueryClient() const { data } = await queryClient.fetchQuery({ queryKey: ['sitemap'], diff --git a/src/components/common/ErrorBoundary.tsx b/src/components/common/ErrorBoundary.tsx index af097a8eec..9a60a3de3a 100644 --- a/src/components/common/ErrorBoundary.tsx +++ b/src/components/common/ErrorBoundary.tsx @@ -5,11 +5,32 @@ import type { FC, PropsWithChildren } from 'react' import { captureException } from '@sentry/nextjs' +import { StyledButton } from '../ui/button' + const Noop = () => null + +const FallbackComponent = () => { + return ( +
    + Something went wrong. Please contract to{' '} + + i@innei.ren + + . + { + window.location.reload() + }} + > + Reload Page + +
    + ) +} export const ErrorBoundary: FC = ({ children }) => { return ( { console.error(e) diff --git a/src/components/common/HydrationEndDetector.tsx b/src/components/common/HydrationEndDetector.tsx new file mode 100644 index 0000000000..b5630d7a04 --- /dev/null +++ b/src/components/common/HydrationEndDetector.tsx @@ -0,0 +1,23 @@ +'use client' + +import { useEffect } from 'react' +import { atom } from 'jotai' + +import { jotaiStore } from '~/lib/store' + +const hydrateEndAtom = atom(false) + +/** + * To skip page transition when first load, improve LCP + */ +export const HydrationEndDetector = () => { + useEffect(() => { + // waiting for hydration end and animation end + setTimeout(() => { + jotaiStore.set(hydrateEndAtom, true) + }, 2000) + }, []) + return null +} + +export const isHydrationEnded = () => jotaiStore.get(hydrateEndAtom) diff --git a/src/components/layout/footer/FooterInfo.tsx b/src/components/layout/footer/FooterInfo.tsx index 3921b04887..ac87a3ebbf 100644 --- a/src/components/layout/footer/FooterInfo.tsx +++ b/src/components/layout/footer/FooterInfo.tsx @@ -22,7 +22,7 @@ export const FooterInfo = () => { } const FooterLinkSection = async () => { - const queryClient = await getQueryClient() + const queryClient = getQueryClient() const data = await queryClient.fetchQuery(queries.aggregation.root()) const { footer } = data.theme const footerConfig: FooterConfig = footer || { @@ -123,7 +123,7 @@ const FooterBottom = async () => { // } // } - const queryClient = await getQueryClient() + const queryClient = getQueryClient() const data = await queryClient.fetchQuery(queries.aggregation.root()) const { footer } = data.theme const footerConfig = footer || {} diff --git a/src/components/ui/transition/factor.tsx b/src/components/ui/transition/factor.tsx index ca27958262..138d0fdcf3 100644 --- a/src/components/ui/transition/factor.tsx +++ b/src/components/ui/transition/factor.tsx @@ -1,6 +1,6 @@ 'use client' -import { memo } from 'react' +import { memo, useMemo } from 'react' import { m } from 'framer-motion' import type { HTMLMotionProps, @@ -11,6 +11,7 @@ import type { import type { FC, PropsWithChildren } from 'react' import type { BaseTransitionProps } from './typings' +import { isHydrationEnded } from '~/components/common/HydrationEndDetector' import { microReboundPreset } from '~/constants/spring' interface TransitionViewParams { @@ -24,16 +25,16 @@ export const createTransitionView = ( params: TransitionViewParams, ): FC> => { const { from, to, initial, preset } = params + const TransitionView = (props: PropsWithChildren) => { const { timeout = {}, duration = 0.5, - appear = true, animation = {}, as = 'div', delay = 0, - + lcpOptimization = false, ...rest } = props @@ -41,11 +42,17 @@ export const createTransitionView = ( const MotionComponent = m[as] as FC> - if (!appear) return props.children - return ( + lcpOptimization + ? isHydrationEnded() + ? initial || from + : true + : initial || from, + [], + )} animate={{ ...to, transition: { @@ -54,9 +61,6 @@ export const createTransitionView = ( ...animation.enter, delay: enter / 1000, }, - onTransitionEnd() { - props.onEntered?.() - }, }} exit={{ ...from, diff --git a/src/components/ui/transition/typings.ts b/src/components/ui/transition/typings.ts index 06c50ffcef..1e31139cdf 100644 --- a/src/components/ui/transition/typings.ts +++ b/src/components/ui/transition/typings.ts @@ -2,8 +2,7 @@ import type { HTMLMotionProps, m, TargetAndTransition } from 'framer-motion' export interface BaseTransitionProps extends HTMLMotionProps<'div'> { duration?: number - onEntered?: () => void - appear?: boolean + timeout?: { exit?: number enter?: number @@ -16,5 +15,6 @@ export interface BaseTransitionProps extends HTMLMotionProps<'div'> { exit?: TargetAndTransition['transition'] } + lcpOptimization?: boolean as?: keyof typeof m } diff --git a/src/components/widgets/comment/CommentRootLazy.tsx b/src/components/widgets/comment/CommentRootLazy.tsx index c799d0f7d0..20e33eb8f0 100644 --- a/src/components/widgets/comment/CommentRootLazy.tsx +++ b/src/components/widgets/comment/CommentRootLazy.tsx @@ -2,6 +2,7 @@ import dynamic from 'next/dynamic' +import { ErrorBoundary } from '~/components/common/ErrorBoundary' import { LazyLoad } from '~/components/common/Lazyload' import { Loading } from '~/components/ui/loading' @@ -17,8 +18,10 @@ const CommentAreaRoot = dynamic( export const CommentAreaRootLazy: typeof CommentAreaRoot = (props) => { return ( - - - + + + + + ) } diff --git a/src/components/widgets/comment/index.ts b/src/components/widgets/comment/index.ts index 9c0d213663..7fb7969384 100644 --- a/src/components/widgets/comment/index.ts +++ b/src/components/widgets/comment/index.ts @@ -15,8 +15,4 @@ export const CommentBoxRootLazy = dynamic( }, ) -// export { CommentAreaRoot } from './CommentRoot' export { CommentAreaRootLazy } from './CommentRootLazy' - -// export { Comments } from './Comments' -// export { CommentBoxRoot } from './CommentBox'