Created
December 22, 2017 13:14
-
-
Save haskellcamargo/aabbadb36c68a84ee57048eac0a5b689 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open ReasonReact; | |
open String; | |
type error = {status: int}; | |
type action = | |
| ChangeEmail(string) | |
| ChangePassword(string) | |
| PressEnterOnEmail | |
| PressEnterOnPassword | |
| Submit | |
| ShowError(error) | |
| HideError | |
| Resize(int) | |
| Ignore; | |
type state = { | |
email: string, | |
password: string, | |
loading: bool, | |
error: option(string), | |
passwordRef: ref(option(Dom.element)), | |
maxWidth: int | |
}; | |
/* Window interop */ | |
type window; | |
[@bs.send] external addEventListener : window => string => ((Dom.event) => unit) => unit = ""; | |
/* Component definition */ | |
let component = ReasonReact.reducerComponent("Page"); | |
/* Styling */ | |
let inputStyle = ReactDOMRe.Style.make(~width="100%", ()); | |
let buttonStyle = ReactDOMRe.Style.make( | |
~width="100%", | |
~maxWidth="500px", | |
~overflow="hidden", ()); | |
/* Reducer and state handling */ | |
let reducer = (action, state) => | |
switch action { | |
| ChangeEmail(email) => ReasonReact.Update({...state, email}) | |
| ChangePassword(password) => ReasonReact.Update({...state, password}) | |
| PressEnterOnEmail => ReasonReact.SideEffects((_) => | |
switch (state.passwordRef^) { | |
| Some(field) => | |
switch (trim(state.email) != "") { | |
| true => ReactDOMRe.domElementToObj(field)##focus() | |
| false => () | |
} | |
| None => () | |
}) | |
| PressEnterOnPassword => ReasonReact.SideEffects((self) => | |
switch (trim(state.password) !== "" && trim(state.email) !== "") { | |
| true => self.reduce((_) => Submit)() | |
| false => () | |
}) | |
| Submit => ReasonReact.UpdateWithSideEffects({...state, loading: true}, (self) => | |
{ | |
Js.log("OK"); | |
Js.Global.setTimeout(self.reduce((_) => ShowError({status:200})), 500) |> ignore | |
}) | |
| ShowError(error) => ReasonReact.UpdateWithSideEffects( | |
{ | |
...state, | |
error: Some(if (error.status >= 400) "Authentication error" else "Network error"), | |
loading: false | |
}, | |
(self) => Js.Global.setTimeout(self.reduce((_) => HideError), 500) |> ignore) | |
| HideError => ReasonReact.Update({...state, error: None}) | |
| Resize(maxWidth) => ReasonReact.Update({...state, maxWidth}) | |
| Ignore => ReasonReact.NoUpdate | |
}; | |
let initialState = () => { | |
email: "", | |
password: "", | |
loading: false, | |
error: None, | |
passwordRef: ref(None), | |
maxWidth: 0 | |
}; | |
/* Handlers */ | |
let handleChangeEmail = (event) => event | |
|> ReactEventRe.Form.target | |
|> ReactDOMRe.domElementToObj | |
|> (target) => ChangeEmail(target##value); | |
let handleChangePassword = (event) => event | |
|> ReactEventRe.Form.target | |
|> ReactDOMRe.domElementToObj | |
|> (target) => ChangePassword(target##value); | |
let handleEmailKeyDown = (event) => | |
if (ReactEventRe.Keyboard.keyCode(event) === 13) PressEnterOnEmail | |
else Ignore; | |
let handlePasswordKeyDown = (event) => | |
if (ReactEventRe.Keyboard.keyCode(event) === 13) PressEnterOnPassword | |
else Ignore; | |
/* References */ | |
let setPasswordRef = (elem, {ReasonReact.state}) => | |
state.passwordRef := Js.Nullable.to_opt(elem); | |
/* Lifecycle */ | |
let didMount = (self) => { | |
let innerWidth: int = [%bs.raw "window.innerWidth"]; | |
let updateWidth = (_event) => self.reduce((_) => { | |
let innerWidth: int = [%bs.raw "window.innerWidth"]; | |
Resize(innerWidth - 20) | |
})(); | |
let window: window = [%bs.raw "window"]; | |
addEventListener(window, "resize", updateWidth); | |
ReasonReact.Update({...self.state, maxWidth: innerWidth - 20}); | |
}; | |
/* Rendering */ | |
let render = ({handle, reduce, state}) => | |
<div> | |
<input | |
style=inputStyle | |
_type="text" | |
value=(state.email) | |
autoFocus=(Js.true_) | |
placeholder="Rung email" | |
onChange=(reduce(handleChangeEmail)) | |
onKeyDown=(reduce(handleEmailKeyDown)) | |
/> | |
<input | |
style=inputStyle | |
ref=(handle(setPasswordRef)) | |
_type="password" | |
value=(state.password) | |
placeholder="Rung password" | |
onChange=(reduce(handleChangePassword)) | |
onKeyDown=(reduce(handlePasswordKeyDown)) | |
/> | |
<div> | |
<button | |
style=buttonStyle | |
disabled=(Js.Boolean.to_js_boolean(state.loading)) | |
className=((switch state.error { | |
| Some(_) => "mod-danger" | |
| None => "mod-primary" | |
}) ++ " mod-bottom") | |
onClick=(reduce((_) => Submit))> | |
(ReasonReact.stringToElement( | |
if (state.loading) "Making the magic happen..." | |
else switch state.error { | |
| Some(message) => message | |
| None => "Link my Rung account" | |
})) | |
</button> | |
<div> | |
(ReasonReact.stringToElement("Don't you have an account? ")) | |
<a href="#">(ReasonReact.stringToElement("Sign up!"))</a> | |
</div> | |
</div> | |
</div>; | |
let make = (_children) => { | |
...component, | |
initialState, | |
reducer, | |
didMount, | |
render | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment