-----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Miso.Native.FFI
-- Copyright   :  (C) 2016-2026 David M. Johnson
-- License     :  BSD3-style (see the file LICENSE)
-- Maintainer  :  David M. Johnson <code@dmj.io>
-- Stability   :  experimental
-- Portability :  non-portable
----------------------------------------------------------------------------
module Miso.Native.FFI
  ( -- *** Lynx specific FFI
    setInterval
  , clearInterval
  , invokeExec
  , enableDebugging
  ) where
----------------------------------------------------------------------------
import Control.Monad
-----------------------------------------------------------------------------
import Miso
-----------------------------------------------------------------------------
-- | Turn on the native console→syslog bridge for on-device debugging.
--
-- On a physical device, background-thread @console.*@ output is not printed to
-- the platform log (iOS syslog / Android logcat) without a LynxDevTool
-- connection. After calling this, every 'Miso.FFI.consoleError' (and any other
-- @console.error@) is mirrored — prefixed @[miso]@ — through
-- @lynx.reportError@, which the host /does/ surface in the device log. Grep for
-- @[miso]@ in @idevicesyslog@ (iOS) or @adb logcat@ (Android).
--
-- Sets @globalThis.debug = true@; the bridge checks that flag per line, so this
-- takes effect immediately even though it runs after startup. Enable it early
-- (e.g. at the top of @main@) to capture diagnostics from the whole session.
enableDebugging :: IO ()
enableDebugging :: IO ()
enableDebugging = MisoString -> Bool -> Object -> IO ()
forall v. ToJSVal v => MisoString -> v -> Object -> IO ()
set MisoString
"debug" Bool
True (Object -> IO ()) -> (JSVal -> Object) -> JSVal -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. JSVal -> Object
Object (JSVal -> IO ()) -> IO JSVal -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< MisoString -> IO JSVal
jsg MisoString
"globalThis"
-----------------------------------------------------------------------------
-- | <https://lynxjs.org/api/lynx-api/global/set-interval.html>
--
setInterval :: Double -> IO () -> IO Double
setInterval :: Double -> IO () -> IO Double
setInterval Double
delay IO ()
f = do
  cb <- JSVal -> IO JSVal
forall a. ToJSVal a => a -> IO JSVal
toJSVal (JSVal -> IO JSVal) -> IO JSVal -> IO JSVal
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< IO () -> IO JSVal
asyncCallback IO ()
f
  v <- toJSVal delay
  result <- jsg "lynx" # "setInterval" $ ([cb, v] :: [JSVal])
  fromJSValUnchecked result
-----------------------------------------------------------------------------
-- | <https://lynxjs.org/api/lynx-api/global/clear-interval.html>
--
clearInterval :: Double -> IO Double
clearInterval :: Double -> IO Double
clearInterval Double
intervalId = do
  result <- MisoString -> IO JSVal
jsg MisoString
"lynx" IO JSVal -> MisoString -> [Double] -> IO JSVal
forall object args.
(ToObject object, ToArgs args) =>
object -> MisoString -> args -> IO JSVal
# MisoString
"clearInterval" ([Double] -> IO JSVal) -> [Double] -> IO JSVal
forall a b. (a -> b) -> a -> b
$ [Double
intervalId]
  fromJSValUnchecked result
-----------------------------------------------------------------------------
-- | <https://lynxjs.org/api/lynx-api/nodes-ref/nodes-ref-invoke.html>
--
-- Used to call methods on elements in 'view_', 'image_', etc.
-- We use this internally to implement the various 'Method' sections
-- per the lynx docs.
--
-- > invokeExec "gifs" "startAnimate" :: IO ()
--
-- @ 
-- lynx.createSelectorQuery()
--   .select('#gifs')
--   .invoke({
--    method: 'startAnimate',
--  }).exec();
-- @
--
--
-- > invoke
--
invokeExec
  :: (ToJSVal params, FromJSVal argument)
  => MisoString
  -- ^ method
  -> MisoString
  -- ^ selector
  -> params
  -- ^ params
  -> (argument -> action)
  -- ^ successful
  -> (MisoString -> action)
  -- ^ errorful
  -> Effect context props model action
invokeExec :: forall params argument action context props model.
(ToJSVal params, FromJSVal argument) =>
MisoString
-> MisoString
-> params
-> (argument -> action)
-> (MisoString -> action)
-> Effect context props model action
invokeExec MisoString
method MisoString
selector params
params argument -> action
successful MisoString -> action
errorful = do
  (Sink action -> IO ()) -> Effect context props model action
forall action context props model.
(Sink action -> IO ()) -> Effect context props model action
withSink ((Sink action -> IO ()) -> Effect context props model action)
-> (Sink action -> IO ()) -> Effect context props model action
forall a b. (a -> b) -> a -> b
$ \Sink action
sink -> do
    selector_ <- MisoString -> IO JSVal
forall a. ToJSVal a => a -> IO JSVal
toJSVal MisoString
selector
    successful_ <- toJSVal =<< do
      asyncCallback1 $ \JSVal
arg -> do
        result <- JSVal -> IO argument
forall a. FromJSVal a => JSVal -> IO a
fromJSValUnchecked JSVal
arg
        sink (successful result)
    errorful_ <- toJSVal =<< do
      asyncCallback1 $ \JSVal
arg -> do
        rect <- JSVal -> IO MisoString
forall a. FromJSVal a => JSVal -> IO a
fromJSValUnchecked JSVal
arg
        sink (errorful rect)
    params_ <- toJSVal params
    method__ <- toJSVal method
    void $ do
      jsg "globalThis" # "invokeExec" $
        [ selector_
        , method__
        , params_
        , successful_
        , errorful_
        ]
-----------------------------------------------------------------------------