Instantly share code, notes, and snippets.
Created
December 29, 2020 17:29
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save karlosmid/e920de3c29b70aed3cd83160eda192cc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Picshare exposing (main) | |
import Browser | |
import Html exposing (..) | |
import Html.Events exposing (onClick, onInput, onSubmit) | |
import Html.Attributes exposing (class, src, placeholder, type_, disabled, value) | |
import Json.Decode exposing (Decoder, bool, int, list, string, succeed) | |
import Json.Decode.Pipeline exposing (hardcoded, required) | |
import Http | |
type alias Id = | |
Int | |
type alias Photo = | |
{ id : Id | |
, url : String | |
, caption : String | |
, liked : Bool | |
, comments : List String | |
, newComment : String | |
} | |
type alias Model = | |
-- 1. our model is record with photo filed that Maybe Photo type. | |
{ photo : Maybe Photo } | |
photoDecoder : Decoder Photo | |
photoDecoder = | |
succeed Photo | |
|> required "id" int | |
|> required "url" string | |
|> required "caption" string | |
|> required "liked" bool | |
|> required "comments" (list string) | |
|> hardcoded "" | |
main : Program () Model Msg | |
main = | |
Browser.element | |
{ init = init | |
, view = view | |
, update = update | |
, subscriptions = subscriptions | |
} | |
init : () -> ( Model, Cmd Msg) | |
init () = | |
(initialModel, fetchFeed ) | |
-- 7. we need viewFeed to bridge | |
viewFeed : Maybe Photo -> Html Msg | |
viewFeed maybePhoto = | |
case maybePhoto of | |
Just photo -> | |
viewDetailedPhoto photo | |
Nothing -> | |
text "" | |
-- 8. use viewFeed function. | |
view : Model -> Html Msg | |
view model = | |
div [] | |
[ | |
div [ class "header" ] | |
[ h1 [] [text "Picshare"] ], | |
div [ class "content-flow"] | |
[ viewFeed model.photo ] | |
] | |
type Msg | |
= ToggleLike | |
| UpdateComment String | |
| SaveComment | |
| LoadFeed (Result Http.Error Photo) | |
-- 3. Model => Photo because our Model could only have Photo value. | |
-- argument name model => photo is cosmetic, but would help us to have more readable code | |
viewLoveButton : Photo -> Html Msg | |
viewLoveButton photo = | |
let | |
buttonClass = | |
if photo.liked then | |
"fa-heart" | |
else | |
"fa-heart-o" | |
in | |
div [ class "like-button" ] | |
[ i | |
[ class "fa fa-2x" | |
, class buttonClass | |
, onClick ToggleLike | |
] | |
[] | |
] | |
viewComment : String -> Html Msg | |
viewComment comment = | |
li [] | |
[ strong [] [ text "Comment:" ] | |
, text (" " ++ comment) | |
] | |
viewCommentList : List String -> Html Msg | |
viewCommentList comments = | |
case comments of | |
[] -> | |
text "" | |
_ -> | |
div [ class "comments" ] | |
[ ul [] | |
(List.map viewComment comments) | |
] | |
-- 4. Model => Photo because our Model could only have Photo value. | |
-- argument name model => photo is cosmetic, but would help us to have more readable code | |
viewComments : Photo -> Html Msg | |
viewComments photo = | |
div [] | |
[ viewCommentList photo.comments | |
, form [ class "new-comment", onSubmit SaveComment] | |
[ input | |
[ type_ "text" | |
, placeholder "Add a comment..." | |
, value photo.newComment | |
, onInput UpdateComment | |
] | |
[] | |
, button | |
[ disabled (String.isEmpty photo.newComment) ] | |
[ text "Save" ] | |
] | |
] | |
-- 5. Model => Photo because our Model could only have Photo value. | |
-- argument name model => photo is cosmetic, but would help us to have more readable code | |
viewDetailedPhoto : Photo -> Html Msg | |
viewDetailedPhoto photo = | |
div [ class "detailed-photo" ] | |
[ img [ src photo.url ] [] | |
, div [ class "photo-info" ] | |
[ viewLoveButton photo | |
, h2 [ class "caption" ] [ text photo.caption] | |
, viewComments photo | |
] | |
] | |
-- 9. add toggleLike and updateComment functions in order to DRY your code | |
toggleLike : Photo -> Photo | |
toggleLike photo = | |
{ photo | liked = not photo.liked } | |
updateComment : String -> Photo -> Photo | |
updateComment comment photo = | |
{ photo | newComment = comment } | |
-- 10. updateFeed bridges to Maybe Photo type | |
updateFeed : (Photo -> Photo) -> Maybe Photo -> Maybe Photo | |
updateFeed updatePhoto maybePhoto = | |
-- new trick is Maybe.map function, it maps first argument function to second type | |
-- it handles Just and Nothing out of the box | |
Maybe.map updatePhoto maybePhoto | |
-- 11. use updateFeed to bridge model and Mybe type | |
update : Msg -> Model -> ( Model, Cmd Msg ) | |
update msg model = | |
case msg of | |
ToggleLike -> | |
( { model | |
| photo = updateFeed toggleLike model.photo } | |
, Cmd.none | |
) | |
UpdateComment comment -> | |
( { model | |
| photo = updateFeed (updateComment comment) model.photo | |
} | |
, Cmd.none | |
) | |
SaveComment -> | |
( { model | |
| photo = updateFeed saveNewComment model.photo | |
} | |
, Cmd.none | |
) | |
LoadFeed _ -> | |
( model, Cmd.none ) | |
subscriptions : Model -> Sub Msg | |
subscriptions model = | |
Sub.none | |
-- 6. Model => Photo because our Model could only have Photo value. | |
-- argument name model => photo is cosmetic, but would help us to have more readable code | |
saveNewComment : Photo -> Photo | |
saveNewComment photo = | |
let | |
comment = | |
String.trim photo.newComment | |
in | |
case comment of | |
"" -> photo | |
_ -> | |
{ photo | |
| comments = photo.comments ++ [ comment ] | |
, newComment = "" | |
} | |
baseUrl : String | |
baseUrl = "https://programming-elm.com" | |
--2. as Model is of type Maybe, we need to add Just | |
initialModel : Model | |
initialModel = | |
{ photo = | |
Just | |
{ id = 1 | |
, url = baseUrl ++ "/1.jpg" | |
, caption = "Surfing" | |
, liked = False | |
, comments = [ "Really nice place!" ] | |
, newComment = "" | |
} | |
} | |
fetchFeed : Cmd Msg | |
fetchFeed = | |
Http.get | |
{ url = baseUrl ++ "/feed/1" | |
, expect = Http.expectJson LoadFeed photoDecoder | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment