packages feed

ema-0.8.0.0: src/Ema/Example/Common.hs

module Ema.Example.Common (
  tailwindLayout,
  watchDirForked,
) where

import Control.Concurrent (Chan, forkIO, newChan, threadDelay)
import System.FSNotify qualified as FSNotify
import Text.Blaze.Html.Renderer.Utf8 qualified as RU
import Text.Blaze.Html5 ((!))
import Text.Blaze.Html5 qualified as H
import Text.Blaze.Html5.Attributes qualified as A

-- | A simple and off-the-shelf layout using Tailwind CSS
tailwindLayout :: H.Html -> H.Html -> LByteString
tailwindLayout h b =
  layoutWith "en" "UTF-8" (tailwind2ShimCdn >> h) $
    -- The "overflow-y-scroll" makes the scrollbar visible always, so as to
    -- avoid janky shifts when switching to routes with suddenly scrollable content.
    H.body ! A.class_ "overflow-y-scroll" $ b
  where
    -- A general layout
    layoutWith :: H.AttributeValue -> H.AttributeValue -> H.Html -> H.Html -> LByteString
    layoutWith lang encoding appHead appBody = RU.renderHtml $ do
      H.docType
      H.html ! A.lang lang $ do
        H.head $ do
          H.meta ! A.charset encoding
          -- This makes the site mobile friendly by default.
          H.meta ! A.name "viewport" ! A.content "width=device-width, initial-scale=1"
          appHead
        appBody

    -- Loads full tailwind CSS from CDN (not good for production)
    tailwind2ShimCdn :: H.Html
    tailwind2ShimCdn =
      H.link
        ! A.href "https://unpkg.com/tailwindcss@2/dist/tailwind.min.css"
        ! A.rel "stylesheet"
        ! A.type_ "text/css"

-- Observe changes to a directory path, and return the `Chan` of its events.
watchDirForked :: FilePath -> IO (Chan FSNotify.Event)
watchDirForked path = do
  ch <- newChan
  -- FIXME: We should be using race_, not forkIO.
  void . forkIO $
    FSNotify.withManager $ \mgr -> do
      _stopListening <- FSNotify.watchDirChan mgr path (const True) ch
      threadDelay maxBound
  pure ch