Files
lootalot-client/src/Main.elm

302 lines
7.4 KiB
Elm

module Main exposing (..)
import Api exposing (Claim, Claims, Item, Loot, Player, Wealth)
import Browser
import Browser.Navigation as Nav
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Json.Encode as E
import Page.Admin as Admin
import Page.Chest as Chest exposing (Msg)
import Route exposing (..)
import Session exposing (..)
import Set exposing (Set)
import Svg.Attributes
import Url
import Utils exposing (..)
-- Main
main : Program () Model Msg
main =
Browser.application
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
, onUrlChange = UrlChanged
, onUrlRequest = LinkClicked
}
-- Model
type alias Model =
{ navbar : Navbar
, page : Page
}
type alias Navbar =
{ menuOpen : Bool
, navKey : Nav.Key
}
initNavbar key =
Navbar False key
type Page
= Chest Chest.Model
| Admin Admin.Model
| About
| Loading
type alias HasPage r =
{ r | page : Page }
setPage : Page -> HasPage r -> HasPage r
setPage page model =
{ model | page = page }
-- This is not what we really want.
-- The flags will be a Maybe Int (id of logged in player), so
-- in case there is no player logged in, we need to display
-- a "Home" page
-- This mean Chest cannot be initiated right away, and many model
-- fields are useless.
--
-- A User can :
-- - not be logged in -> See About page
-- - just loggend in -> See Loading page then Chest
-- - coming back being still logged in -> See Chest (or same as above)
init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init _ _ key =
( { navbar = initNavbar key
, page = Loading
}
, Session.init SessionLoaded key
)
-- VIEW
view : Model -> Browser.Document Msg
view model =
let
( title, header, content ) =
viewPage model.page
in
{ title = title
, body =
viewHeaderBar header.title header.links model.navbar
:: content
}
viewPage page =
let
( title, content ) =
case page of
Chest chest ->
( "Loot-a-lot", List.map (Html.map GotChestMsg) (Chest.view chest) )
Admin admin ->
( "Administration", Admin.view admin )
About ->
( "A propos", [ p [] [ text "A propos" ] ] )
Loading ->
( "Veuillez patienter...", [ p [] [ text "Chargement" ] ] )
navbarTitle =
case page of
Chest chest ->
chest.state.player.name
Admin _ ->
"Administration"
About ->
"Loot-a-lot"
Loading ->
"Loot-a-(...)"
navbarLinks =
case page of
Chest chest ->
let
linkWithGem =
navLink "fas fa-gem"
in
[ navLink "fas fa-store-alt" "Marchand" "/marchand"
, if chest.state.player.id == 0 then
linkWithGem "Nouveau loot" "/nouveau-tresor"
else
linkWithGem "Coffre de groupe" "/coffre"
]
_ ->
[]
in
( title, { title = navbarTitle, links = navbarLinks }, content )
-- HEADER SECTION
navLink icon linkText url =
a [ class "navbar-item", href url ]
[ renderIcon { icon = icon, ratio = "1x", size = "medium" }
, span [] [ text linkText ]
]
viewHeaderBar : String -> List (Html Msg) -> Navbar -> Html Msg
viewHeaderBar navbarTitle navbarLinks navbar =
nav [ class "navbar", class "is-transparent" ]
[ div [ class "navbar-brand" ]
[ a [ class "navbar-item", href "/" ]
[ renderIcon { icon = "fab fa-d-and-d", size = "medium", ratio = "2x" }
, span [ class "title is-4", style "padding-left" "0.4em" ] [ text navbarTitle ]
]
, a
[ class "navbar-burger"
, classList [ ( "is-active", navbar.menuOpen ) ]
, onClick SwitchMenuOpen
]
[ span [ attribute "aria-hidden" "true" ] []
, span [ attribute "aria-hidden" "true" ] []
, span [ attribute "aria-hidden" "true" ] []
]
]
, div [ class "navbar-menu", classList [ ( "is-active", navbar.menuOpen ) ] ]
[ div [ class "navbar-end" ] navbarLinks
]
]
-- UPDATE
type Msg
= UrlChanged Url.Url
| LinkClicked Browser.UrlRequest
| SessionLoaded (Maybe Session)
| SwitchMenuOpen
| GotChestMsg Chest.Msg
| GotAdminMsg Admin.Msg
-- | GotAdminMsg Admin.Msg
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
let
updateChest chestMsg =
case model.page of
Chest chest ->
let
( newChest, cmd ) =
Chest.update chestMsg chest
in
( setPage (Chest newChest) model, Cmd.map GotChestMsg cmd )
_ ->
( model |> setPage About, Cmd.none )
in
case msg of
SessionLoaded session ->
case session of
Just logged ->
let
navKey =
Session.key logged
user =
Session.user logged
in
case user of
Session.Player playerId ->
let
( chest, cmd ) =
Chest.init navKey playerId
in
( model |> setPage (Chest chest), Cmd.map GotChestMsg cmd )
Session.Admin ->
let
( admin, cmd ) =
Admin.init navKey
in
( model |> setPage (Admin admin), Cmd.map GotAdminMsg cmd )
Nothing ->
( model |> setPage About, Cmd.none )
LinkClicked urlRequest ->
case model.page of
Chest chestModel ->
case urlRequest of
Browser.Internal url ->
( model, Nav.pushUrl model.navbar.navKey (Url.toString url) )
Browser.External href ->
( model, Cmd.none )
_ ->
( model, Cmd.none )
UrlChanged url ->
let
route =
Route.fromUrl url
in
case route of
Just (Route.Home content) ->
updateChest (Chest.SetContent content)
_ ->
( model |> setPage About, Cmd.none )
GotChestMsg chestMsg ->
updateChest chestMsg
GotAdminMsg adminMsg ->
( model, Cmd.none )
SwitchMenuOpen ->
( { model | navbar = Navbar (not model.navbar.menuOpen) model.navbar.navKey }, Cmd.none )
-- SUBSCRIPTIONS
--
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none