Skip to content

Instantly share code, notes, and snippets.

@jvoigtlaender
Forked from chrisbuttery/Main.elm
Created November 26, 2015 12:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jvoigtlaender/34c686bdaef4258e6bf4 to your computer and use it in GitHub Desktop.
Save jvoigtlaender/34c686bdaef4258e6bf4 to your computer and use it in GitHub Desktop.
module Main where
import Signal
import Time exposing (Time, millisecond)
import String
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Effects exposing (Never, Effects)
import Task exposing (..)
import StartApp
import Debug exposing (..)
import Http
import Json.Decode as Json exposing ((:=))
-- MODEL
{--
Model type:
Has the shape of a record
Has a query prop that is of sting type
Has a results prop that 'Maybe' is a string or Nothing
has a debounceState that is of type 'DebounceState'
--}
type alias Repo =
{ name: String
, url: String
}
type alias Model = {
query : String
, results : Maybe (List Repo)
, debounceState : DebounceState
}
{--
DebounceState type:
Will 'Maybe' have the shape of a record or Nothing
Has a query prop that is of sting type
Has a prevClockTime prop that is of type Time
Has a elapsedTime prop that is of type Time
--}
type alias DebounceState = Maybe {
prevClockTime : Time
, elapsedTime: Time
}
{--
Model (record)
Sets a query prop to an empty string
Sets results as Nothing, as this value 'maybe' a string or nothing
Sets debounceState to Nothing, as this type is a 'Maybe'
--}
model : (Model, Effects Action)
model =
({
query = ""
, results = Nothing
, debounceState = Nothing
}
, Effects.none )
{--
Action = Union Type
It can either be:
Input: which is a String
UpdateResults: which 'might' be a String
--}
type Action = Input String
| UpdateResults (Maybe (List Repo))
| Tick Time
{--
debounceTime = half a second
--}
debounceTime : Time
debounceTime = 500 * millisecond
{--
elapsedTimeOf
takes debounceState and a clockTime
if debounceState Nothing, return 0
otherwise get 'elapsedTime' and 'prevClockTime'
and return 'elapsedTime' plus 'clockTime' minus 'prevClockTime'
--}
elapsedTimeOf : DebounceState -> Time -> Time
elapsedTimeOf debounceState clockTime =
case debounceState of
Nothing -> 0
Just { elapsedTime, prevClockTime } -> elapsedTime + (clockTime - prevClockTime)
-- UPDATE
{--
Update:
Requires an address and model
If the address was:
Input - Update the query with the accompaning string
UpdateResults - update results with a possible value
--}
update : Action -> Model -> (Model, Effects Action)
update action model =
case action of
{--
on 'Input'
- check the model's debounceState record
- if it's Nothing, set the query on the model an trigger 'Tick'
- otherwise set the query and set 'debounceState' to Nothing and
don't trigger anything
--}
Input query ->
case model.debounceState of
Nothing ->
({ model | query = query }, Effects.tick Tick)
Just oldDebounceState ->
({ model | query = query, debounceState = Nothing }, Effects.none)
{--
on 'UpdateResults'
set the results on the model, don't trigger anything
--}
UpdateResults results ->
({ model | results = results }, Effects.none)
{--
on 'Tick'
first calculate the new newElapsedTime since debounceState was Nothing
if the 'newElapsedTime' in greather than 'debounceTime' (500ms)
- set debounceState to Nothing on the model and perform HTTP request with the query
otherwise set the new debounceState on the model and trigger another Tick
--}
Tick clockTime ->
let
newElapsedTime = elapsedTimeOf model.debounceState clockTime
in
if newElapsedTime > debounceTime then
({model | debounceState = Nothing}, search model.query)
else
({ model | debounceState = Just { elapsedTime = newElapsedTime, prevClockTime = clockTime } }
, Effects.tick Tick)
{--
search:
Look up the github Url
- decode repos ?
- map the results
- I assume wrap it up as a 'Effects.task' ?
--}
search : String -> Effects Action
search query =
Http.get repos ("https://api.github.com/users/" ++ query ++ "/repos")
|> Task.toMaybe
|> Task.map UpdateResults
|> Effects.task
{--
repos
the result of the HTTP request in an array or objects
[
{"id": "foo", "name": "bar", "url": "baz", ... },
{"id": "foo", "name": "bar", "url": "baz", ... }
]
Get the name and url values
--}
repos : Json.Decoder (List Repo)
repos =
let repo =
Json.object2 Repo
("name" := Json.string)
("url" := Json.string)
in
Json.list repo
-- VIEW
{--
return a li for an item
--}
result item =
li [] [ text item.name ]
{--
view takes a Signal Address action and a model
on input we explicity tell it to send the targetValue to the address action Input
targetValue becomes the 'query' param in our update function
if model.results exists, it should render a LI for each repo
--}
view: Signal.Address Action -> Model -> Html
view address model =
div [] [
input [
placeholder model.query
, value model.query
, on "input" targetValue (Signal.message (Signal.forwardTo address Input))
] []
, ul [] (List.map result (Maybe.withDefault [] model.results))
]
-- START
app =
StartApp.start {
init = model
, view = view
, update = update
, inputs = []
}
main = app.html
port tasks : Signal (Task.Task Never ())
port tasks = app.tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment