Compare commits
No commits in common. "e2a11ae0d1663b11882fdf08de243b919bd070d4" and "bf8e33ee1cfe9a83b17cc3bf09cbe769da7db3f4" have entirely different histories.
e2a11ae0d1
...
bf8e33ee1c
89
elm/Main.elm
89
elm/Main.elm
|
|
@ -1,23 +1,22 @@
|
|||
module Main exposing (main)
|
||||
|
||||
import Api
|
||||
import Browser
|
||||
import Element exposing (Element)
|
||||
import Element.Background
|
||||
import GameList exposing (Game, GameList)
|
||||
import Json.Decode as D
|
||||
import Layout
|
||||
import Material.Icons as Icons
|
||||
import Program
|
||||
import Widget.Icon
|
||||
import Http
|
||||
import ScreenSize exposing (ScreenSize)
|
||||
import Theme
|
||||
|
||||
|
||||
main : Program () Model Msg
|
||||
main =
|
||||
Program.document
|
||||
{ flagsDecoder = D.string
|
||||
, headers = headers
|
||||
, init = init
|
||||
, subscriptions = subscriptions
|
||||
, title = always "Coolio!" -- TODO
|
||||
, update = update
|
||||
Browser.document
|
||||
{ init = init
|
||||
, view = view
|
||||
, update = update
|
||||
, subscriptions = subscriptions
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -27,8 +26,10 @@ main =
|
|||
|
||||
type alias Model =
|
||||
{ baseUrl : String
|
||||
, flavor : Theme.Flavor
|
||||
, games : GameList
|
||||
, screen : Screen
|
||||
, size : ScreenSize
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -41,25 +42,25 @@ type Screen
|
|||
type Msg
|
||||
= OnGameList GameList.Msg
|
||||
| OnScreen Screen
|
||||
| OnScreenSize ScreenSize
|
||||
|
||||
|
||||
init : Result D.Error String -> ( Model, Cmd Msg )
|
||||
init baseUrl =
|
||||
init : () -> ( Model, Cmd Msg )
|
||||
init () =
|
||||
let
|
||||
( gmdl, gmsg ) =
|
||||
GameList.init {}
|
||||
in
|
||||
( { baseUrl =
|
||||
case baseUrl of
|
||||
Ok s ->
|
||||
s
|
||||
|
||||
Err _ ->
|
||||
"http://localhost:5000"
|
||||
( { baseUrl = "http://localhost:5000"
|
||||
, flavor = Theme.Latte
|
||||
, games = gmdl
|
||||
, screen = ViewGameSelectionMenu
|
||||
, size = ScreenSize.init
|
||||
}
|
||||
, Cmd.batch
|
||||
[ ScreenSize.updateScreenSize OnScreenSize
|
||||
, Cmd.map OnGameList gmsg
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -78,6 +79,9 @@ update msg model =
|
|||
OnScreen screen ->
|
||||
( { model | screen = screen }, Cmd.none )
|
||||
|
||||
OnScreenSize size ->
|
||||
( { model | size = size }, Cmd.none )
|
||||
|
||||
|
||||
|
||||
-- SUBSCRIPTIONS
|
||||
|
|
@ -85,50 +89,59 @@ update msg model =
|
|||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
Sub.map OnGameList (GameList.subscriptions model.games)
|
||||
Sub.batch
|
||||
[ ScreenSize.onResize OnScreenSize
|
||||
, Sub.map OnGameList (GameList.subscriptions model.games)
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
headers : Model -> List { icon : Widget.Icon.Icon Msg, onPress : Msg }
|
||||
headers model =
|
||||
view : Model -> Browser.Document Msg
|
||||
view model =
|
||||
{ title =
|
||||
case model.screen of
|
||||
ViewCreateGame ->
|
||||
[ { icon = Layout.iconAsIcon Icons.arrow_back, onPress = OnScreen ViewGameSelectionMenu }
|
||||
]
|
||||
"Create Game | Bot-Man-Toe"
|
||||
|
||||
ViewGameSelectionMenu ->
|
||||
[]
|
||||
"Menu | Bot-Man-Toe"
|
||||
|
||||
ViewGame _ ->
|
||||
[ { icon = Layout.iconAsIcon Icons.arrow_back, onPress = OnScreen ViewGameSelectionMenu }
|
||||
"Replay | Bot-Man-Toe"
|
||||
, body =
|
||||
viewScreen model
|
||||
|> Element.layout
|
||||
[ Element.Background.color (Theme.baseUI model.flavor)
|
||||
]
|
||||
|> List.singleton
|
||||
}
|
||||
|
||||
|
||||
view : Program.ViewBox Model -> Element Msg
|
||||
view data =
|
||||
case data.model.screen of
|
||||
viewScreen : Model -> Element Msg
|
||||
viewScreen model =
|
||||
case model.screen of
|
||||
ViewCreateGame ->
|
||||
Element.text "Create game menu!"
|
||||
|
||||
ViewGameSelectionMenu ->
|
||||
GameList.viewSelection
|
||||
{ flavor = data.flavor
|
||||
, height = data.size.height
|
||||
, model = data.model.games
|
||||
{ flavor = model.flavor
|
||||
, height = model.size.height
|
||||
, model = model.games
|
||||
, onCreateGame = OnScreen ViewCreateGame
|
||||
, onNavigateToGame = OnScreen << ViewGame
|
||||
, width = data.size.width
|
||||
, width = model.size.width
|
||||
}
|
||||
|
||||
ViewGame game ->
|
||||
GameList.viewGame
|
||||
{ flavor = data.flavor
|
||||
{ flavor = model.flavor
|
||||
, game = game
|
||||
, height = data.size.height
|
||||
, height = model.size.height
|
||||
, onNavigateBack = OnScreen ViewGameSelectionMenu
|
||||
, toMsg = OnGameList
|
||||
, width = data.size.width
|
||||
, width = model.size.width
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ module Match exposing (..)
|
|||
import Api
|
||||
import Duration exposing (Duration)
|
||||
import Element exposing (Element)
|
||||
import Element.Background
|
||||
import Http
|
||||
import Json.Decode as D
|
||||
import Layout
|
||||
|
|
@ -251,5 +252,5 @@ viewMenu :
|
|||
, width : Quantity Int Pixels
|
||||
}
|
||||
-> Element (Msg gameState)
|
||||
viewMenu _ =
|
||||
viewMenu data =
|
||||
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