256 lines
5.9 KiB
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
|