aboutsummaryrefslogtreecommitdiff
path: root/client/src/Loadable.hs
blob: 9a14b3f26c539310c6e30df65358a37c4b0e5f92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
module Loadable
  ( Loadable (..)
  , Loadable2 (..)
  , fromEvent
  , view
  , view2
  ) where

import qualified Data.Map     as M
import           Reflex.Dom   (MonadWidget)
import qualified Reflex.Dom   as R

import           Data.Functor (Functor)
import           Data.Text    (Text)
import           Reflex.Dom   (Dynamic, Event, MonadWidget)
import qualified Reflex.Dom   as R

data Loadable t
  = Loading
  | Error Text
  | Loaded t
  deriving Show

instance Functor Loadable where
  fmap f Loading    = Loading
  fmap f (Error e)  = Error e
  fmap f (Loaded x) = Loaded (f x)

instance Applicative Loadable where
  pure x = Loaded x

  Loading <*> _ = Loading
  (Error e) <*> _ = Error e
  (Loaded f) <*> Loading = Loading
  (Loaded f) <*> (Error e) = Error e
  (Loaded f) <*> (Loaded x) = Loaded (f x)

instance Monad Loadable where
  Loading >>= f = Loading
  (Error e) >>= f = Error e
  (Loaded x) >>= f = f x

fromEvent :: forall t m a. MonadWidget t m => Event t (Either Text a) -> m (Dynamic t (Loadable a))
fromEvent =
  R.foldDyn
    (\res _ -> case res of
      Left err -> Error err
      Right t  -> Loaded t
    )
    Loading

view :: forall t m a b. MonadWidget t m => (a -> m b) -> Loadable a -> m (Maybe b)
view _ Loading  = (R.divClass "pageSpinner" $ R.divClass "spinner" $ R.blank) >> return Nothing
view _ (Error e)  = R.text e >> return Nothing
view f (Loaded x) = Just <$> f x

data Loadable2 t a = Loadable2
  { _loadable_isLoading :: Dynamic t Bool
  , _loadable_value     :: Dynamic t (Maybe a)
  }

view2 :: forall t m a b. MonadWidget t m => Loadable2 t a -> (a -> m b) -> m (Event t (Maybe b))
view2 (Loadable2 isLoading value) f =
  withLoader isLoading $
    R.dyn . R.ffor value . viewMaybe $ f

  where
    viewMaybe _ Nothing  = return Nothing
    viewMaybe f (Just x) = Just <$> f x

withLoader
  :: forall t m a. MonadWidget t m
  => Dynamic t Bool
  -> m a
  -> m a
withLoader isLoading block =
  R.divClass "g-Loadable" $ do
    R.elDynAttr "div" (spinnerAttrs <$> isLoading) $
      R.divClass "spinner" R.blank
    R.elDynAttr "div" (blockAttrs <$> isLoading) $
      block
  where
    spinnerAttrs l = M.singleton "class" $
      "g-Loadable__Spinner"
      <> (if l then " g-Loadable__Spinner--Loading" else "")

    blockAttrs l = M.singleton "class" $
      "g-Loadable__Content"
      <> (if l then " g-Loadable__Content--Loading" else "")