module Common exposing (..) {-| This module is intended to to be used throughout the application. Don't put anything in this file unless it is safe to be used everywhere. Mostly, this consists of useful shorthands, alternative methods, aliases, etc. -} import Task import Http exposing (Error(..)) import Ports import Maybe.Extra as MaybeX import Dict exposing (Dict(..)) import Html.Attributes as Attr import Html as Html import Navigation exposing (Location) import UrlParser exposing (..) import Html exposing (Html, Attribute) import RemoteData exposing (WebData, RemoteData(..)) import Log import Navigation as Nav {-| This is the global state, which can be thought of as the entire application's model, or as the set intersection of all the other models in the application. -} type alias GlobalModel = { assets : Dict String String } {-| Given a global model, a way to construct an attribute, and an asset key, return that attribute constructed with the asset value. If the asset key does not may to a value, returns the attribute constructed with the asset key. -} assetAttr : GlobalModel -> (String -> Html.Attribute msg) -> String -> Html.Attribute msg assetAttr global attrFactory assetKey = let maybeAssetValue = Dict.get assetKey global.assets value = Maybe.withDefault assetKey maybeAssetValue in attrFactory value assetSrc : GlobalModel -> String -> Html.Attribute msg assetSrc global assetKey = assetAttr global Attr.src assetKey assetHref : GlobalModel -> String -> Html.Attribute msg assetHref global assetKey = assetAttr global Attr.href assetKey {-| This pairing operator is based on -} (=>) : a -> b -> ( a, b ) (=>) = (,) {-| infixl 0 means the (=>) operator has the same precedence as (<|) and (|>), meaning you can use it at the end of a pipeline and have the precedence work out. -} infixl 0 => {-| Useful when building up a Cmd via a pipeline, and then pairing it with a model at the end. session.user |> User.Request.foo |> Task.attempt Foo |> pair { model | something = blah } -} pair : a -> b -> ( a, b ) pair = (,) message : msg -> Cmd msg message it = Task.succeed it |> Task.perform identity const : b -> a -> b const = always constUnit : a -> () constUnit = const () const2 : c -> a -> b -> c const2 it = \a b -> it maybeToList : Maybe a -> List a maybeToList = MaybeX.maybeToList maybesToList : List (Maybe a) -> List a maybesToList = List.filterMap id fst : ( a, b ) -> a fst = Tuple.first snd : ( a, b ) -> b snd = Tuple.second id : a -> a id = identity -- This is used ALL THE TIME for wrapping tagged second values mapSndCmd : (b -> c) -> ( a, Cmd b ) -> ( a, Cmd c ) mapSndCmd f = Tuple.mapSecond <| Cmd.map f reportError : String -> String -> Cmd msg reportError context err = Ports.reportError <| "Error while " ++ context ++ ":\t" ++ err reportHttpError : String -> Http.Error -> Cmd msg reportHttpError context err = reportError context <| case err of BadUrl url -> "Bad URL => " ++ url Timeout -> "network timeout" NetworkError -> "network error" BadStatus response -> response.status.message ++ " (error code " ++ (toString response.status.code) ++ ")" BadPayload payloadErr _ -> "invalid response => " ++ payloadErr apply : a -> List (a -> b) -> List b apply val = List.map ((|>) val) type Route = InitialRoute | FrontRoute | JokeRoute parser : Parser (Route -> c) c parser = oneOf [ map FrontRoute (s "front") , map JokeRoute (s "joke") , map InitialRoute top ] routeToString : Route -> String routeToString route = case route of FrontRoute -> "#front" JokeRoute -> "#joke" InitialRoute -> "#" parseToRoute : Location -> Route parseToRoute location = Maybe.withDefault FrontRoute <| parseHash parser location {-| Generates a Font Awesome icon with the given name. The argument is the icon class name without the "fa-" prefix. -} fa : String -> Html msg fa = faList << List.singleton -- TODO Add in aria-hidden="true" {- Generates a Font Awesome icon with multiple attributes. The arguments are the class name attributes of the icon without the "fa-" prefix. -} faList : List String -> Html msg faList names = let classes = (::) "fa" <| List.map ((++) "fa-") names in Html.i (List.map Attr.class classes) [] {-| Signals that the route has changed. -} routeChanged : Route -> Cmd msg routeChanged = routeToString >> Ports.routeChanged {-| Signals the route has started to load. -} routeLoadStart : Route -> Cmd msg routeLoadStart = routeToString >> Ports.routeLoadStart {-| Signals that the route has finished loading. -} routeLoadEnd : Route -> Cmd msg routeLoadEnd = routeToString >> Ports.routeLoadEnd mapCmdRemoteData : (a -> b) -> Cmd (RemoteData x a) -> Cmd (RemoteData x b) mapCmdRemoteData mapper cmd = Cmd.map (\webdata -> RemoteData.map mapper webdata) cmd handleWebData : String -> WebData a -> Cmd (Maybe a) handleWebData description data = let toNothing cmd = Cmd.map (const Nothing) cmd logInfoToNothing _ = toNothing <| Log.info ("[WEBDATA]\t" ++ description) data in case data of NotAsked -> logInfoToNothing () Loading -> logInfoToNothing () Failure err -> reportHttpError description err |> toNothing Success a -> message (Just a) navigateTo : Route -> Cmd msg navigateTo = Nav.newUrl << routeToString navigateToQueried : Route -> String -> Cmd msg navigateToQueried route query = Nav.newUrl <| "?" ++ query ++ routeToString route