Skip to content

Instantly share code, notes, and snippets.

@haskellcamargo
Created December 22, 2017 13:14
Show Gist options
  • Save haskellcamargo/aabbadb36c68a84ee57048eac0a5b689 to your computer and use it in GitHub Desktop.
Save haskellcamargo/aabbadb36c68a84ee57048eac0a5b689 to your computer and use it in GitHub Desktop.
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