Last active
March 10, 2016 15:12
Artist Search Example
This file contains 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
-------------------------- | |
-- CORE LIBRARY IMPORTS -- | |
-------------------------- | |
import Json.Decode as Decode exposing (Decoder, object2, map, string, list, (:=)) | |
import Task exposing (Task, andThen, succeed, fail, onError) | |
import Signal exposing (Signal, Mailbox, mailbox, message, send) | |
import String | |
------------------------- | |
-- THIRD PARTY IMPORTS -- | |
------------------------- | |
import Html exposing (Html, text, div, input, img, a) | |
import Html.Attributes exposing (style, href, src, placeholder) | |
import Html.Events exposing (on, targetValue) | |
import Html.Lazy exposing (lazy, lazy2, lazy3) | |
import Http exposing (url, get) | |
import Markdown exposing (toHtml) | |
andMap = object2 (<|) | |
last : List a -> Maybe a | |
last list = case list of | |
[] -> Nothing | |
x :: [] -> Just x | |
x :: xs -> last xs | |
----------------------------- | |
-- LAST.FM URL AND API KEY -- | |
----------------------------- | |
baseUrl = | |
"http://ws.audioscrobbler.com/2.0/" | |
apiKey = | |
"4cf3c87329172d056c2d67ee0617f5da" | |
----------- | |
-- MODEL -- | |
----------- | |
type alias Model = | |
{ artist : Maybe Artist } | |
initialModel : Model | |
initialModel = | |
{ artist = Nothing } | |
type alias Artist = | |
{ name : String | |
, url : String | |
, bio : String | |
, imageSource : String | |
} | |
------------------ | |
-- JSON PARSING -- | |
------------------ | |
type alias ArtistQueryJson = | |
{ artist : ArtistJson } | |
artistQueryJson : Decoder ArtistQueryJson | |
artistQueryJson = ArtistQueryJson | |
`map` ("artist" := artistJson) | |
type alias ArtistJson = | |
{ name : String | |
, url : String | |
, image : List ImageJson | |
, bio : BioJson | |
} | |
artistJson : Decoder ArtistJson | |
artistJson = ArtistJson | |
`map` ("name" := string) | |
`andMap` ("url" := string) | |
`andMap` ("image" := list imageJson) | |
`andMap` ("bio" := bioJson) | |
type alias ImageJson = | |
{ text : String | |
, size : String | |
} | |
imageJson : Decoder ImageJson | |
imageJson = ImageJson | |
`map` ("#text" := string) | |
`andMap` ("size" := string) | |
type alias BioJson = | |
{ summary : String } | |
bioJson : Decoder BioJson | |
bioJson = BioJson | |
`map` ("summary" := string) | |
----------------- | |
-- CONVERSIONS -- | |
----------------- | |
jsonToArtist : ArtistQueryJson -> Artist | |
jsonToArtist {artist} = | |
let imageSrc = case last artist.image of | |
Nothing -> "" | |
Just image -> image.text | |
in | |
Artist (artist.name) (artist.url) (artist.bio.summary) (imageSrc) | |
----------- | |
-- TASKS -- | |
----------- | |
getArtistInfo : String -> Task Http.Error () | |
getArtistInfo artist = | |
let | |
requestUrl = url baseUrl | |
[ ("method", "artist.getInfo") | |
, ("artist", artist) | |
, ("api_key", apiKey) | |
, ("format", "json") | |
] | |
in get artistQueryJson requestUrl | |
`andThen` (jsonToArtist >> succeed) | |
`andThen` (Just >> send artistMailbox.address) | |
`onError` \_ -> send artistMailbox.address Nothing | |
--------------- | |
-- MAILBOXES -- | |
--------------- | |
artistMailbox : Mailbox (Maybe Artist) | |
artistMailbox = mailbox Nothing | |
getArtistTaskMailbox : Mailbox (Task Http.Error ()) | |
getArtistTaskMailbox = mailbox (succeed ()) | |
----------- | |
-- PORTS -- | |
----------- | |
port getArtistPort : Signal (Task Http.Error ()) | |
port getArtistPort = | |
getArtistTaskMailbox.signal | |
------------- | |
-- SIGNALS -- | |
------------- | |
model : Signal Model | |
model = | |
Signal.map Model artistMailbox.signal | |
---------- | |
-- VIEW -- | |
---------- | |
type alias Style = List (String, String) | |
(:::) = (,) | |
artistInputStyle : Style | |
artistInputStyle = | |
[ "height" ::: "44px" | |
, "font-size" ::: "14pt" | |
, "font-weight" ::: "100" | |
, "width" ::: "90%" | |
, "max-width" ::: "400px" | |
, "max-height" ::: "44px" | |
, "align-self" ::: "center" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
artistInputWrapperStyle : Style | |
artistInputWrapperStyle = | |
[ "flex" ::: "2" | |
, "display" ::: "flex" | |
, "align-items" ::: "center" | |
, "justify-content" ::: "center" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
artistInput : Html | |
artistInput = | |
div | |
[ style artistInputWrapperStyle ] | |
[ input | |
[ style artistInputStyle | |
, placeholder "Search for Artist or Band" | |
, on "input" targetValue (getArtistInfo >> message getArtistTaskMailbox.address) | |
] | |
[] | |
] | |
artistViewWrapperStyle : Style | |
artistViewWrapperStyle = | |
[ "display" ::: "flex" | |
, "flex" ::: "6" | |
, "flex-direction" ::: "column" | |
, "width" ::: "100%" | |
, "justify-content" ::: "flex-start" | |
, "align-items" ::: "center" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
artistViewStyle : Style | |
artistViewStyle = | |
[ "display" ::: "flex" | |
, "flex-direction" ::: "row" | |
, "justify-content" ::: "space-between" | |
, "width" ::: "90%" | |
, "flex" ::: "1" | |
, "max-height" ::: "500px" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
emptyArtistViewStyle : Style | |
emptyArtistViewStyle = | |
[ "display" ::: "flex" | |
, "flex" ::: "0.0001" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
artistView : Model -> Html | |
artistView {artist} = case artist of | |
Nothing -> | |
div | |
[ style emptyArtistViewStyle ] [] | |
Just artist -> | |
div | |
[ style artistViewWrapperStyle ] | |
[ div | |
[ style artistViewStyle ] | |
[ lazy3 leftView artist.name artist.url artist.bio | |
, lazy rightView artist.imageSource | |
] | |
] | |
rightViewStyle : String -> Style | |
rightViewStyle imageSource = | |
let | |
backgroundStyle = "url(" ++ imageSource ++ ") center center no-repeat" | |
in | |
[ "flex" ::: "1" | |
, "background" ::: backgroundStyle | |
, "background-size" ::: "cover" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
rightView : String -> Html | |
rightView imageSource = | |
div | |
[ style (rightViewStyle imageSource) | |
] | |
[] | |
leftViewStyle : Style | |
leftViewStyle = | |
[ "flex" ::: "1" | |
, "display" ::: "flex" | |
, "flex-direction" ::: "column" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
leftView : String -> String -> String -> Html | |
leftView name url bio = | |
div | |
[ style leftViewStyle ] | |
[ lazy2 artistTitle name url | |
, lazy artistBio bio | |
] | |
artistTitleStyle : Style | |
artistTitleStyle = | |
[ "font-size" ::: "52pt" | |
, "margin-left" ::: "5px" | |
, "margin-top" ::: "10px" | |
, "color" ::: "black" | |
, "font-weight" ::: "100" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
artistTitle : String -> String -> Html | |
artistTitle name url = | |
a | |
[ href url | |
, style artistTitleStyle | |
] | |
[ lazy text name ] | |
artistBioStyle : Style | |
artistBioStyle = | |
[ "font-size" ::: "16pt" | |
, "font-family" ::: "serif" | |
, "margin-left" ::: "5px" | |
, "margin-right" ::: "5px" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
artistBioWrapperStyle : Style | |
artistBioWrapperStyle = | |
[ "flex" ::: "6" | |
, "display" ::: "flex" | |
, "overflow" ::: "hidden" | |
, "align-items" ::: "flex-start" | |
, "justify-content" ::: "center" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
artistBio : String -> Html | |
artistBio bio = | |
div | |
[ style artistBioWrapperStyle ] | |
[ div | |
[ style artistBioStyle ] | |
[ text bio ] | |
] | |
mainViewStyle : Style | |
mainViewStyle = | |
[ "width" ::: "100vw" | |
, "height" ::: "100vh" | |
, "display" ::: "flex" | |
, "flex-direction" ::: "column" | |
, "font-weight" ::: "100" | |
, "transition" ::: "all 0.2s ease-out" | |
] | |
view : Model -> Html | |
view model = | |
div | |
[ style mainViewStyle ] | |
[ artistInput | |
, lazy artistView model | |
] | |
---------- | |
-- MAIN -- | |
---------- | |
main : Signal Html | |
main = Signal.map view model |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment