Bot-Man-Toe/elm/Match.elm

256 lines
5.9 KiB
Elm

module Match exposing (..)
{-| A match describes a game's history. It shows what took place.
-}
import Api
import Duration exposing (Duration)
import Element exposing (Element)
import Http
import Json.Decode as D
import Layout
import Pixels exposing (Pixels)
import Quantity exposing (Quantity)
import Theme
import Time
import Zipper exposing (Zipper)
-- MODEL
type Match gameState
= Match
{ autoScroll : Maybe Duration
, baseUrl : String
, decoder : D.Decoder gameState
, empty : gameState
, matchId : String
, turns : Zipper gameState
, winner : Maybe Int
}
type Msg gameState
= AskUpdate
| Autoscroll
| OnUpdate (Result Http.Error (Api.GameDetails gameState))
| PageEnd
| PageNext
| PagePrev
| PageStart
init :
{ autoScroll : Maybe Duration
, baseUrl : String
, decoder : D.Decoder gameState
, empty : gameState
, matchId : String
}
-> ( Match gameState, Cmd (Msg gameState) )
init data =
( Match
{ autoScroll = data.autoScroll
, baseUrl = data.baseUrl
, decoder = data.decoder
, empty = data.empty
, matchId = data.matchId
, turns = Zipper.init data.empty
, winner = Nothing
}
, Cmd.none
)
-- UPDATE
update : Msg gameState -> Match gameState -> ( Match gameState, Cmd (Msg gameState) )
update msg (Match data) =
case msg of
AskUpdate ->
( Match data
, Api.gameDetails
{ baseUrl = data.baseUrl
, decoder = data.decoder
, gameId = data.matchId
, toMsg = OnUpdate
}
)
Autoscroll ->
( Match { data | turns = Zipper.next data.turns }
, Cmd.none
)
OnUpdate (Err _) ->
-- For now, do nothing with failed API requests
( Match data, Cmd.none )
OnUpdate (Ok details) ->
( Match
{ data
| turns =
details.turns
|> List.map .state
|> Zipper.fromList data.empty
|> Zipper.samePageAs data.turns
, winner = details.winner
}
, Cmd.none
)
PageEnd ->
( Match
{ data
| autoScroll = Nothing
, turns = Zipper.toEnd data.turns
}
, Cmd.none
)
PageNext ->
( Match
{ data
| autoScroll = Nothing
, turns = Zipper.next data.turns
}
, Cmd.none
)
PagePrev ->
( Match
{ data
| autoScroll = Nothing
, turns = Zipper.prev data.turns
}
, Cmd.none
)
PageStart ->
( Match
{ data
| autoScroll = Nothing
, turns = Zipper.toStart data.turns
}
, Cmd.none
)
-- SUBSCRIPTIONS
subscriptions : Match gameState -> Sub (Msg gameState)
subscriptions (Match data) =
Sub.batch
[ case data.autoScroll of
Just duration ->
Time.every (Duration.inMilliseconds duration) (always Autoscroll)
Nothing ->
Sub.none
, case data.winner of
Just _ ->
Sub.none
Nothing ->
Time.every 550 (always AskUpdate)
]
-- VIEW
view :
{ flavor : Theme.Flavor
, height : Quantity Int Pixels
, match : Match gameState
, toMsg : Msg gameState -> msg
, width : Quantity Int Pixels
, viewGame :
{ flavor : Theme.Flavor
, game : gameState
, height : Quantity Int Pixels
, width : Quantity Int Pixels
}
-> Element msg
}
-> Element msg
view data =
let
menuHeight =
data.height
|> Quantity.toFloatQuantity
|> Quantity.multiplyBy (1 / 8)
|> Quantity.floor
tinyScreen =
data.height |> Quantity.lessThan (Pixels.pixels 300)
gameHeight =
if tinyScreen then
data.height
|> Quantity.minus menuHeight
else
data.height
in
Element.column
[ Element.height (Element.px (Pixels.inPixels data.height))
, Element.width (Element.px (Pixels.inPixels data.width))
]
[ case data.match of
Match { turns } ->
data.viewGame
{ flavor = data.flavor
, game = Zipper.current turns
, height = gameHeight
, width = data.width
}
, if tinyScreen then
viewMenu
{ height = menuHeight
, width = data.width
}
|> Element.map data.toMsg
else
Element.none
]
viewListItem :
{ flavor : Theme.Flavor
, height : Quantity Int Pixels
, match : Match gameState
, onPress : Maybe msg
, width : Quantity Int Pixels
}
-> Element msg
viewListItem data =
case data.match of
Match match ->
Layout.itemWithSubtext
{ color = Theme.mantle data.flavor
, leftIcon = always Element.none
, onPress = data.onPress
, rightIcon = always Element.none
, text = "Subtext"
, title = match.matchId
}
[]
viewMenu :
{ height : Quantity Int Pixels
, width : Quantity Int Pixels
}
-> Element (Msg gameState)
viewMenu _ =
Element.none