recipes.coopcloud.tech/src/Pages/Top.elm

278 lines
6.7 KiB
Elm

module Pages.Top exposing (Model, Msg, Params, page)
import Html exposing (Html, a, button, div, h2, h5, i, img, li, p, span, text, ul)
import Html.Attributes exposing (alt, class, href, src, style)
import Html.Events exposing (onClick)
import Http
import Json.Decode as Decode
import Json.Decode.Extra as Decode exposing (andMap)
import Maybe exposing (withDefault)
import Spa.Document exposing (Document)
import Spa.Generated.Route as Route
import Spa.Page as Page exposing (Page)
import Spa.Url as Url exposing (Url)
import String.Extra exposing (ellipsis)
import Util exposing (Direction(..), andThen, by)
page : Page Params Model Msg
page =
Page.element
{ init = init
, update = update
, view = view
, subscriptions = subscriptions
}
-- INIT
type alias Params =
()
type alias App =
{ name : String
, category : String
, repository : Maybe String
, versions : Maybe (List String)
, icon : Maybe String
, status : Int
, slug : String
, default_branch : String
, website : Maybe String
, description : Maybe String
}
type Model
= Failure
| Loading
| Success (List App)
init : Url Params -> ( Model, Cmd Msg )
init { params } =
( Loading, loadApps )
default_image : String
default_image =
"/logo.png"
-- UPDATE
type Msg
= MorePlease
| GotApps (Result Http.Error (List App))
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
MorePlease ->
( Loading, loadApps )
GotApps result ->
case result of
Ok apps ->
( Success apps, Cmd.none )
Err _ ->
( Failure, Cmd.none )
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
view : Model -> Document Msg
view model =
{ title = "abra apps"
, body = [ body model ]
}
body : Model -> Html Msg
body model =
div [ class "pt-3" ]
[ viewApps model
]
viewStatusBadge : App -> Html Msg
viewStatusBadge app =
let
status_class =
case app.status of
1 ->
"badge-success"
2 ->
"badge-info"
3 ->
"badge-warning"
4 ->
"badge-danger"
_ ->
"badge-dark"
in
span [ class ("card-link badge " ++ status_class) ]
[ text ("Score: " ++ String.fromInt app.status) ]
viewApp : App -> Html Msg
viewApp app =
let
icon_url =
case app.icon of
Just "" ->
default_image
Just i ->
i
Nothing ->
default_image
repository_link =
case app.repository of
Just link ->
a [ class "card-link", href link ]
[ i [ class "fab fa-git-alt" ] []
, text "code"
]
Nothing ->
text ""
website_link =
case app.website of
Just link ->
case link of
"" ->
text ""
_ ->
a [ class "card-link", href link ]
[ i [ class "fas fa-home" ] []
, text "homepage"
]
Nothing ->
text ""
app_href =
Route.toString <| Route.App_String { app = app.slug }
in
div [ class "col-md-4 mb-3 col-sm-12" ]
[ div [ class "card" ]
[ img [ class "card-img-top", src icon_url, alt ("icon for " ++ app.name) ] []
, div [ class "card-body" ]
[ h5 [ class "card-title" ]
[ a [ href app_href ] [ text app.name ] ]
, p [] [ text (ellipsis 100 (withDefault "" app.description)) ]
, repository_link
, website_link
, a [ class "card-link", href app_href ]
[ i [ class "fas fa-book" ] []
, text "docs"
]
]
, div [ class "card-footer" ]
[ span [ class "card-link badge badge-secondary" ] [ text app.category ]
, viewStatusBadge app
]
]
]
viewApps : Model -> Html Msg
viewApps model =
case model of
Failure ->
div []
[ div [ class "alert alert-danger" ]
[ p [] [ text "Unable to load app data" ]
, button [ class "btn btn-danger", onClick MorePlease ] [ text "Try Again!" ]
]
]
Loading ->
div [ class "d-flex align-items-center", style "height" "89vh" ]
[ div [ class "spinner-border m-auto text-light" ]
[ span [ class "sr-only" ] [ text "Loading..." ]
]
]
Success apps ->
div []
[ div [ class "row" ]
(List.map viewApp
(apps
|> List.sortWith
(by .status ASC
|> andThen (String.toLower << .name) ASC
)
)
)
]
-- HTTP
loadApps : Cmd Msg
loadApps =
Http.get
{ url = "https://cors-container.herokuapp.com/https://apps.coopcloud.tech/"
, expect = Http.expectJson GotApps appListDecoder
}
featuresDecoder =
Decode.oneOf
[ Decode.at [ "status" ] Decode.int
, Decode.succeed 5
]
appDecoder : Decode.Decoder App
appDecoder =
Decode.succeed App
|> andMap (Decode.field "name" Decode.string)
|> andMap (Decode.field "category" Decode.string)
|> andMap (Decode.maybe (Decode.field "repository" Decode.string))
|> andMap (Decode.succeed Nothing)
|> andMap (Decode.maybe (Decode.field "icon" Decode.string))
|> andMap (Decode.at [ "features" ] featuresDecoder)
|> andMap (Decode.succeed "")
|> andMap (Decode.field "default_branch" Decode.string)
|> andMap (Decode.maybe (Decode.field "website" Decode.string))
|> andMap (Decode.maybe (Decode.field "description" Decode.string))
appListDecoder : Decode.Decoder (List App)
appListDecoder =
Decode.keyValuePairs appDecoder
|> Decode.map buildApp
buildApp : List ( String, App ) -> List App
buildApp apps =
List.map (\( slug, app ) -> { app | slug = slug }) apps