{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE UndecidableInstances #-} {- | Demonstration of merging multiple sites For an alternative (easier) approach, see `Ex05_MultiRoute.hs`. -} module Ema.Example.Ex04_Multi where import Data.Generics.Sum.Any (AsAny (_As)) import Ema import Ema.Example.Common (tailwindLayout) import Ema.Example.Ex00_Hello qualified as Ex00 import Ema.Example.Ex01_Basic qualified as Ex01 import Ema.Example.Ex02_Clock qualified as Ex02 import Ema.Example.Ex03_Store qualified as Ex03 import Ema.Route.Generic import GHC.Generics qualified as GHC import Generics.SOP (Generic, HasDatatypeInfo, I (I), NP (Nil, (:*))) import Optics.Core (Prism', (%)) import Text.Blaze.Html5 ((!)) import Text.Blaze.Html5 qualified as H import Text.Blaze.Html5.Attributes qualified as A import Prelude hiding (Generic) data M = M { mClock :: Ex02.Model , mClockFast :: Ex02.Model , mStore :: Ex03.Model } deriving stock (GHC.Generic) data R = R_Index | R_Hello Ex00.Route | R_Basic Ex01.Route | R_Clock Ex02.Route | R_ClockFast Ex02.Route | R_Store Ex03.Route deriving stock (Show, Ord, Eq, GHC.Generic) deriving anyclass (Generic, HasDatatypeInfo) deriving (HasSubRoutes, HasSubModels, IsRoute) via ( GenericRoute R '[ WithModel M , WithSubModels [ () , () , () , -- You can refer to a record field by the field name -- (We use `Proxy` only because heteregenous type -- lists must be uni-kind). Proxy "mClock" , Proxy "mClockFast" , -- Or by the field type. -- Thanks to Data.Generics.Product.Any Ex03.Model ] ] ) main :: IO () main = do void $ Ema.runSite @R () instance EmaSite R where siteInput cliAct () = do x1 :: Dynamic m Ex02.Model <- siteInput @Ex02.Route cliAct Ex02.delayNormal x2 :: Dynamic m Ex02.Model <- siteInput @Ex02.Route cliAct Ex02.delayFast x3 :: Dynamic m Ex03.Model <- siteInput @Ex03.Route cliAct () pure $ liftA3 M x1 x2 x3 siteOutput rp m = \case R_Index -> pure $ Ema.AssetGenerated Ema.Html $ renderIndex rp m R_Hello r -> siteOutput (rp % (_As @"R_Hello")) m2 r R_Basic r -> siteOutput (rp % (_As @"R_Basic")) m3 r R_Clock r -> siteOutput (rp % (_As @"R_Clock")) m4 r R_ClockFast r -> siteOutput (rp % (_As @"R_Clock")) m5 r R_Store r -> siteOutput (rp % (_As @"R_Store")) m6 r where I () :* I m2 :* I m3 :* I m4 :* I m5 :* I m6 :* Nil = subModels @R m renderIndex :: Prism' FilePath R -> M -> LByteString renderIndex rp m = tailwindLayout (H.title "Ex04_Multi" >> H.base ! A.href "/") $ H.div ! A.class_ "container mx-auto text-center mt-8 p-2" $ do H.p "You can compose Ema sites. Here are three sites composed to produce one:" H.ul ! A.class_ "flex flex-col justify-center .items-center mt-4 space-y-4" $ do H.li $ routeElem (R_Hello $ Ex00.Route ()) "Ex00_Hello" H.li $ routeElem (R_Basic Ex01.Route_Index) "Ex01_Basic" H.li $ routeElem (R_Clock Ex02.Route_Index) "Ex02_Clock" H.li $ routeElem (R_ClockFast Ex02.Route_Index) "Ex02_ClockFast" H.li $ routeElem (R_Store Ex03.Route_Index) "Ex03_Store" H.p $ do "The current time is: " H.small $ show $ mClock m where routeElem r w = do H.a ! A.class_ "text-xl text-purple-500 hover:underline" ! A.href (H.toValue $ routeUrl rp r) $ w