2021-04-19 23:31:11 +00:00
|
|
|
|
module Pages.App_String exposing (Model, Msg, Params, page)
|
|
|
|
|
|
2021-05-04 16:27:42 +00:00
|
|
|
|
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)
|
2021-04-19 23:31:11 +00:00
|
|
|
|
import Html.Events exposing (onClick)
|
|
|
|
|
import Http
|
|
|
|
|
import Json.Decode as Decode
|
2021-04-24 12:10:32 +00:00
|
|
|
|
import Json.Decode.Extra as Decode exposing (andMap)
|
2021-05-04 16:27:42 +00:00
|
|
|
|
import Markdown
|
|
|
|
|
import Regex
|
2021-04-19 23:31:11 +00:00
|
|
|
|
import Spa.Document exposing (Document)
|
2021-04-24 12:10:32 +00:00
|
|
|
|
import Spa.Generated.Route as Route
|
2021-04-19 23:31:11 +00:00
|
|
|
|
import Spa.Page as Page exposing (Page)
|
|
|
|
|
import Spa.Url as Url exposing (Url)
|
|
|
|
|
|
|
|
|
|
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:50:06 +00:00
|
|
|
|
-- INIT
|
|
|
|
|
|
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
page : Page Params Model Msg
|
|
|
|
|
page =
|
|
|
|
|
Page.element
|
|
|
|
|
{ init = init
|
|
|
|
|
, update = update
|
|
|
|
|
, view = view
|
|
|
|
|
, subscriptions = subscriptions
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
type alias Params =
|
2021-05-04 16:27:42 +00:00
|
|
|
|
{ app : String
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
type alias App =
|
|
|
|
|
{ name : String
|
|
|
|
|
, category : String
|
|
|
|
|
, repository : Maybe String
|
|
|
|
|
, versions : Maybe (List String)
|
|
|
|
|
, icon : Maybe String
|
|
|
|
|
, status : String
|
2021-04-24 12:10:32 +00:00
|
|
|
|
, slug : String
|
|
|
|
|
, default_branch : String
|
|
|
|
|
, website : Maybe String
|
2021-04-25 13:51:39 +00:00
|
|
|
|
, description : Maybe String
|
2021-04-19 23:31:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
|
|
|
|
type alias Model =
|
2021-04-19 23:31:11 +00:00
|
|
|
|
{ url : Url Params
|
|
|
|
|
, status : Status
|
|
|
|
|
, readme : String
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Status
|
|
|
|
|
= Failure
|
|
|
|
|
| Loading
|
|
|
|
|
| Success App
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
init : Url Params -> ( Model, Cmd Msg )
|
|
|
|
|
init url =
|
2021-04-24 12:10:32 +00:00
|
|
|
|
( { url = url, status = Loading, readme = "" }, loadApp )
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default_image : String
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-04-19 23:50:06 +00:00
|
|
|
|
-- FIXME: change to absolute URL, if this works?
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
default_image =
|
|
|
|
|
"/logo.png"
|
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- UPDATE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type Msg
|
|
|
|
|
= MorePlease
|
|
|
|
|
| GotApps (Result Http.Error (List App))
|
|
|
|
|
| GotText (Result Http.Error String)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
update : Msg -> Model -> ( Model, Cmd Msg )
|
|
|
|
|
update msg model =
|
|
|
|
|
case msg of
|
|
|
|
|
MorePlease ->
|
2021-04-24 12:10:32 +00:00
|
|
|
|
( { model | status = Loading }, loadApp )
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
GotApps result ->
|
|
|
|
|
case result of
|
|
|
|
|
Ok apps ->
|
2021-05-04 16:27:42 +00:00
|
|
|
|
let
|
|
|
|
|
-- TODO better way of getting a single app?
|
|
|
|
|
apps_filtered =
|
|
|
|
|
List.filter (\app -> app.slug == model.url.params.app) apps
|
|
|
|
|
in
|
|
|
|
|
case List.head apps_filtered of
|
|
|
|
|
Nothing ->
|
|
|
|
|
( { model | status = Failure }, Cmd.none )
|
|
|
|
|
|
|
|
|
|
Just item ->
|
|
|
|
|
( { model | status = Success item }, loadREADME item )
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
2021-04-25 14:00:04 +00:00
|
|
|
|
Err _ ->
|
2021-05-04 16:27:42 +00:00
|
|
|
|
( { model | status = Failure }, Cmd.none )
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
GotText result ->
|
|
|
|
|
case result of
|
|
|
|
|
Ok content ->
|
2021-04-19 23:50:06 +00:00
|
|
|
|
-- update model.content with the loaded README
|
|
|
|
|
let
|
|
|
|
|
-- remove HTML comments
|
2021-05-04 16:27:42 +00:00
|
|
|
|
pattern =
|
|
|
|
|
"<!--.*-->"
|
|
|
|
|
|
|
|
|
|
maybeRegex =
|
|
|
|
|
Regex.fromString pattern
|
|
|
|
|
|
|
|
|
|
regex =
|
|
|
|
|
Maybe.withDefault Regex.never maybeRegex
|
2021-04-19 23:50:06 +00:00
|
|
|
|
in
|
2021-05-04 16:27:42 +00:00
|
|
|
|
( { model | readme = Regex.replace regex (\_ -> "") content }, Cmd.none )
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
2021-04-25 14:00:04 +00:00
|
|
|
|
Err _ ->
|
2021-05-04 16:27:42 +00:00
|
|
|
|
( { model | status = Failure }, Cmd.none )
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
subscriptions : Model -> Sub Msg
|
|
|
|
|
subscriptions model =
|
|
|
|
|
Sub.none
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- VIEW
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
view : Model -> Document Msg
|
|
|
|
|
view model =
|
2021-04-23 17:06:06 +00:00
|
|
|
|
{ title = title model
|
2021-04-19 23:31:11 +00:00
|
|
|
|
, body = [ body model ]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-04-23 17:06:06 +00:00
|
|
|
|
title : Model -> String
|
|
|
|
|
title model =
|
|
|
|
|
case model.status of
|
|
|
|
|
Loading ->
|
2021-04-25 15:47:57 +00:00
|
|
|
|
"loading – abra apps"
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-23 17:06:06 +00:00
|
|
|
|
Failure ->
|
2021-04-25 15:47:57 +00:00
|
|
|
|
"error –- abra apps"
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-23 17:06:06 +00:00
|
|
|
|
Success app ->
|
2021-04-25 15:47:57 +00:00
|
|
|
|
app.name ++ " – abra apps"
|
2021-04-23 17:06:06 +00:00
|
|
|
|
|
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
body : Model -> Html Msg
|
|
|
|
|
body model =
|
|
|
|
|
div [ class "pt-3" ]
|
|
|
|
|
[ case model.status of
|
|
|
|
|
Failure ->
|
2021-04-24 13:36:56 +00:00
|
|
|
|
div []
|
|
|
|
|
[ div [ class "alert alert-danger" ]
|
|
|
|
|
[ p [] [ text "Unable to load app data" ]
|
|
|
|
|
, button [ class "btn btn-danger", onClick MorePlease ] [ text "Try Again!" ]
|
|
|
|
|
]
|
2021-04-19 23:31:11 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
Loading ->
|
2021-05-04 16:27:42 +00:00
|
|
|
|
div [ class "d-flex align-items-center", style "height" "89vh" ]
|
|
|
|
|
[ div [ class "spinner-border m-auto text-light" ]
|
|
|
|
|
[ span [ class "sr-only" ] [ text "Loading..." ]
|
|
|
|
|
]
|
|
|
|
|
]
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
Success app ->
|
|
|
|
|
div []
|
|
|
|
|
[ div [ class "row" ]
|
2021-05-04 16:27:42 +00:00
|
|
|
|
[ viewApp app model.readme ]
|
2021-04-19 23:31:11 +00:00
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
viewStatusBadge : App -> Html Msg
|
|
|
|
|
viewStatusBadge app =
|
|
|
|
|
let
|
|
|
|
|
status_class =
|
|
|
|
|
case app.status of
|
|
|
|
|
"1" ->
|
|
|
|
|
"badge-success"
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
"2" ->
|
|
|
|
|
"badge-info"
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
"3" ->
|
|
|
|
|
"badge-warning"
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
|
|
|
|
"4" ->
|
2021-04-19 23:31:11 +00:00
|
|
|
|
"badge-danger"
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
_ ->
|
|
|
|
|
"badge-dark"
|
|
|
|
|
in
|
2021-05-04 16:27:42 +00:00
|
|
|
|
span [ class ("card-link badge " ++ status_class) ]
|
|
|
|
|
[ text ("Score: " ++ app.status) ]
|
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
viewApp : App -> String -> Html Msg
|
|
|
|
|
viewApp app readme =
|
|
|
|
|
let
|
|
|
|
|
icon_url =
|
|
|
|
|
case app.icon of
|
|
|
|
|
Just "" ->
|
|
|
|
|
default_image
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
Just i ->
|
|
|
|
|
i
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
Nothing ->
|
|
|
|
|
default_image
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
|
|
|
|
repository_link =
|
2021-04-19 23:31:11 +00:00
|
|
|
|
case app.repository of
|
2021-04-23 17:06:06 +00:00
|
|
|
|
Just link ->
|
2021-05-04 16:27:42 +00:00
|
|
|
|
a [ class "card-link", href link ]
|
|
|
|
|
[ i [ class "fab fa-git-alt" ] []
|
|
|
|
|
, text "code"
|
|
|
|
|
]
|
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
Nothing ->
|
|
|
|
|
text ""
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
|
|
|
|
website_link =
|
2021-04-24 12:10:32 +00:00
|
|
|
|
case app.website of
|
|
|
|
|
Just link ->
|
|
|
|
|
case link of
|
|
|
|
|
"" ->
|
|
|
|
|
text ""
|
2021-05-04 16:27:42 +00:00
|
|
|
|
|
2021-04-24 12:10:32 +00:00
|
|
|
|
_ ->
|
2021-05-04 16:27:42 +00:00
|
|
|
|
a [ class "card-link", href link ]
|
|
|
|
|
[ i [ class "fas fa-home" ] []
|
|
|
|
|
, text "homepage"
|
|
|
|
|
]
|
|
|
|
|
|
2021-04-24 12:10:32 +00:00
|
|
|
|
Nothing ->
|
|
|
|
|
text ""
|
2021-05-04 16:27:42 +00:00
|
|
|
|
in
|
|
|
|
|
div [ class "col-md-6 col-sm-10 mb-3 offset-md-3 offset-sm-1" ]
|
|
|
|
|
[ div [ class "card" ]
|
|
|
|
|
[ div [ class "card-header" ]
|
|
|
|
|
[ a
|
|
|
|
|
[ class "btn btn-sm border border-secondary card-link"
|
|
|
|
|
, href (Route.toString Route.Top)
|
2021-04-19 23:31:11 +00:00
|
|
|
|
]
|
2021-05-04 16:27:42 +00:00
|
|
|
|
[ text "← back" ]
|
|
|
|
|
, span [ class "card-link badge badge-secondary" ] [ text app.category ]
|
|
|
|
|
, viewStatusBadge app
|
|
|
|
|
, repository_link
|
|
|
|
|
, website_link
|
|
|
|
|
]
|
|
|
|
|
, img [ class "card-img-top", src icon_url, alt ("icon for " ++ app.name) ] []
|
|
|
|
|
, div [ class "card-body" ]
|
|
|
|
|
-- render Markdown with no special options
|
|
|
|
|
[ div [] (Markdown.toHtml Nothing readme)
|
2021-04-19 23:31:11 +00:00
|
|
|
|
]
|
2021-05-04 16:27:42 +00:00
|
|
|
|
, div [ class "card-footer" ]
|
|
|
|
|
[]
|
2021-04-19 23:31:11 +00:00
|
|
|
|
]
|
2021-05-04 16:27:42 +00:00
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
-- HTTP
|
|
|
|
|
|
|
|
|
|
|
2021-04-24 12:10:32 +00:00
|
|
|
|
loadApp : Cmd Msg
|
|
|
|
|
loadApp =
|
2021-05-04 16:27:42 +00:00
|
|
|
|
Http.get
|
2021-05-30 12:58:09 +00:00
|
|
|
|
{ url = "https://cors-container.herokuapp.com/https://apps.coopcloud.tech/"
|
2021-05-04 16:27:42 +00:00
|
|
|
|
, expect = Http.expectJson GotApps appListDecoder
|
|
|
|
|
}
|
2021-04-24 12:10:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
loadREADME : App -> Cmd Msg
|
2021-05-04 16:27:42 +00:00
|
|
|
|
loadREADME app =
|
2021-04-24 12:10:32 +00:00
|
|
|
|
let
|
2021-05-04 16:27:42 +00:00
|
|
|
|
repository_link =
|
2021-04-24 12:10:32 +00:00
|
|
|
|
case app.repository of
|
|
|
|
|
Just link ->
|
2021-05-04 16:27:42 +00:00
|
|
|
|
a [ class "card-link", href link ]
|
|
|
|
|
[ i [ class "fab fa-git-alt" ] []
|
|
|
|
|
, text "code"
|
|
|
|
|
]
|
|
|
|
|
|
2021-04-24 12:10:32 +00:00
|
|
|
|
Nothing ->
|
|
|
|
|
text ""
|
|
|
|
|
in
|
2021-05-04 16:27:42 +00:00
|
|
|
|
Http.get
|
|
|
|
|
-- FIXME use live Gitea link
|
|
|
|
|
{ url = "https://cors-container.herokuapp.com/https://git.autonomic.zone/coop-cloud/" ++ app.slug ++ "/raw/branch/" ++ app.default_branch ++ "/README.md"
|
|
|
|
|
, expect = Http.expectString GotText
|
|
|
|
|
}
|
2021-04-24 12:10:32 +00:00
|
|
|
|
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
2021-04-25 13:25:25 +00:00
|
|
|
|
featuresDecoder : Decode.Decoder String
|
2021-04-19 23:31:11 +00:00
|
|
|
|
featuresDecoder =
|
2021-04-19 23:50:06 +00:00
|
|
|
|
-- get features.status if it's there
|
2021-05-04 16:27:42 +00:00
|
|
|
|
Decode.oneOf
|
2021-04-19 23:31:11 +00:00
|
|
|
|
[ Decode.at [ "status" ] Decode.string
|
|
|
|
|
, Decode.succeed ""
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
appDecoder : Decode.Decoder App
|
|
|
|
|
appDecoder =
|
2021-04-24 12:10:32 +00:00
|
|
|
|
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)
|
2021-04-25 13:25:25 +00:00
|
|
|
|
|> andMap (Decode.succeed "")
|
2021-04-24 12:10:32 +00:00
|
|
|
|
|> andMap (Decode.field "default_branch" Decode.string)
|
|
|
|
|
|> andMap (Decode.maybe (Decode.field "website" Decode.string))
|
2021-04-25 13:51:39 +00:00
|
|
|
|
|> andMap (Decode.maybe (Decode.field "description" Decode.string))
|
2021-04-19 23:31:11 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
appListDecoder : Decode.Decoder (List App)
|
|
|
|
|
appListDecoder =
|
2021-04-25 13:25:25 +00:00
|
|
|
|
Decode.keyValuePairs appDecoder
|
|
|
|
|
|> Decode.map buildApp
|
|
|
|
|
|
|
|
|
|
|
2021-05-04 16:27:42 +00:00
|
|
|
|
buildApp : List ( String, App ) -> List App
|
2021-04-25 13:25:25 +00:00
|
|
|
|
buildApp apps =
|
2021-05-04 16:27:42 +00:00
|
|
|
|
List.map (\( slug, app ) -> { app | slug = slug }) apps
|