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 Element.Background 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 data = Element.none