packages feed

concoct-0.1.0: src/Concoct/View/Build.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}

-- |
-- Module      : Concoct.View.Build
-- Copyright   : (c) Matt Hunzinger, 2026
-- License     : BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  : matt@hunzinger.me
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
module Concoct.View.Build (Build (..), runBuild) where

import Concoct.View.Internal
import Control.Monad.State
import Data.IORef

newtype Build m a = Build {unBuild :: StateT ViewState m a}
  deriving (Functor, Applicative, Monad)

instance (MonadIO m) => MonadView m (Build m) where
  useState a = Build $ do
    a' <- lift a
    ref <- liftIO $ newIORef a'
    vs <- get
    let sRef = StateRef ref $ viewUpdater vs
    put vs {viewStack = pushStack sRef (viewStack vs)}
    return sRef
  useRef a = Build $ do
    a' <- lift a
    let vRef = ViewRef a'
    modify $ \vs -> vs {viewStack = pushStack vRef (viewStack vs)}
    return vRef
  useEffect deps f = Build $ do
    deps' <- lift deps
    modify $ \vs -> vs {viewStack = pushStack deps' (viewStack vs)}
    lift $ f deps'
  useOnUnmount _ = return ()
  component vb = Build $ do
    ref <- liftIO $ newIORef False
    let updater m = writeIORef ref True >> m
    vs <- get
    put vs {viewUpdater = updater, viewStack = pushStack ref (viewStack vs)}
    unBuild vb
    vs' <- get
    put vs' {viewUpdater = viewUpdater vs}
  liftView = Build . lift
  switchView cond vTrue vFalse = Build $ do
    cond' <- lift cond
    modify $ \vs -> vs {viewStack = pushStack cond' (viewStack vs)}
    if cond' then unBuild vTrue else unBuild vFalse
  listView items f = Build $ do
    items' <- lift items
    modify $ \vs -> vs {viewStack = pushStack items' (viewStack vs)}
    mapM_ (unBuild . f) items'

runBuild :: (MonadIO m) => Build m a -> ViewState -> m (a, ViewState)
runBuild vb = runStateT (unBuild vb)