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