@@ -48,7 +48,7 @@ import Dominator.DOMC
4848import Dominator.JSDOM
4949import GHCJS.Marshal (toJSVal , fromJSVal )
5050import GHCJS.Foreign.Export (Export , export , derefExport )
51- import GHCJS.Foreign.Callback (Callback , syncCallback1 , OnBlocked (ContinueAsync ))
51+ import GHCJS.Foreign.Callback (OnBlocked ( .. ), Callback , syncCallback1 , OnBlocked (ContinueAsync ))
5252import GHCJS.Nullable (Nullable (.. ), nullableToMaybe , maybeToNullable )
5353import GHCJS.Types (JSVal , jsval )
5454import Happstack.Authenticate.Core (ClientInitData (.. ), Email (.. ), User (.. ), Username (.. ), AuthenticateURL (AmAuthenticated , AuthenticationMethods , InitClient , Logout ), AuthenticationMethod (.. ), JSONResponse (.. ), Status (.. ), jsonOptions )
@@ -107,6 +107,7 @@ data AuthenticateModel = AuthenticateModel
107107 , _postLoginRedirectURL :: Maybe Text
108108 , _postSignupRedirectURL :: Maybe Text
109109 , _redraws :: [AuthenticateModel -> IO () ]
110+ , _turnstileToken :: Maybe Text
110111 }
111112makeLenses ''AuthenticateModel
112113
@@ -143,6 +144,7 @@ initAuthenticateModel = AuthenticateModel
143144 , _postLoginRedirectURL = Nothing
144145 , _postSignupRedirectURL = Nothing
145146 , _redraws = []
147+ , _turnstileToken = Nothing
146148 }
147149
148150data SignupPlugin = forall a . SignupPlugin
@@ -188,10 +190,14 @@ dummyPlugin = SignupPlugin
188190signupPasswordForm :: [(Text , SignupPlugin )] -> JSDocument -> IO (JSNode , AuthenticateModel -> IO () )
189191signupPasswordForm sps =
190192 [domc |
193+ <div>
194+ <div id="cf-turnstile-widget" class="cf-turnstile"></div>
195+
191196 <d-if cond="isJust (_muser model)">
192197 <p>
193198 <span>You are currently logged in as </span><span>{{ maybe "Unknown" (Text.unpack . _unUsername . _username) (_muser model) }}</span><span>. To create a new account you must first </span><a data-ha-action="logout" href="#">{{ render LogoutMsg }}</a>
194199 </p>
200+
195201 <form role="form">
196202 <div class="form-group error">{{_signupError model}}</div>
197203 <div class="form-group">
@@ -215,7 +221,9 @@ signupPasswordForm sps =
215221 <input class="form-control" type="submit" value="{{render SignUpMsg}}" />
216222 </div>
217223 </form>
224+
218225 </d-if>
226+ </div>
219227 |]
220228 where
221229 pluginList :: JSDocument -> IO (JSNode , SignupPlugin -> IO () )
@@ -615,11 +623,15 @@ signupHandler :: (AuthenticateURL -> Text) -> [(Text, SignupPlugin)] -> JSElemen
615623signupHandler routeFn sps rootNode inputUsername inputEmail inputPassword inputPasswordConfirm modelTV e =
616624 do preventDefault e
617625 stopPropagation e
626+
618627 musername <- getValue inputUsername
619628 memail <- getValue inputEmail
620629 mpassword <- getValue inputPassword
621630 mpasswordConfirm <- getValue inputPasswordConfirm
622- debugStrLn $ " signupHandler - " ++ show (musername, memail, mpassword, mpasswordConfirm)
631+
632+ token <- atomically $ fmap _turnstileToken (readTVar modelTV )
633+ debugStrLn $ " signupHandler - " ++ show (musername, memail, mpassword, mpasswordConfirm, token)
634+
623635 case (musername, memail, mpassword, mpasswordConfirm) of
624636 (Just username, Just email, Just password, Just passwordConfirm) ->
625637 do let newAccountData =
@@ -629,6 +641,7 @@ signupHandler routeFn sps rootNode inputUsername inputEmail inputPassword inputP
629641 }
630642 , _naPassword = textFromJSString password
631643 , _naPasswordConfirm = textFromJSString passwordConfirm
644+ , _naTurnstileToken = token
632645 }
633646
634647 -- validate plugins
@@ -858,9 +871,22 @@ clearUser routeFn modelTV =
858871 send xhr
859872 doRedraws modelTV
860873
874+ -- foreign import javascript unsafe "turnstile.render($1, { sitekey: $2, callback: function(token) {console.log('turnstile success', token);} })"
875+ foreign import javascript unsafe " turnstile.render($1, { sitekey: $2, callback: $3 })"
876+ js_turnstileRender :: JSString -> JSString -> Callback (JSVal -> IO () ) -> IO JSVal
877+
878+ -- NOTE: instead of selector, render can also take an implementation of HTMLElement
879+ -- we should implement a binding to that version as well
880+ turnstileRender :: Text -> Text -> (JSString -> IO () ) -> IO JSVal
881+ turnstileRender turnstileId turnstileSiteKey onSuccess =
882+ do cb <- syncCallback1 ThrowWouldBlock (\ jsval ->
883+ do (Just token) <- fromJSVal (jsval :: JSVal )
884+ onSuccess token)
885+ js_turnstileRender (textToJSString turnstileId) (textToJSString turnstileSiteKey) cb
886+
861887-- FIXME: what happens if this is called twice?
862- initHappstackAuthenticateClient :: Text -> [(Text , SignupPlugin )] -> IO ()
863- initHappstackAuthenticateClient baseURL sps =
888+ initHappstackAuthenticateClient :: Text -> Maybe Text -> [(Text , SignupPlugin )] -> IO ()
889+ initHappstackAuthenticateClient baseURL mTurnstileKey sps =
864890 do debugStrLn " initHappstackAuthenticateClient"
865891 hSetBuffering stdout LineBuffering
866892 (Just d) <- currentDocument
@@ -955,6 +981,18 @@ initHappstackAuthenticateClient baseURL sps =
955981 let (Just newElem) = fromJSNode @ JSElement newNode
956982 addEventListener newNode (ev @ Submit ) (signupHandler (\ url -> baseURL <> toPathInfo url) sps newElem inputUsername inputEmail inputPassword inputPasswordConfirm modelTV) False
957983 addEventListener newNode (ev @ Click ) (logoutHandler (\ url -> baseURL <> toPathInfo url) update modelTV) False
984+
985+ -- add turnstile widget
986+ let addTurnstileToken :: JSString -> IO ()
987+ addTurnstileToken token =
988+ do debugStrLn " Adding turnstile token"
989+ atomically $ modifyTVar' modelTV $ \ m -> m { _turnstileToken = Just (textFromJSString token) }
990+
991+ case mTurnstileKey of
992+ Nothing -> pure ()
993+ (Just siteKey) ->
994+ do tId <- turnstileRender " #cf-turnstile-widget" siteKey addTurnstileToken
995+ pure ()
958996 pure update
959997-- addEventListener newNode (ev @Click) (logoutHandler (\url -> baseURL <> toPathInfo url) update modelTV) False
960998 -- listen for changes to local storage
@@ -1190,8 +1228,10 @@ clientMain sps =
11901228 (Just script) ->
11911229 do mUrl <- getData (toJSNode script) " baseUrl"
11921230 debugStrLn $ " mUrl = " ++ show mUrl
1231+ mTurnstileKey <- getData (toJSNode script) " turnstileKey"
1232+ debugStrLn $ " turnstileKey = " ++ show mTurnstileKey
11931233 case mUrl of
11941234 Nothing -> debugStrLn " could not find base url"
11951235 (Just url) ->
11961236 do mapM_ (debugStrLn . Text. unpack . fst ) sps
1197- initHappstackAuthenticateClient (textFromJSString url) sps
1237+ initHappstackAuthenticateClient (textFromJSString url) ( fmap textFromJSString mTurnstileKey) sps
0 commit comments