Files
lootalot-client/src/Page.elm
2019-12-15 14:30:28 +01:00

552 lines
15 KiB
Elm

module Page exposing (Page(..), PageMsg, gotoGroupChest, gotoHome, gotoShop, initHome, update, view)
import Api
import Api.Player
import Bulma as B
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Page.Dashboard as Home
import Page.GroupChest as GroupChest
import Page.Shop as Shop
import Process
import Route exposing (toHref)
import Session exposing (Session)
import Task
import Wealth
type Page
= Home Home.Model
| GroupChest GroupChest.Model
| Shop Shop.Model
| About
| Loading
init =
Loading
mapPageMsg toMsg ( controls, content ) =
( Html.map toMsg controls
, List.map (Html.map toMsg) content
)
maybeSession page =
case page of
Home model ->
Just <| Home.getSession model
GroupChest model ->
Just <| Session.getSession model
Shop model ->
Just <| Session.getSession model
_ ->
Nothing
view page =
let
( pageTitle, ( controls, content ) ) =
case page of
Home home ->
( "Loot-a-lot"
, Home.view home
|> mapPageMsg GotHomeMsg
)
GroupChest chest ->
( "Coffre de groupe"
, GroupChest.view chest
|> mapPageMsg GotGroupChestMsg
)
Shop shop ->
( "Marchand"
, Shop.view shop
|> mapPageMsg GotShopMsg
)
About ->
( "Loot-a-lot", ( text "", [ p [] [ text "A propos" ] ] ) )
Loading ->
( "Loot-a-lot"
, ( text ""
, [ div [ class "hero" ]
[ div [ class "hero-body" ]
[ p [] [ text "Chargement" ] ]
]
]
)
)
( navbarTitle, navbarLinks ) =
case maybeSession page of
Just session ->
case Session.user session of
Session.Player data ->
( data.player.name
, [ navLink "fas fa-dungeon" Route.Home page
, navLink "fas fa-store-alt" Route.Merchant page
, if data.player.id /= 0 then
navLink "fas fa-gem" Route.GroupChest page
else
text ""
]
)
Session.Admin _ ->
( "Administration"
, [ navLink "fas fa-users" Route.Home page
, navLink "fas fa-store-alt" Route.Merchant page
]
)
Nothing ->
( "Loot-a-lot"
, []
)
in
( pageTitle
, { title = navbarTitle, links = navbarLinks }
, [ div [ class "container box is-paddingless" ] <|
[ viewSessionBar (maybeSession page) pageTitle [ controls ]
, section [ class "section" ]
content
]
]
)
type NotificationKind
= NotifySuccess
| NotifyError
viewNotification kind content =
let
className =
case kind of
NotifySuccess ->
"is-success"
NotifyError ->
"is-danger"
in
div
[ class ("level-item notification " ++ className)
, style "margin-left" "1.5rem"
]
[ text content
, a [ class "delete", onClick <| CloseNotification kind ] []
]
viewSessionBar session pageTitle pageControls =
let
user =
case Maybe.map Session.user session of
Nothing ->
[]
Just (Session.Player data) ->
-- TODO: Urgh ! When will this Wealth.Model move out of session !
[ div [ class "level-item level is-mobile" ] <|
List.map (Html.map Wealth) <|
Wealth.view data.player.wealth data.wealth
++ (if data.player.debt > 0 then
[ div [ class "level-item" ]
[ p [ class "has-text-right has-text-danger" ]
[ strong [ class "heading is-marginless has-text-danger" ] [ text "Dette" ]
, span [ class <| "is-size-4" ] [ text (String.fromInt data.player.debt ++ "po") ]
]
]
]
else
[]
)
]
Just (Session.Admin _) ->
[]
notifications =
[ case Maybe.map Session.notification session of
Just (Just t) ->
viewNotification NotifySuccess t
_ ->
text ""
, case Maybe.map Session.error session of
Just (Just t) ->
viewNotification NotifyError t
_ ->
text ""
]
title =
p [ class "title" ] [ text pageTitle ]
in
section [ class "hero is-dark is-bold" ]
[ div [ class "hero-body", style "padding" "3rem 1.5rem 1.5rem 1.5rem" ]
[ renderLevel notifications user
, renderLevel [ title ] pageControls
]
]
renderLevel left right =
div [ class "level" ]
[ div [ class "level-left" ] left
, div [ class "level-right" ] right
]
-- PLAYER BAR
navLink icon route page =
let
linkText =
case route of
Route.Merchant ->
"Marchand"
Route.GroupChest ->
"Coffre de groupe"
Route.Home ->
"Accueil"
Route.About ->
"About"
isActive =
case ( route, page ) of
( Route.Merchant, Shop _ ) ->
True
( Route.GroupChest, GroupChest _ ) ->
True
( Route.Home, Home _ ) ->
True
( Route.About, About ) ->
True
_ ->
False
in
a [ class "navbar-item", classList [ ( "is-active", isActive ) ], href (toHref route) ]
[ B.icon { icon = icon, ratio = Just "fa-1x", size = Just "is-medium" }
, span [] [ text linkText ]
]
-- UPDATE
--
type PageMsg
= ApiMsg Api.Msg
| GotGroupChestMsg GroupChest.Msg
| GotHomeMsg Home.Msg
| GotShopMsg Shop.Msg
| Wealth Wealth.Msg
| CloseNotification NotificationKind
-- Restores the page after an action has be resolved (either success or error)
closeAction ( page, cmd ) =
let
( newPage, pageCmd ) =
case page of
Home from ->
gotoHome page
GroupChest from ->
gotoGroupChest page
Shop from ->
gotoShop page
_ ->
( page, Cmd.none )
in
( newPage, Cmd.batch [ cmd, pageCmd ] )
update msg page =
case ( msg, page, maybeSession page ) of
-- Home page
-- Capture API messages
( GotHomeMsg (Home.Api apiMsg), Home home, _ ) ->
update (ApiMsg apiMsg) page
|> closeAction
-- Relay others
( GotHomeMsg subMsg, Home home, _ ) ->
Home.update subMsg home
|> updatePage Home GotHomeMsg
( GotHomeMsg _, _, _ ) ->
( page, Cmd.none )
-- Group chest
( GotGroupChestMsg (GroupChest.Api apiMsg), GroupChest _, _ ) ->
update (ApiMsg apiMsg) page
|> closeAction
( GotGroupChestMsg subMsg, GroupChest chest, _ ) ->
GroupChest.update subMsg chest
|> updatePage GroupChest GotGroupChestMsg
( GotGroupChestMsg _, _, _ ) ->
( page, Cmd.none )
-- Shop page
( GotShopMsg (Shop.Api apiMsg), Shop shop, _ ) ->
update (ApiMsg apiMsg) page
|> closeAction
( GotShopMsg subMsg, Shop shop, _ ) ->
Shop.update subMsg shop
|> updatePage Shop GotShopMsg
( GotShopMsg _, _, _ ) ->
( page, Cmd.none )
-- Notifications
--
( CloseNotification kind, _, Just session ) ->
( map
(case kind of
NotifySuccess ->
Session.setNotification Nothing
NotifyError ->
Session.setError Nothing
)
page
, Cmd.none
)
( CloseNotification _, _, _ ) ->
let
_ =
Debug.log "lost close msg" 0
in
( page, Cmd.none )
-- Wealth viewer/editor
( Wealth wealthMsg, _, Just session ) ->
let
wealthModel =
Session.wealth session
in
case Session.user session of
Session.Player data ->
let
( newWealth, maybeEdit ) =
Wealth.update wealthMsg data.wealth
in
( map (Session.updateWealth newWealth) page
, case maybeEdit of
Just amount ->
Api.confirmAction
(String.fromInt (.id data.player))
(Api.WealthPayload amount)
|> Cmd.map ApiMsg
Nothing ->
Cmd.none
)
_ ->
Debug.log "not a player but updates wealth"
( page, Cmd.none )
( Wealth wealthMsg, _, Nothing ) ->
( page, Cmd.none )
-- Handle API messages
( ApiMsg (Api.GotActionResult response), _, Just session ) ->
let
_ =
Debug.log "got api response" response
in
case response of
Ok result ->
let
updates =
Maybe.withDefault [] result.updates
updatedUser =
List.foldl applyUpdate (Session.user session) updates
in
( page
|> map (Session.updateUser updatedUser)
|> map (Session.updateNotifications ( result.notification, result.errors ))
, case result.notification of
Just n ->
Task.perform
(\_ -> CloseNotification NotifySuccess)
(Process.sleep 5000.0)
Nothing ->
Cmd.none
)
Err error ->
let
_ =
Debug.log "ApiError" error
in
( page
|> map (Session.setError (Just "Oups! Cela n'a pas fonctionné"))
, Cmd.none
)
( ApiMsg apiMsg, _, Nothing ) ->
let
_ =
Debug.log "rogue api msg !" apiMsg
in
( page, Cmd.none )
updatePage toPage toMsg ( subModel, subMsg ) =
( toPage subModel
, Cmd.map toMsg subMsg
)
-- Maps the page session to a function, if any
map func page =
case maybeSession page of
Nothing ->
page
Just session ->
case page of
Home model ->
Home <| Home.updateSession model (func session)
GroupChest model ->
GroupChest { model | session = func session }
Shop model ->
Shop { model | session = func session }
_ ->
page
applyUpdate : Api.Update -> Session.User -> Session.User
applyUpdate u user =
{- Note: DbUpdates always refer to the active player -}
case user of
Session.Player data ->
case u of
Api.ItemRemoved item ->
Session.Player
{ data
| loot =
List.filter
(\i -> i.id /= item.id)
data.loot
}
Api.ItemAdded item ->
Session.Player { data | loot = item :: data.loot }
Api.WealthUpdated diff ->
let
player =
data.player
wealth =
player.wealth
in
Session.Player
{ data
| player =
{ player
| wealth =
Api.Player.Wealth
(wealth.cp + diff.cp)
(wealth.sp + diff.sp)
(wealth.gp + diff.gp)
(wealth.pp + diff.pp)
}
}
Api.ClaimAdded claim ->
Session.Player { data | claims = claim :: data.claims }
Api.ClaimRemoved claim ->
Session.Player { data | claims = List.filter (\c -> c.id /= claim.id) data.claims }
Session.Admin _ ->
user
-- CHANGE ROUTE
initHome session =
Home.init session
|> updatePage Home GotHomeMsg
gotoHome page =
case maybeSession page of
Nothing ->
( page, Cmd.none )
Just session ->
Home.init session
|> updatePage Home GotHomeMsg
gotoShop page =
case maybeSession page of
Nothing ->
( page, Cmd.none )
Just session ->
Shop.init session
|> updatePage Shop GotShopMsg
gotoGroupChest page =
case maybeSession page of
Nothing ->
( page, Cmd.none )
Just session ->
GroupChest.init session
|> updatePage GroupChest GotGroupChestMsg
gotoInventory session =
()