packages feed

hyperbole-0.7.0: src/Web/Hyperbole/HyperView/Types.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE UndecidableInstances #-}

module Web.Hyperbole.HyperView.Types where

import Data.Kind (Type)
import Effectful
import Effectful.Reader.Dynamic
import Effectful.State.Dynamic
import GHC.Generics
import Web.Hyperbole.Data.Encoded as Encoded
import Web.Hyperbole.Effect.Hyperbole (Hyperbole)
import Web.Hyperbole.View (View (..), ViewAction, ViewId (..), none)


-- HyperView --------------------------------------------

{- | HyperViews are interactive subsections of a 'Page'

Create an instance with a unique view id type and a sum type describing the actions the HyperView supports. The View Id can contain context (a database id, for example)

@
#EMBED Example.Simple data Message

#EMBED Example.Simple instance HyperView Message es where
@
-}
class (ViewId id, ViewAction (Action id), ConcurrencyValue (Concurrency id)) => HyperView id es where
  {- | Outline all actions that are permitted in this HyperView

  > data Action Message = SetMessage Text | ClearMessage
  >   deriving (Generic, ViewAction)
  -}
  data Action id


  {- | Include any child hyperviews here. The compiler will make sure that the page knows how to handle them

  > type Require Messages = '[ChildView]
  -}
  type Require id :: [Type]


  type Require id = '[]


  -- type ViewState id :: Type
  -- type ViewState id = ()

  {- | Control how overlapping actions are handled. 'Drop' by default

  > type Concurrency Autocomplete = Replace
  -}
  type Concurrency id :: ConcurrencyMode


  type Concurrency id = Drop


  {- | Specify how the view should be updated for each Action

  > update (SetMessage msg) = pure $ messageView msg
  > update ClearMessage = pure $ messageView ""
  -}
  update :: (Hyperbole :> es) => Action id -> Eff (Reader id : State (ViewState id) : es) (View id ())


instance HyperView () es where
  data Action () = TupleNone
    deriving (Generic, ViewAction)
  update _ = pure none


-- convert the type to a value
class ConcurrencyValue a where
  concurrencyMode :: ConcurrencyMode
instance ConcurrencyValue 'Drop where
  concurrencyMode = Drop
instance ConcurrencyValue 'Replace where
  concurrencyMode = Replace


data ConcurrencyMode
  = -- | Do not send any actions that occur while one is active. Prevents double-submitting writes or expensive operations
    Drop
  | -- | Ignore the results of older actions in favor of new ones. Use for read-only views with fast-firing interactions, like autocomplete, sliders, etc
    Replace
  deriving (Generic, ToEncoded, FromEncoded)


-- | The top-level view returned by a 'Page'. It carries a type-level list of every 'HyperView' used in our 'Page' so the compiler can check our work and wire everything together.
data Root (views :: [Type]) = Root
  deriving (Generic, ViewId)


instance HyperView (Root views) es where
  data Action (Root views) = RootNone
    deriving (Generic, ViewAction)
  type Require (Root views) = views
  update _ = pure none