import { AppContext as FleurAppContext } from '@fleur/fleur' ; import { bindFleurContext , deserializeContext , FleurishGetServerSidePropsContext , FleurishGetStaticPropsContext , FleurishNextAppContext , NextJsOps , serializeContext , } from '@fleur/next' ; import { FleurContext } from '@fleur/react' ; import { GetServerSidePropsResult , GetStaticPropsResult } from 'next' ; import { AppInitialProps , AppProps , AppContext } from 'next/app' ; import { useMemo } from 'react' ; import { useRef , useEffect } from 'react' ; import { createContext } from '../domains' ; const FLEUR_CONTEXT_KEY = '__FLEUR_CONTEXT__' ; export type FleurSSProps = { __FLEUR_STATE__ : string ; } ; const clientStatic = { } ; /** Get static object only client side, otherwise always returns new object */ const getSafeClientStatic = ( ) => { const isServer = typeof window === 'undefined' ; if ( ! isServer ) return clientStatic ; return { } as any ; } ; export const getOrCreateFleurContext = ( state = null ) : FleurAppContext => { const clientStatic = getSafeClientStatic ( ) ; if ( clientStatic [ FLEUR_CONTEXT_KEY ] ) { return clientStatic [ FLEUR_CONTEXT_KEY ] ; } const context = createContext ( ) ; if ( state ) context . rehydrate ( state ) ; clientStatic [ FLEUR_CONTEXT_KEY ] = context ; return context ; } ; export interface FleurishFunctionApp < P = { } > { ( props ) : JSX . Element ; getInitialProps ? ( appContext ) : Promise < Record < string , any > & Partial < AppInitialProps > > ; } export function appWithFleur < P > ( App , { enableGetIntialProps } ) { const FleurishApp = ( { pageProps : { __FLEUR_STATE__ , ... pageProps } , ... props } ) => { const fleurContext = useMemo ( ( ) => getOrCreateFleurContext ( deserializeContext ( __FLEUR_STATE__ ) ) , [ ] ) ; useFleurRehydration ( fleurContext , __FLEUR_STATE__ ) ; return ( < FleurContext value = { fleurContext } > < App pageProps = { pageProps } { ... ( props as any ) } /> < /FleurContext>); } ; if ( enableGetIntialProps ) { FleurishApp . getInitialProps = async ( nextAppContext ) : Promise < AppInitialProps > => { const fleurContext = getOrCreateFleurContext ( ) ; const fleurishAppContext = bindFleurContext ( fleurContext , nextAppContext ) ; const appProps = await App . getInitialProps ?. ( fleurishAppContext ) ; const pageProps = await fleurishAppContext . Component . getInitialProps ?. ( fleurishAppContext . ctx ) ; return { ... appProps , pageProps : { __FLEUR_STATE__ : serializeContext ( fleurContext ) , ... pageProps , } , } ; } ; } return FleurishApp ; } /** Rehydrate serverSideProps to Stores */ const useFleurRehydration = ( context , state ) => { const isFirstRendering = useRef < boolean > ( true ) ; useEffect ( ( ) => { if ( isFirstRendering . current ) return ; if ( state == null ) return ; context . executeOperation ( NextJsOps . rehydrateServerSideProps , deserializeContext ( state ) ) ; } , [ context , state ] ) ; isFirstRendering . current = false ; } ; export const getServerSidePropsWithFleur = < P extends object = any > ( getServerSideProps ) => { return async ( context ) : Promise < GetServerSidePropsResult < P & FleurSSProps > > => { const fleurCtx = getOrCreateFleurContext ( ) ; context . executeOperation = fleurCtx . executeOperation ; context . getStore = fleurCtx . getStore ; const result = await getServerSideProps ( context ) ; if ( 'props' in result ) { return { ... result , props : { ... ( await result . props ) , __FLEUR_STATE__ : serializeContext ( fleurCtx ) , } , } ; } return result ; } ; } ; export const getStaticPropsWithFleur = < P extends object = any > ( getStaticProps ) => { return async ( context ) : Promise < GetServerSidePropsResult < P & FleurSSProps > > => { const fleurCtx = getOrCreateFleurContext ( ) ; context . executeOperation = fleurCtx . executeOperation ; context . getStore = fleurCtx . getStore ; const result = await getStaticProps ( context ) ; if ( 'props' in result ) { return { ... result , props : { ... result . props , __FLEUR_STATE__ : serializeContext ( fleurCtx ) } , } ; } return result ; } ; } ;