module React.Hook where

import Prelude hiding ((!!))

import Control.Monad
import Control.Monad.Except
import Control.Monad.Reader
import Language.Javascript.JSaddle hiding (Ref)

import React.JSaddle
import React.Misc
import React.Types

--TODO: Input can be an initializer function rather than value
--TODO: `set` can take `a -> a` instead of `a`
--TODO: I bet React always returns the same function object for the setter; if we re-wrap the function using `useCallback` each time, we are probably hurting performance by making it be a new object each time and forcing rerendering of children
useState :: (ToJSVal a, FromJSVal a) => a -> Hook (a, a -> JSM ())
useState :: a -> Hook (a, a -> JSM ())
useState a
initialValue = ReaderT React JSM (a, a -> JSM ()) -> Hook (a, a -> JSM ())
forall a. ReaderT React JSM a -> Hook a
Hook (ReaderT React JSM (a, a -> JSM ()) -> Hook (a, a -> JSM ()))
-> ReaderT React JSM (a, a -> JSM ()) -> Hook (a, a -> JSM ())
forall a b. (a -> b) -> a -> b
$ do
  React
react <- ReaderT React JSM React
forall r (m :: * -> *). MonadReader r m => m r
ask
  JSVal
initialJSVal <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ a -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal a
initialValue
  JSVal
result <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ (React
react React -> Text -> JSVal -> JSM JSVal
forall this name args.
(MakeObject this, ToJSString name, MakeArgs args) =>
this -> name -> args -> JSM JSVal
# Text -> Text
t Text
"useState") JSVal
initialJSVal
  Just a
s <- JSM (Maybe a) -> ReaderT React JSM (Maybe a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM (Maybe a) -> ReaderT React JSM (Maybe a))
-> JSM (Maybe a) -> ReaderT React JSM (Maybe a)
forall a b. (a -> b) -> a -> b
$ JSVal -> JSM (Maybe a)
forall a. FromJSVal a => JSVal -> JSM (Maybe a)
fromJSVal (JSVal -> JSM (Maybe a)) -> JSM JSVal -> JSM (Maybe a)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSVal
result JSVal -> Int -> JSM JSVal
forall this. MakeObject this => this -> Int -> JSM JSVal
!! Int
0 --TODO: Exception handling
  JSVal
setter <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ JSVal
result JSVal -> Int -> JSM JSVal
forall this. MakeObject this => this -> Int -> JSM JSVal
!! Int
1
  (a, a -> JSM ()) -> ReaderT React JSM (a, a -> JSM ())
forall (f :: * -> *) a. Applicative f => a -> f a
pure
    ( a
s
    , \a
v' -> JSM JSVal -> JSM ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (JSM JSVal -> JSM ()) -> JSM JSVal -> JSM ()
forall a b. (a -> b) -> a -> b
$ JSVal -> Object -> [a] -> JSM JSVal
forall f this args.
(MakeObject f, MakeObject this, MakeArgs args) =>
f -> this -> args -> JSM JSVal
call JSVal
setter Object
nullObject [a
v']
    )

useRef :: JSVal -> Hook JSVal
useRef :: JSVal -> Hook JSVal
useRef JSVal
initialValue = ReaderT React JSM JSVal -> Hook JSVal
forall a. ReaderT React JSM a -> Hook a
Hook (ReaderT React JSM JSVal -> Hook JSVal)
-> ReaderT React JSM JSVal -> Hook JSVal
forall a b. (a -> b) -> a -> b
$ do
  React
react <- ReaderT React JSM React
forall r (m :: * -> *). MonadReader r m => m r
ask
  JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ (React
react React -> Text -> JSVal -> JSM JSVal
forall this name args.
(MakeObject this, ToJSString name, MakeArgs args) =>
this -> name -> args -> JSM JSVal
# Text -> Text
t Text
"useRef") JSVal
initialValue

useEffect :: (JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> Maybe [JSVal] -> Hook ()
useEffect :: (JSVal -> JSVal -> [JSVal] -> JSM JSVal)
-> Maybe [JSVal] -> Hook ()
useEffect JSVal -> JSVal -> [JSVal] -> JSM JSVal
f Maybe [JSVal]
deps = ReaderT React JSM () -> Hook ()
forall a. ReaderT React JSM a -> Hook a
Hook (ReaderT React JSM () -> Hook ())
-> ReaderT React JSM () -> Hook ()
forall a b. (a -> b) -> a -> b
$ do
  React
react <- ReaderT React JSM React
forall r (m :: * -> *). MonadReader r m => m r
ask
  Function' CallbackId
_ Object
cb <- JSM Function' -> ReaderT React JSM Function'
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM Function' -> ReaderT React JSM Function')
-> JSM Function' -> ReaderT React JSM Function'
forall a b. (a -> b) -> a -> b
$ (JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> JSM Function'
function' JSVal -> JSVal -> [JSVal] -> JSM JSVal
f
  [JSVal]
depsArg <- case Maybe [JSVal]
deps of
    Maybe [JSVal]
Nothing -> [JSVal] -> ReaderT React JSM [JSVal]
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
    Just [JSVal]
someDeps -> do
      JSVal
depsArray <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ [JSVal] -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal [JSVal]
someDeps
      [JSVal] -> ReaderT React JSM [JSVal]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [JSVal
depsArray]
  JSVal
_ <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ (React
react React -> Text -> [JSVal] -> JSM JSVal
forall this name args.
(MakeObject this, ToJSString name, MakeArgs args) =>
this -> name -> args -> JSM JSVal
# Text -> Text
t Text
"useEffect") ([JSVal] -> JSM JSVal) -> [JSVal] -> JSM JSVal
forall a b. (a -> b) -> a -> b
$ [Object -> JSVal
forall a. PToJSVal a => a -> JSVal
pToJSVal Object
cb] [JSVal] -> [JSVal] -> [JSVal]
forall a. Semigroup a => a -> a -> a
<> [JSVal]
depsArg
  () -> ReaderT React JSM ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

useMemo :: (ToJSVal a, FromJSVal a) => JSM a -> Maybe [JSVal] -> Hook a
useMemo :: JSM a -> Maybe [JSVal] -> Hook a
useMemo JSM a
a Maybe [JSVal]
deps = ReaderT React JSM a -> Hook a
forall a. ReaderT React JSM a -> Hook a
Hook (ReaderT React JSM a -> Hook a) -> ReaderT React JSM a -> Hook a
forall a b. (a -> b) -> a -> b
$ do
  React
react <- ReaderT React JSM React
forall r (m :: * -> *). MonadReader r m => m r
ask
  Function' CallbackId
_ Object
cb <- JSM Function' -> ReaderT React JSM Function'
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM Function' -> ReaderT React JSM Function')
-> JSM Function' -> ReaderT React JSM Function'
forall a b. (a -> b) -> a -> b
$ (JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> JSM Function'
function' ((JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> JSM Function')
-> (JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> JSM Function'
forall a b. (a -> b) -> a -> b
$ \JSVal
_ JSVal
_ [JSVal]
_ -> a -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal (a -> JSM JSVal) -> JSM a -> JSM JSVal
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSM a
a
  [JSVal]
depsArg <- case Maybe [JSVal]
deps of
    Maybe [JSVal]
Nothing -> [JSVal] -> ReaderT React JSM [JSVal]
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
    Just [JSVal]
someDeps -> do
      JSVal
depsArray <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ [JSVal] -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal [JSVal]
someDeps
      [JSVal] -> ReaderT React JSM [JSVal]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [JSVal
depsArray]
  JSVal
resultVal <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ (React
react React -> Text -> [JSVal] -> JSM JSVal
forall this name args.
(MakeObject this, ToJSString name, MakeArgs args) =>
this -> name -> args -> JSM JSVal
# Text -> Text
t Text
"useMemo") ([JSVal] -> JSM JSVal) -> [JSVal] -> JSM JSVal
forall a b. (a -> b) -> a -> b
$ [Object -> JSVal
forall a. PToJSVal a => a -> JSVal
pToJSVal Object
cb] [JSVal] -> [JSVal] -> [JSVal]
forall a. Semigroup a => a -> a -> a
<> [JSVal]
depsArg
  Just a
result <- JSM (Maybe a) -> ReaderT React JSM (Maybe a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM (Maybe a) -> ReaderT React JSM (Maybe a))
-> JSM (Maybe a) -> ReaderT React JSM (Maybe a)
forall a b. (a -> b) -> a -> b
$ JSVal -> JSM (Maybe a)
forall a. FromJSVal a => JSVal -> JSM (Maybe a)
fromJSVal JSVal
resultVal
  a -> ReaderT React JSM a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
result

useCallback :: ToJSVal result => (JSVal -> JSVal -> [JSVal] -> JSM result) -> Maybe [JSM JSVal] -> Hook JSVal
useCallback :: (JSVal -> JSVal -> [JSVal] -> JSM result)
-> Maybe [JSM JSVal] -> Hook JSVal
useCallback JSVal -> JSVal -> [JSVal] -> JSM result
f Maybe [JSM JSVal]
deps = ReaderT React JSM JSVal -> Hook JSVal
forall a. ReaderT React JSM a -> Hook a
Hook (ReaderT React JSM JSVal -> Hook JSVal)
-> ReaderT React JSM JSVal -> Hook JSVal
forall a b. (a -> b) -> a -> b
$ do
  React
react <- ReaderT React JSM React
forall r (m :: * -> *). MonadReader r m => m r
ask
  Function' CallbackId
_ Object
cb <- JSM Function' -> ReaderT React JSM Function'
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM Function' -> ReaderT React JSM Function')
-> JSM Function' -> ReaderT React JSM Function'
forall a b. (a -> b) -> a -> b
$ (JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> JSM Function'
function' ((JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> JSM Function')
-> (JSVal -> JSVal -> [JSVal] -> JSM JSVal) -> JSM Function'
forall a b. (a -> b) -> a -> b
$ \JSVal
fObj JSVal
this [JSVal]
args -> result -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal (result -> JSM JSVal) -> JSM result -> JSM JSVal
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSVal -> JSVal -> [JSVal] -> JSM result
f JSVal
fObj JSVal
this [JSVal]
args
  [JSVal]
depsArg <- case Maybe [JSM JSVal]
deps of
    Maybe [JSM JSVal]
Nothing -> [JSVal] -> ReaderT React JSM [JSVal]
forall (f :: * -> *) a. Applicative f => a -> f a
pure []
    Just [JSM JSVal]
someDeps -> do
      JSVal
depsArray <- JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ [JSVal] -> JSM JSVal
forall a. ToJSVal a => a -> JSM JSVal
toJSVal ([JSVal] -> JSM JSVal) -> JSM [JSVal] -> JSM JSVal
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< [JSM JSVal] -> JSM [JSVal]
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence [JSM JSVal]
someDeps
      [JSVal] -> ReaderT React JSM [JSVal]
forall (f :: * -> *) a. Applicative f => a -> f a
pure [JSVal
depsArray]
  JSM JSVal -> ReaderT React JSM JSVal
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (JSM JSVal -> ReaderT React JSM JSVal)
-> JSM JSVal -> ReaderT React JSM JSVal
forall a b. (a -> b) -> a -> b
$ (React
react React -> Text -> [JSVal] -> JSM JSVal
forall this name args.
(MakeObject this, ToJSString name, MakeArgs args) =>
this -> name -> args -> JSM JSVal
# Text -> Text
t Text
"useCallback") ([JSVal] -> JSM JSVal) -> [JSVal] -> JSM JSVal
forall a b. (a -> b) -> a -> b
$ [Object -> JSVal
forall a. PToJSVal a => a -> JSVal
pToJSVal Object
cb] [JSVal] -> [JSVal] -> [JSVal]
forall a. Semigroup a => a -> a -> a
<> [JSVal]
depsArg