Last active
January 18, 2020 01:20
-
-
Save n1215/85e5d4a36a6bf5e6c21344dd63c570a2 to your computer and use it in GitHub Desktop.
timer event sourcing elm
This file contains 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
module Main exposing (main) | |
import Browser | |
import Browser.Events exposing (onAnimationFrame) | |
import Html exposing (Html, button, div, p, table, tbody, td, text, th, thead, tr) | |
import Html.Events exposing (onClick) | |
import List | |
import Task | |
import Time | |
import Tuple | |
main = | |
Browser.element { init = init, update = update, view = view, subscriptions = subscriptions } | |
type alias ElapsedTimeMillis = | |
Int | |
type EventType | |
= Started | |
| Stopped | |
eventTypeToString : EventType -> String | |
eventTypeToString eventType = | |
case eventType of | |
Started -> | |
"started" | |
Stopped -> | |
"stopped" | |
type alias Event = | |
{ eventType : EventType | |
, occurredAt : Time.Posix | |
} | |
type alias Model = | |
{ events : List Event | |
, now : Time.Posix | |
} | |
init : () -> ( Model, Cmd Msg ) | |
init _ = | |
( { events = [], now = Time.millisToPosix 0 }, Task.perform Tick Time.now ) | |
isStopped : Model -> Bool | |
isStopped model = | |
let | |
isEven : Int -> Bool | |
isEven int = | |
modBy 2 int == 0 | |
in | |
isEven (List.length model.events) | |
getElapsedTime : Model -> ElapsedTimeMillis | |
getElapsedTime model = | |
let | |
chunk : Int -> List a -> List (List a) | |
chunk i list = | |
case List.take i list of | |
[] -> | |
[] | |
listHead -> | |
listHead :: chunk i (List.drop i list) | |
calcTimeForSpan : Time.Posix -> List Event -> ElapsedTimeMillis | |
calcTimeForSpan now span = | |
case span of | |
[ startedEvent, stoppedEvent ] -> | |
Time.posixToMillis stoppedEvent.occurredAt - Time.posixToMillis startedEvent.occurredAt | |
[ startedEvent ] -> | |
Time.posixToMillis now - Time.posixToMillis startedEvent.occurredAt | |
_ -> | |
0 | |
in | |
chunk 2 model.events | |
|> List.map (calcTimeForSpan model.now) | |
|> List.sum | |
type Msg | |
= Tick Time.Posix | |
| StartStop Time.Posix | |
| Click | |
update : Msg -> Model -> ( Model, Cmd Msg ) | |
update msg model = | |
case msg of | |
Tick currentTime -> | |
( { model | now = currentTime }, Cmd.none ) | |
StartStop occurredAt -> | |
let | |
getEventType = | |
if isStopped model then | |
Started | |
else | |
Stopped | |
in | |
( { model | |
| events = List.append model.events [ { eventType = getEventType, occurredAt = occurredAt } ] | |
, now = occurredAt | |
} | |
, Cmd.none | |
) | |
Click -> | |
( model, Task.perform StartStop Time.now ) | |
eventTableRow : ( Int, Event ) -> Html Msg | |
eventTableRow ( index, event ) = | |
tr [] | |
[ td [] [ text <| String.fromInt (index + 1) ] | |
, td [] [ text <| eventTypeToString event.eventType ] | |
, td [] [ text <| String.fromInt <| Time.posixToMillis event.occurredAt ] | |
] | |
view : Model -> Html Msg | |
view model = | |
div [] | |
[ p [] [ text <| "timer (sec): " ++ (String.fromInt <| getElapsedTime model // 1000) ] | |
, p [] [ text <| "timer (ms): " ++ String.fromInt (getElapsedTime model) ] | |
, button [ onClick Click ] | |
[ text <| | |
if isStopped model then | |
"start" | |
else | |
"stop" | |
] | |
, table [] | |
[ thead [] | |
[ tr [] | |
[ th [] [ text <| "id" ] | |
, th [] [ text <| "event" ] | |
, th [] [ text <| "occurred at" ] | |
] | |
] | |
, tbody [] (List.map eventTableRow (List.indexedMap Tuple.pair model.events)) | |
] | |
] | |
subscriptions : Model -> Sub Msg | |
subscriptions _ = | |
onAnimationFrame Tick |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment