Skip to content
This repository was archived by the owner on Apr 21, 2020. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions client/src/elm/Item/Encoder.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module Item.Encoder exposing (encodeItemName)

import Json.Encode exposing (Value, object, string)
import Item.Model exposing (Item)


{-| Encode just the name of the item, so we can send a
PATCH request to update the name on the backend.
-}
encodeItemName : Item -> Value
encodeItemName item =
object [ ( "label", string item.name ) ]
3 changes: 3 additions & 0 deletions client/src/elm/ItemManager/Model.elm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ just stay within the `WebData` container.
-}
type alias Model =
{ items : Dict ItemId (WebData Item)
, itemPage : Pages.Item.Model.Model
, itemsPage : Pages.Items.Model.Model
}

Expand All @@ -43,11 +44,13 @@ type Msg
| MsgPagesItems Pages.Items.Model.Msg
| HandleFetchedItem ItemId (Result Http.Error Item)
| HandleFetchedItems (Result Http.Error ItemsDict)
| HandlePatchResponse (Result Http.Error ())
| HandlePusherEvent (Result String PusherEvent)


emptyModel : Model
emptyModel =
{ items = Dict.empty
, itemPage = Pages.Item.Model.emptyModel
, itemsPage = Pages.Items.Model.emptyModel
}
57 changes: 51 additions & 6 deletions client/src/elm/ItemManager/Update.elm
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import Config.Model exposing (BackendUrl)
import Date exposing (Date)
import Dict exposing (Dict)
import Item.Model exposing (Item, ItemId)
import Item.Encoder exposing (encodeItemName)
import ItemManager.Decoder exposing (decodeItemFromResponse, decodeItemsFromResponse)
import ItemManager.Model exposing (..)
import ItemManager.Utils exposing (..)
import Json.Decode exposing (decodeValue)
import Json.Encode exposing (Value)
import HttpBuilder exposing (get, withQueryParams)
import Json.Encode exposing (Value, object)
import HttpBuilder exposing (get, withQueryParams, withJsonBody, send)
import Pages.Item.Update
import Pages.Item.Model exposing (ItemUpdate(..), unwrapItemUpdate)
import Pages.Items.Update
import Pusher.Decoder exposing (decodePusherEvent)
import Pusher.Model exposing (PusherEventData(..))
Expand Down Expand Up @@ -70,11 +72,32 @@ update currentDate backendUrl accessToken user msg model =
case getItem id model of
Success item ->
let
( subModel, subCmd, redirectPage ) =
Pages.Item.Update.update backendUrl accessToken user subMsg item
( subModel, itemUpdate, subCmd, redirectPage ) =
Pages.Item.Update.update backendUrl accessToken user subMsg item model.itemPage

updatedItems =
itemUpdate
|> unwrapItemUpdate model.items
(\item_ ->
Dict.insert id (Success item_) model.items
)

sendItemCmd =
case itemUpdate of
UpdateFromUser item_ ->
sendUpdatedItemToBackend backendUrl accessToken id item_

_ ->
Cmd.none
in
( { model | items = Dict.insert id (Success subModel) model.items }
, Cmd.map (MsgPagesItem id) subCmd
( { model
| items = updatedItems
, itemPage = subModel
}
, Cmd.batch
[ Cmd.map (MsgPagesItem id) subCmd
, sendItemCmd
]
, redirectPage
)

Expand Down Expand Up @@ -125,6 +148,16 @@ update currentDate backendUrl accessToken user msg model =
, Nothing
)

HandlePatchResponse (Ok ()) ->
( model, Cmd.none, Nothing )

HandlePatchResponse (Err err) ->
let
_ =
Debug.log "HandlePatchResponse" err
in
( model, Cmd.none, Nothing )

HandleFetchedItem itemId (Err err) ->
( { model | items = Dict.insert itemId (Failure err) model.items }
, Cmd.none
Expand Down Expand Up @@ -185,6 +218,18 @@ fetchAllItemsFromBackend backendUrl accessToken model =
)


sendUpdatedItemToBackend : BackendUrl -> String -> ItemId -> Item -> Cmd Msg
sendUpdatedItemToBackend backendUrl accessToken itemId item =
let
command =
HttpBuilder.patch (backendUrl ++ "/api/items/" ++ itemId)
|> withQueryParams [ ( "access_token", accessToken ) ]
|> withJsonBody (encodeItemName item)
|> send HandlePatchResponse
in
command


subscriptions : Model -> Page -> Sub Msg
subscriptions model activePage =
pusherItemMessages (decodeValue decodePusherEvent >> HandlePusherEvent)
2 changes: 1 addition & 1 deletion client/src/elm/ItemManager/View.elm
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ viewPageItem currentDate id user model =

Success item ->
div []
[ Html.map (MsgPagesItem id) <| Pages.Item.View.view currentDate user id item ]
[ Html.map (MsgPagesItem id) <| Pages.Item.View.view currentDate user id item model.itemPage ]
56 changes: 52 additions & 4 deletions client/src/elm/Pages/Item/Model.elm
Original file line number Diff line number Diff line change
@@ -1,12 +1,60 @@
module Pages.Item.Model
exposing
( Msg(..)
)
module Pages.Item.Model exposing (..)

import App.PageType exposing (Page(..))
import Item.Model exposing (Item)
import Pusher.Model exposing (PusherEventData)


type Msg
= HandlePusherEventData PusherEventData
| SetRedirectPage Page
| EditingNameBegin
| EditingNameUpdate String
| EditingNameFinish
| EditingNameCancel


{-| This lets us keep track of who has updated the item that
we're returning from `Pages.Item.Update.update` to the
ItemManager. If the user changed it, then we should send the
updated item to the server; if, on the other hand, we got
this change from the server in the first place, all we have to
do is make a note of it ourselves.
-}
type ItemUpdate
= UpdateFromBackend Item
| UpdateFromUser Item
| NoUpdate


{-| This allows you to easily do something with the updated
item if there is one, wherever you got it from. Cf.
`Maybe.Extra.unwrap`.
-}
unwrapItemUpdate : a -> (Item -> a) -> ItemUpdate -> a
unwrapItemUpdate default f itemUpdate =
case itemUpdate of
UpdateFromBackend item ->
f item

UpdateFromUser item ->
f item

NoUpdate ->
default


{-| At the moment the only state peculiar to the Item page is
whether the user is currently editing the name of the item,
and if so, what have they typed in. (We wouldn't have to keep
track of the latter in the Model, but doing so allows us to
the contents of the input field if the users navigates to
another page and then comes back.)
-}
type alias Model =
{ editingItemName : Maybe String }


emptyModel : Model
emptyModel =
{ editingItemName = Nothing }
54 changes: 48 additions & 6 deletions client/src/elm/Pages/Item/Update.elm
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ module Pages.Item.Update exposing (update)

import App.PageType exposing (Page(..))
import Config.Model exposing (BackendUrl)
import Item.Model exposing (Item)
import User.Model exposing (..)
import Pages.Item.Model exposing (Msg(..))
import Pages.Item.Model exposing (Model, Msg(..), ItemUpdate(..))
import Pusher.Model exposing (PusherEventData(..))
import Item.Model exposing (Item)


update : BackendUrl -> String -> User -> Msg -> Item -> ( Item, Cmd Msg, Maybe Page )
update backendUrl accessToken user msg item =
update : BackendUrl -> String -> User -> Msg -> Item -> Model -> ( Model, ItemUpdate, Cmd Msg, Maybe Page )
update backendUrl accessToken user msg item model =
case msg of
HandlePusherEventData event ->
case event of
Expand All @@ -18,10 +18,52 @@ update backendUrl accessToken user msg item =
-- which has already been saved at the server. Note that
-- we may have just pushed this change ourselves, so it's
-- already reflected here.
( newItem
( model
, UpdateFromBackend newItem
, Cmd.none
, Nothing
)

SetRedirectPage page ->
( item, Cmd.none, Just page )
( model, NoUpdate, Cmd.none, Just page )

EditingNameBegin ->
( { model | editingItemName = Just item.name }
, NoUpdate
, Cmd.none
, Nothing
)

EditingNameFinish ->
let
newName =
case model.editingItemName of
Just name ->
name

Nothing ->
-- if this happens, then the view
-- code is broken. We can't
-- finish editing the name unless
-- we've already started!
item.name
in
( { model | editingItemName = Nothing }
, UpdateFromUser { item | name = newName }
, Cmd.none
, Nothing
)

EditingNameUpdate updatedName ->
( { model | editingItemName = Just updatedName }
, NoUpdate
, Cmd.none
, Nothing
)

EditingNameCancel ->
( { model | editingItemName = Nothing }
, NoUpdate
, Cmd.none
, Nothing
)
70 changes: 57 additions & 13 deletions client/src/elm/Pages/Item/View.elm
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,74 @@ module Pages.Item.View exposing (view)
import Date exposing (Date)
import Html exposing (..)
import Html.Attributes exposing (..)
import Pages.Item.Model exposing (Msg(..))
import Html.Events exposing (onClick, on, targetValue)
import Json.Decode
import Pages.Item.Model exposing (Model, Msg(..))
import Item.Model exposing (ItemId, Item)
import User.Model exposing (User)


view : Date -> User -> ItemId -> Item -> Html Msg
view currentDate currentUser itemId item =
view : Date -> User -> ItemId -> Item -> Model -> Html Msg
view currentDate currentUser itemId item model =
div []
[ div
[ class "ui secondary pointing fluid menu" ]
[ h2
[ class "ui header" ]
[ text item.name ]
, div
[ class "right menu" ]
[ a
[ class "ui active item" ]
[ text "Overview" ]
]
]
<|
itemHeader item model
++ [ div
[ class "right menu" ]
[ a
[ class "ui active item" ]
[ text "Overview" ]
]
]
, div []
[ img [ src item.image, alt item.name ] []
]
, div
[ class "ui divider" ]
[]
]


itemHeader : Item -> Model -> List (Html Msg)
itemHeader item model =
case model.editingItemName of
Just editedName ->
[ h2 [ class "ui header input" ]
[ input
[ value editedName
, onChange EditingNameUpdate
]
[]
]
, button
[ name "Done"
, type_ "button"
, onClick EditingNameFinish
]
[ text "Done" ]
, button
[ name "Cancel"
, type_ "button"
, onClick EditingNameCancel
]
[ text "Cancel" ]
]

Nothing ->
[ h2
[ class "ui header" ]
[ text item.name ]
, button
[ name "Edit"
, type_ "button"
, onClick EditingNameBegin
]
[ text "Edit" ]
]


onChange : (String -> Msg) -> Attribute Msg
onChange tagger =
on "change" (Json.Decode.map tagger targetValue)