{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Glazier.React.Model where import Control.Concurrent.MVar import qualified Control.Disposable as CD import Control.Lens import qualified GHC.Generics as G -- | Lens to the callbacks and interactions with React class HasPlan c pln | c -> pln where plan :: Lens' c pln -- | Lens to the pure model for state and rendering. class HasModel c mdl | c -> mdl where model :: Lens' c mdl -- | Convert to the pure serializable model for saving and restoring class ToOutline c o | c -> o where outline :: c -> o -- | A record of Model and Plan data Scene mdl pln = Scene { _model :: mdl , _plan :: pln } deriving (G.Generic) class HasScene c mdl pln | c -> mdl pln where scene :: Lens' c (Scene mdl pln) instance HasScene (Scene mdl pln) mdl pln where scene = id {-# INLINE scene #-} -- | All scenes should be disposable to make it easier for cleanup of callbacks. instance (CD.Disposing pln, CD.Disposing mdl) => CD.Disposing (Scene mdl pln) instance HasPlan (Scene mdl pln) pln where plan f (Scene mdl pln) = fmap (\pln' -> Scene mdl pln') (f pln) {-# INLINE plan #-} instance HasModel (Scene mdl pln) mdl where model f (Scene mdl pln) = fmap (\mdl' -> Scene mdl' pln) (f mdl) {-# INLINE model #-} -- | A Scene can be converted to Outline by using the Model instance ToOutline mdl ol => ToOutline (Scene mdl pln) ol where outline = view (model . to outline) {-# INLINE outline #-} -- | Frame is a Mvar of Scene. React rendering callback uses this MVar for rendering. type Frame mdl pln = MVar (Scene mdl pln) class HasFrame c mdl pln | c -> mdl pln where frame :: Lens' c (Frame mdl pln) instance HasFrame (Frame mdl pln) mdl pln where frame = id {-# INLINE frame #-} -- | A record of Scene and Frame. data Gizmo mdl pln = Gizmo { _scene :: Scene mdl pln , _frame :: Frame mdl pln } deriving (G.Generic) -- | Undecidableinstances! -- But this is safe because Scene is definitely smaller than Gizmo instance CD.Disposing (Scene mdl pln) => CD.Disposing (Gizmo mdl pln) where disposing s = CD.disposing $ s ^. scene {-# INLINE disposing #-} class (HasScene c mdl pln, HasFrame c mdl pln) => HasGizmo c mdl pln | c -> mdl pln where gizmo :: Lens' c (Gizmo mdl pln) instance HasGizmo (Gizmo mdl pln) mdl pln where gizmo = id {-# INLINE gizmo #-} instance HasFrame (Gizmo mdl pln) mdl pln where frame f (Gizmo scn frm) = fmap (\frm' -> Gizmo scn frm') (f frm) {-# INLINE frame #-} instance HasScene (Gizmo mdl pln) mdl pln where scene f (Gizmo scn frm) = fmap (\scn' -> Gizmo scn' frm) (f scn) {-# INLINE scene #-} instance HasPlan (Gizmo mdl pln) pln where plan = scene . plan {-# INLINE plan #-} instance HasModel (Gizmo mdl pln) mdl where model = scene . model {-# INLINE model #-} -- | A Gizmo can be converted to Outline by using the Model instance ToOutline mdl o => ToOutline (Gizmo mdl pln) o where outline = view (model . to outline) {-# INLINE outline #-}