Compare commits
No commits in common. "e2a11ae0d1663b11882fdf08de243b919bd070d4" and "bf8e33ee1cfe9a83b17cc3bf09cbe769da7db3f4" have entirely different histories.
e2a11ae0d1
...
bf8e33ee1c
107
elm/Main.elm
107
elm/Main.elm
|
|
@ -1,23 +1,22 @@
|
||||||
module Main exposing (main)
|
module Main exposing (main)
|
||||||
|
|
||||||
|
import Api
|
||||||
|
import Browser
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
|
import Element.Background
|
||||||
import GameList exposing (Game, GameList)
|
import GameList exposing (Game, GameList)
|
||||||
import Json.Decode as D
|
import Http
|
||||||
import Layout
|
import ScreenSize exposing (ScreenSize)
|
||||||
import Material.Icons as Icons
|
import Theme
|
||||||
import Program
|
|
||||||
import Widget.Icon
|
|
||||||
|
|
||||||
|
|
||||||
|
main : Program () Model Msg
|
||||||
main =
|
main =
|
||||||
Program.document
|
Browser.document
|
||||||
{ flagsDecoder = D.string
|
{ init = init
|
||||||
, headers = headers
|
|
||||||
, init = init
|
|
||||||
, subscriptions = subscriptions
|
|
||||||
, title = always "Coolio!" -- TODO
|
|
||||||
, update = update
|
|
||||||
, view = view
|
, view = view
|
||||||
|
, update = update
|
||||||
|
, subscriptions = subscriptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -27,8 +26,10 @@ main =
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ baseUrl : String
|
{ baseUrl : String
|
||||||
|
, flavor : Theme.Flavor
|
||||||
, games : GameList
|
, games : GameList
|
||||||
, screen : Screen
|
, screen : Screen
|
||||||
|
, size : ScreenSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -41,25 +42,25 @@ type Screen
|
||||||
type Msg
|
type Msg
|
||||||
= OnGameList GameList.Msg
|
= OnGameList GameList.Msg
|
||||||
| OnScreen Screen
|
| OnScreen Screen
|
||||||
|
| OnScreenSize ScreenSize
|
||||||
|
|
||||||
|
|
||||||
init : Result D.Error String -> ( Model, Cmd Msg )
|
init : () -> ( Model, Cmd Msg )
|
||||||
init baseUrl =
|
init () =
|
||||||
let
|
let
|
||||||
( gmdl, gmsg ) =
|
( gmdl, gmsg ) =
|
||||||
GameList.init {}
|
GameList.init {}
|
||||||
in
|
in
|
||||||
( { baseUrl =
|
( { baseUrl = "http://localhost:5000"
|
||||||
case baseUrl of
|
, flavor = Theme.Latte
|
||||||
Ok s ->
|
|
||||||
s
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
"http://localhost:5000"
|
|
||||||
, games = gmdl
|
, games = gmdl
|
||||||
, screen = ViewGameSelectionMenu
|
, screen = ViewGameSelectionMenu
|
||||||
|
, size = ScreenSize.init
|
||||||
}
|
}
|
||||||
, Cmd.map OnGameList gmsg
|
, Cmd.batch
|
||||||
|
[ ScreenSize.updateScreenSize OnScreenSize
|
||||||
|
, Cmd.map OnGameList gmsg
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -78,6 +79,9 @@ update msg model =
|
||||||
OnScreen screen ->
|
OnScreen screen ->
|
||||||
( { model | screen = screen }, Cmd.none )
|
( { model | screen = screen }, Cmd.none )
|
||||||
|
|
||||||
|
OnScreenSize size ->
|
||||||
|
( { model | size = size }, Cmd.none )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
-- SUBSCRIPTIONS
|
||||||
|
|
@ -85,50 +89,59 @@ update msg model =
|
||||||
|
|
||||||
subscriptions : Model -> Sub Msg
|
subscriptions : Model -> Sub Msg
|
||||||
subscriptions model =
|
subscriptions model =
|
||||||
Sub.map OnGameList (GameList.subscriptions model.games)
|
Sub.batch
|
||||||
|
[ ScreenSize.onResize OnScreenSize
|
||||||
|
, Sub.map OnGameList (GameList.subscriptions model.games)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- VIEW
|
-- VIEW
|
||||||
|
|
||||||
|
|
||||||
headers : Model -> List { icon : Widget.Icon.Icon Msg, onPress : Msg }
|
view : Model -> Browser.Document Msg
|
||||||
headers model =
|
view model =
|
||||||
|
{ title =
|
||||||
|
case model.screen of
|
||||||
|
ViewCreateGame ->
|
||||||
|
"Create Game | Bot-Man-Toe"
|
||||||
|
|
||||||
|
ViewGameSelectionMenu ->
|
||||||
|
"Menu | Bot-Man-Toe"
|
||||||
|
|
||||||
|
ViewGame _ ->
|
||||||
|
"Replay | Bot-Man-Toe"
|
||||||
|
, body =
|
||||||
|
viewScreen model
|
||||||
|
|> Element.layout
|
||||||
|
[ Element.Background.color (Theme.baseUI model.flavor)
|
||||||
|
]
|
||||||
|
|> List.singleton
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
viewScreen : Model -> Element Msg
|
||||||
|
viewScreen model =
|
||||||
case model.screen of
|
case model.screen of
|
||||||
ViewCreateGame ->
|
|
||||||
[ { icon = Layout.iconAsIcon Icons.arrow_back, onPress = OnScreen ViewGameSelectionMenu }
|
|
||||||
]
|
|
||||||
|
|
||||||
ViewGameSelectionMenu ->
|
|
||||||
[]
|
|
||||||
|
|
||||||
ViewGame _ ->
|
|
||||||
[ { icon = Layout.iconAsIcon Icons.arrow_back, onPress = OnScreen ViewGameSelectionMenu }
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
view : Program.ViewBox Model -> Element Msg
|
|
||||||
view data =
|
|
||||||
case data.model.screen of
|
|
||||||
ViewCreateGame ->
|
ViewCreateGame ->
|
||||||
Element.text "Create game menu!"
|
Element.text "Create game menu!"
|
||||||
|
|
||||||
ViewGameSelectionMenu ->
|
ViewGameSelectionMenu ->
|
||||||
GameList.viewSelection
|
GameList.viewSelection
|
||||||
{ flavor = data.flavor
|
{ flavor = model.flavor
|
||||||
, height = data.size.height
|
, height = model.size.height
|
||||||
, model = data.model.games
|
, model = model.games
|
||||||
, onCreateGame = OnScreen ViewCreateGame
|
, onCreateGame = OnScreen ViewCreateGame
|
||||||
, onNavigateToGame = OnScreen << ViewGame
|
, onNavigateToGame = OnScreen << ViewGame
|
||||||
, width = data.size.width
|
, width = model.size.width
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewGame game ->
|
ViewGame game ->
|
||||||
GameList.viewGame
|
GameList.viewGame
|
||||||
{ flavor = data.flavor
|
{ flavor = model.flavor
|
||||||
, game = game
|
, game = game
|
||||||
, height = data.size.height
|
, height = model.size.height
|
||||||
, onNavigateBack = OnScreen ViewGameSelectionMenu
|
, onNavigateBack = OnScreen ViewGameSelectionMenu
|
||||||
, toMsg = OnGameList
|
, toMsg = OnGameList
|
||||||
, width = data.size.width
|
, width = model.size.width
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ module Match exposing (..)
|
||||||
import Api
|
import Api
|
||||||
import Duration exposing (Duration)
|
import Duration exposing (Duration)
|
||||||
import Element exposing (Element)
|
import Element exposing (Element)
|
||||||
|
import Element.Background
|
||||||
import Http
|
import Http
|
||||||
import Json.Decode as D
|
import Json.Decode as D
|
||||||
import Layout
|
import Layout
|
||||||
|
|
@ -251,5 +252,5 @@ viewMenu :
|
||||||
, width : Quantity Int Pixels
|
, width : Quantity Int Pixels
|
||||||
}
|
}
|
||||||
-> Element (Msg gameState)
|
-> Element (Msg gameState)
|
||||||
viewMenu _ =
|
viewMenu data =
|
||||||
Element.none
|
Element.none
|
||||||
|
|
|
||||||
300
elm/Program.elm
300
elm/Program.elm
|
|
@ -1,300 +0,0 @@
|
||||||
module Program exposing (Px, ViewBox, document, element)
|
|
||||||
|
|
||||||
import Browser
|
|
||||||
import Color
|
|
||||||
import Element exposing (Element)
|
|
||||||
import Element.Background
|
|
||||||
import Element.Events
|
|
||||||
import Element.Font
|
|
||||||
import Html
|
|
||||||
import Json.Decode as D
|
|
||||||
import Layout
|
|
||||||
import Pixels exposing (Pixels)
|
|
||||||
import Quantity exposing (Quantity)
|
|
||||||
import ScreenSize exposing (ScreenSize)
|
|
||||||
import Svg
|
|
||||||
import Svg.Attributes
|
|
||||||
import Theme
|
|
||||||
import Widget.Icon
|
|
||||||
|
|
||||||
|
|
||||||
type alias Model model =
|
|
||||||
{ content : model
|
|
||||||
, flavor : Theme.Flavor
|
|
||||||
, size : ScreenSize
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type Msg msg
|
|
||||||
= OnContent msg
|
|
||||||
| OnFlavor Theme.Flavor
|
|
||||||
| OnScreenSize ScreenSize
|
|
||||||
|
|
||||||
|
|
||||||
type alias Px =
|
|
||||||
Quantity Int Pixels
|
|
||||||
|
|
||||||
|
|
||||||
type alias ViewBox model =
|
|
||||||
{ flavor : Theme.Flavor, model : model, size : ScreenSize }
|
|
||||||
|
|
||||||
|
|
||||||
element :
|
|
||||||
{ flagsDecoder : D.Decoder flags
|
|
||||||
, init : Result D.Error flags -> ( model, Cmd msg )
|
|
||||||
, subscriptions : model -> Sub msg
|
|
||||||
, update : msg -> model -> ( model, Cmd msg )
|
|
||||||
, view : ViewBox model -> Element msg
|
|
||||||
}
|
|
||||||
-> Program D.Value (Model model) (Msg msg)
|
|
||||||
element data =
|
|
||||||
Browser.element
|
|
||||||
{ init = init { f = data.init, d = data.flagsDecoder }
|
|
||||||
, subscriptions = subscriptions data.subscriptions
|
|
||||||
, update = update data.update
|
|
||||||
, view =
|
|
||||||
view
|
|
||||||
{ body = data.view
|
|
||||||
, headers = always []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
document :
|
|
||||||
{ flagsDecoder : D.Decoder flags
|
|
||||||
, headers : model -> List { icon : Widget.Icon.Icon msg, onPress : msg }
|
|
||||||
, init : Result D.Error flags -> ( model, Cmd msg )
|
|
||||||
, subscriptions : model -> Sub msg
|
|
||||||
, title : model -> String
|
|
||||||
, update : msg -> model -> ( model, Cmd msg )
|
|
||||||
, view : ViewBox model -> Element msg
|
|
||||||
}
|
|
||||||
-> Program D.Value (Model model) (Msg msg)
|
|
||||||
document data =
|
|
||||||
Browser.document
|
|
||||||
{ init = init { f = data.init, d = data.flagsDecoder }
|
|
||||||
, subscriptions = subscriptions data.subscriptions
|
|
||||||
, update = update data.update
|
|
||||||
, view =
|
|
||||||
\model ->
|
|
||||||
{ title = data.title model.content
|
|
||||||
, body = [ view { body = data.view, headers = data.headers } model ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- INIT
|
|
||||||
|
|
||||||
|
|
||||||
init :
|
|
||||||
{ f : Result D.Error flags -> ( model, Cmd msg )
|
|
||||||
, d : D.Decoder flags
|
|
||||||
}
|
|
||||||
-> D.Value
|
|
||||||
-> ( Model model, Cmd (Msg msg) )
|
|
||||||
init data blob =
|
|
||||||
case data.f (D.decodeValue data.d blob) of
|
|
||||||
( mdl, msg ) ->
|
|
||||||
( { content = mdl
|
|
||||||
, flavor = Theme.Frappe
|
|
||||||
, size = ScreenSize.init
|
|
||||||
}
|
|
||||||
, Cmd.batch
|
|
||||||
[ Cmd.map OnContent msg
|
|
||||||
, ScreenSize.updateScreenSize OnScreenSize
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- UPDATE
|
|
||||||
|
|
||||||
|
|
||||||
update :
|
|
||||||
(msg -> model -> ( model, Cmd msg ))
|
|
||||||
-> Msg msg
|
|
||||||
-> Model model
|
|
||||||
-> ( Model model, Cmd (Msg msg) )
|
|
||||||
update f msg model =
|
|
||||||
case msg of
|
|
||||||
OnContent m ->
|
|
||||||
case f m model.content of
|
|
||||||
( newMdl, newMsg ) ->
|
|
||||||
( { model | content = newMdl }
|
|
||||||
, Cmd.map OnContent newMsg
|
|
||||||
)
|
|
||||||
|
|
||||||
OnFlavor flavor ->
|
|
||||||
( { model | flavor = flavor }, Cmd.none )
|
|
||||||
|
|
||||||
OnScreenSize size ->
|
|
||||||
( { model | size = size }, Cmd.none )
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
|
||||||
|
|
||||||
|
|
||||||
subscriptions : (model -> Sub msg) -> Model model -> Sub (Msg msg)
|
|
||||||
subscriptions f model =
|
|
||||||
Sub.batch
|
|
||||||
[ Sub.map OnContent <| f model.content
|
|
||||||
, ScreenSize.onResize OnScreenSize
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- VIEW
|
|
||||||
|
|
||||||
|
|
||||||
view :
|
|
||||||
{ body : ViewBox model -> Element msg
|
|
||||||
, headers : model -> List { icon : Widget.Icon.Icon msg, onPress : msg }
|
|
||||||
}
|
|
||||||
-> Model model
|
|
||||||
-> Html.Html (Msg msg)
|
|
||||||
view data model =
|
|
||||||
let
|
|
||||||
preferredNavBarHeight =
|
|
||||||
Pixels.pixels 40
|
|
||||||
|
|
||||||
showNavBar =
|
|
||||||
preferredNavBarHeight
|
|
||||||
|> Quantity.multiplyBy 6
|
|
||||||
|> Quantity.lessThanOrEqualTo model.size.height
|
|
||||||
|
|
||||||
contentHeight =
|
|
||||||
if showNavBar then
|
|
||||||
model.size.height |> Quantity.minus preferredNavBarHeight
|
|
||||||
|
|
||||||
else
|
|
||||||
model.size.height
|
|
||||||
in
|
|
||||||
[ viewNavBar
|
|
||||||
{ headers = data.headers model.content
|
|
||||||
, iconHeight = preferredNavBarHeight
|
|
||||||
, model = model
|
|
||||||
}
|
|
||||||
, data.body
|
|
||||||
{ flavor = model.flavor
|
|
||||||
, model = model.content
|
|
||||||
, size = { height = contentHeight, width = model.size.width }
|
|
||||||
}
|
|
||||||
|> Element.map OnContent
|
|
||||||
]
|
|
||||||
|> Element.column [ Element.width Element.fill ]
|
|
||||||
|> Element.layout
|
|
||||||
[ Element.Background.color (Theme.baseUI model.flavor)
|
|
||||||
, Element.Font.color (Theme.textUI model.flavor)
|
|
||||||
, Element.width <| Element.px <| Pixels.inPixels model.size.width
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
viewFlavorPicker :
|
|
||||||
{ currentFlavor : Theme.Flavor
|
|
||||||
, flavorToPick : Theme.Flavor
|
|
||||||
, onClick : Theme.Flavor -> msg
|
|
||||||
, size : Px
|
|
||||||
}
|
|
||||||
-> Element msg
|
|
||||||
viewFlavorPicker data =
|
|
||||||
Layout.svg
|
|
||||||
{ aspectRatio = 1 / 1
|
|
||||||
, height = Pixels.inPixels data.size
|
|
||||||
, svg =
|
|
||||||
Svg.circle
|
|
||||||
[ Svg.Attributes.cx "5"
|
|
||||||
, Svg.Attributes.cy "5"
|
|
||||||
, Svg.Attributes.r "4"
|
|
||||||
, Svg.Attributes.strokeWidth "1"
|
|
||||||
, Svg.Attributes.fill (Color.toCssString <| Theme.base data.flavorToPick)
|
|
||||||
, Svg.Attributes.stroke (Color.toCssString <| Theme.crust data.currentFlavor)
|
|
||||||
]
|
|
||||||
[]
|
|
||||||
, viewMinY = 0
|
|
||||||
, viewMaxY = 10
|
|
||||||
, viewMinX = 0
|
|
||||||
, viewMaxX = 10
|
|
||||||
, width = Pixels.inPixels data.size
|
|
||||||
}
|
|
||||||
|> (if data.currentFlavor /= data.flavorToPick then
|
|
||||||
Element.el [ Element.Events.onClick (data.onClick data.flavorToPick) ]
|
|
||||||
|
|
||||||
else
|
|
||||||
identity
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
viewNavBar :
|
|
||||||
{ headers : List { icon : Widget.Icon.Icon msg, onPress : msg }
|
|
||||||
, iconHeight : Px
|
|
||||||
, model : Model model
|
|
||||||
}
|
|
||||||
-> Element (Msg msg)
|
|
||||||
viewNavBar data =
|
|
||||||
let
|
|
||||||
heightAttr =
|
|
||||||
Quantity.twice data.iconHeight
|
|
||||||
|> Pixels.inPixels
|
|
||||||
|> Element.px
|
|
||||||
|> Element.height
|
|
||||||
|
|
||||||
widthAttr =
|
|
||||||
Quantity.twice data.iconHeight
|
|
||||||
|> Pixels.inPixels
|
|
||||||
|> Element.px
|
|
||||||
|> Element.width
|
|
||||||
in
|
|
||||||
Element.row
|
|
||||||
[ Element.Background.color <| Theme.mantleUI data.model.flavor
|
|
||||||
, Element.width Element.fill
|
|
||||||
]
|
|
||||||
[ data.headers
|
|
||||||
|> List.map
|
|
||||||
(viewNavBarIcon
|
|
||||||
{ flavor = data.model.flavor
|
|
||||||
, height = Quantity.twice data.iconHeight
|
|
||||||
, heightIcon = data.iconHeight
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> Element.row []
|
|
||||||
, Element.el [ heightAttr, Element.width Element.fill ] Element.none
|
|
||||||
, [ Theme.Latte, Theme.Frappe, Theme.Macchiato, Theme.Mocha ]
|
|
||||||
|> List.map
|
|
||||||
(\flavor ->
|
|
||||||
viewFlavorPicker
|
|
||||||
{ currentFlavor = data.model.flavor
|
|
||||||
, flavorToPick = flavor
|
|
||||||
, onClick = OnFlavor
|
|
||||||
, size = data.iconHeight
|
|
||||||
}
|
|
||||||
|> Element.el [ Element.centerX, Element.centerY ]
|
|
||||||
|> Element.el [ heightAttr, widthAttr ]
|
|
||||||
)
|
|
||||||
|> Element.row []
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
viewNavBarIcon :
|
|
||||||
{ flavor : Theme.Flavor
|
|
||||||
, height : Px
|
|
||||||
, heightIcon : Px
|
|
||||||
}
|
|
||||||
-> { icon : Widget.Icon.Icon msg, onPress : msg }
|
|
||||||
-> Element (Msg msg)
|
|
||||||
viewNavBarIcon { flavor, height, heightIcon } { icon, onPress } =
|
|
||||||
-- TODO: Implement coloring for hover + onclick
|
|
||||||
icon { color = Theme.text flavor, size = Pixels.inPixels heightIcon }
|
|
||||||
|> Element.el
|
|
||||||
[ Element.centerX
|
|
||||||
, Element.centerY
|
|
||||||
, Element.height <| Element.px <| Pixels.inPixels heightIcon
|
|
||||||
, Element.width <| Element.px <| Pixels.inPixels heightIcon
|
|
||||||
]
|
|
||||||
|> Element.el
|
|
||||||
[ Element.Events.onClick onPress
|
|
||||||
, Element.height <| Element.px <| Pixels.inPixels height
|
|
||||||
, Element.width <| Element.px <| Pixels.inPixels height
|
|
||||||
]
|
|
||||||
|> Element.map OnContent
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
module Screen.CreateGame exposing (..)
|
|
||||||
|
|
||||||
-- MODEL
|
|
||||||
|
|
||||||
type alias Model =
|
|
||||||
{ baseUrl : String
|
|
||||||
, players : List String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Msg
|
|
||||||
= OnBaseUrl String
|
|
||||||
| OnPlayer Int String
|
|
||||||
| RemovePlayer Int
|
|
||||||
|
|
||||||
-- UPDATE
|
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
|
||||||
update msg model =
|
|
||||||
case msg of
|
|
||||||
OnBaseUrl url ->
|
|
||||||
( { model | baseUrl = url }, Cmd.none )
|
|
||||||
|
|
||||||
OnPlayer n p ->
|
|
||||||
let
|
|
||||||
newIndex = List.length model.players == n
|
|
||||||
|
|
||||||
newPlayers =
|
|
||||||
if newIndex && mayCreateNewPlayer model.players then
|
|
||||||
List.append model.players [ p ]
|
|
||||||
else
|
|
||||||
List.indexedMap
|
|
||||||
(\i player ->
|
|
||||||
if n == i then
|
|
||||||
p
|
|
||||||
else
|
|
||||||
player
|
|
||||||
)
|
|
||||||
model.players
|
|
||||||
|
|
||||||
in
|
|
||||||
( { model | players = newPlayers }, Cmd.none )
|
|
||||||
|
|
||||||
RemovePlayer n ->
|
|
||||||
( { model
|
|
||||||
| players =
|
|
||||||
model.players
|
|
||||||
|> List.indexedMap
|
|
||||||
(\i player ->
|
|
||||||
if n == i then
|
|
||||||
Nothing
|
|
||||||
else
|
|
||||||
Just player
|
|
||||||
)
|
|
||||||
|> List.filterMap identity
|
|
||||||
}
|
|
||||||
, Cmd.none
|
|
||||||
)
|
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
|
||||||
|
|
||||||
-- VIEW
|
|
||||||
|
|
||||||
mayCreateNewPlayer : List String -> Bool
|
|
||||||
mayCreateNewPlayer =
|
|
||||||
List.all (not << String.isEmpty)
|
|
||||||
Loading…
Reference in New Issue