play around with "reusable modules"

dockercon2021
Efertone 2021-03-31 13:45:06 +02:00
parent 7b1ee76d48
commit b8e3633ae9
No known key found for this signature in database
GPG Key ID: 07AB750DDFD9EE50
12 changed files with 6725 additions and 190 deletions

View File

@ -14,6 +14,7 @@ prod-build:
upload: prod-build
mc cp public/index.html $(REMOTE)/$(BUCKET)/$(BASE_PATH)/index.html
mc cp public/application.js $(REMOTE)/$(BUCKET)/$(BASE_PATH)/application.js
mc cp public/application.css $(REMOTE)/$(BUCKET)/$(BASE_PATH)/application.css
.PHONY: gen-videos-json
gen-videos-json:

6497
index.html Normal file

File diff suppressed because it is too large Load Diff

75
public/application.css Normal file
View File

@ -0,0 +1,75 @@
html {
margin: 0;
padding: 0;
background-color: #161616;
color: #ccc;
}
video {
margin-left: auto;
margin-right: auto;
display: block;
margin-top: 1em;
}
select,
footer,
.downloadList {
margin-left: auto;
margin-right: auto;
margin-top: 1em;
margin-bottom: 1em;
display: block;
width: 960px;
}
footer {
text-align: center;
}
select {
background-color: #333;
color: #ccc;
border: 1px solid #555;
padding: 0.2em;
}
footer > a {
font-size: 0.8em;
margin-left: 1em;
padding: 0.1em 0.8em;
background-color: #60b5cc;
color: #333;
border-radius: 3px;
text-decoration: none;
}
footer > a:hover {
background-color: #cc60b6;
color: #ccc;
}
.downloadList:before {
content: "Direct Download";
font-weight: bold;
text-decoration: underline;
margin-bottom: 1em;
display: block;
}
.downloadList {
margin-top: 2em;
width: 500px;
margin-bottom: 2em;
}
.downloadList > a {
color: #60b5cc;
display: list-item;
margin-top: 0.2em;
text-decoration: none;
}
.downloadList > a:hover {
color: #ccc;
}

View File

@ -3,83 +3,7 @@
<head>
<meta charset="UTF-8">
<title>Minimalistic WebPlayer</title>
<style>
html {
margin: 0;
padding: 0;
background-color: #161616;
color: #ccc;
}
video {
margin-left: auto;
margin-right: auto;
display: block;
margin-top: 1em;
}
select,
footer,
.downloadList {
margin-left: auto;
margin-right: auto;
margin-top: 1em;
margin-bottom: 1em;
display: block;
width: 960px;
}
footer {
text-align: center;
}
select {
background-color: #333;
color: #ccc;
border: 1px solid #555;
padding: 0.2em;
}
footer > a {
font-size: 0.8em;
margin-left: 1em;
padding: 0.1em 0.8em;
background-color: #60b5cc;
color: #333;
border-radius: 3px;
text-decoration: none;
}
footer > a:hover {
background-color: #cc60b6;
color: #ccc;
}
.downloadList:before {
content: "Direct Download";
font-weight: bold;
text-decoration: underline;
margin-bottom: 1em;
display: block;
}
.downloadList {
margin-top: 2em;
width: 500px;
margin-bottom: 2em;
}
.downloadList > a {
color: #60b5cc;
display: list-item;
margin-top: 0.2em;
text-decoration: none;
}
.downloadList > a:hover {
color: #ccc;
}
</style>
<link rel="stylesheet" href="application.css">
<script type="text/javascript" src="application.js"></script>
</head>
@ -93,22 +17,19 @@
<script>
const app = Elm.Main.init({
node: document.getElementById('elm'),
flags: {
debug: true,
extentions:
[ { extention: "mkv", mime: "video/x-matroska" }
, { extention: "mp4", mime: "video/mp4" }
]
}
flags:
{ debug: true
, withDownload: true
, extentions:
[ { extention: "mkv", mime: "video/x-matroska" }
, { extention: "mp4", mime: "video/mp4" }
]
}
});
app.ports.reloadVideoSource.subscribe(function() {
document.querySelector("video").load();
});
app.ports.reloadVideoSource = function(a) {
}
</script>
</body>

27
src/Dropdown.elm Normal file
View File

@ -0,0 +1,27 @@
module Dropdown exposing (..)
import Html
import Html.Attributes
import Html.Events as Events
type alias Option =
{ value : String
, display : String
}
type alias Config msg =
{ options : List Option
, default : Option
, selectEvent : String -> msg
}
view : Config msg -> Html.Html msg
view config = Html.select [ Events.onInput config.selectEvent ] <|
makeOption config.default
:: List.map makeOption config.options
makeOption : Option -> Html.Html msg
makeOption option =
Html.option
[ Html.Attributes.value option.value ]
[ Html.text option.display ]

19
src/Flags.elm Normal file
View File

@ -0,0 +1,19 @@
module Flags exposing (..)
import Video
type alias Flags =
{ debug : Bool
, withDownload : Bool
, extentions : List Video.FileFormat
}
debugEnabled : Flags -> Bool
debugEnabled flags =
flags.debug
withDownload : Flags -> Bool
withDownload flags =
flags.withDownload

View File

@ -3,12 +3,13 @@ module Main exposing (..)
import Browser
import Msg
import Request
import Types
import Model exposing (Model)
import Update
import View
import Flags exposing (Flags)
main : Program Types.Flags Types.Model Msg.Msg
main : Program Flags Model Msg.Msg
main =
Browser.application
{ init = init
@ -20,15 +21,6 @@ main =
}
init : Types.Flags -> a -> b -> ( Types.Model, Cmd Msg.Msg )
init : Flags -> a -> b -> ( Model, Cmd Msg.Msg )
init flags _ _ =
( initialState flags, Request.videoList )
initialState : Types.Flags -> Types.Model
initialState flags =
{ selected = Nothing
, videos = []
, extensions = flags.extentions
, withDownload = Types.withDownload flags
}
( Model.initialState flags, Request.videoList )

21
src/Model.elm Normal file
View File

@ -0,0 +1,21 @@
module Model exposing (..)
import Flags exposing (Flags, withDownload)
import Video
type alias Model =
{ selected : Maybe String
, videos : List Video.Video
, extensions : List Video.FileFormat
, withDownload : Bool
}
initialState : Flags -> Model
initialState flags =
{ selected = Nothing
, videos = []
, extensions = flags.extentions
, withDownload = withDownload flags
}

View File

@ -1,37 +0,0 @@
module Types exposing (..)
import Video
type alias Flags =
{ debug : Bool
, extentions : List Video.FileFormat
}
type alias Model =
{ selected : Maybe String
, videos : List Video.Video
, extensions : List Video.FileFormat
, withDownload : Bool
}
videos : Model -> List Video.Video
videos model =
model.videos
-- These functions may seem useless, but that way it's easier to swap out
-- from flags without changing the init function.
debugEnabled : Flags -> Bool
debugEnabled flags =
flags.debug
withDownload : Flags -> Bool
withDownload flags =
flags.debug

View File

@ -1,12 +1,14 @@
port module Update exposing (..)
import Msg
import Types
import Model exposing (Model)
update : Msg.Msg -> Types.Model -> ( Types.Model, Cmd Msg.Msg )
update : Msg.Msg -> Model -> ( Model, Cmd Msg.Msg )
update msg model =
case msg of
Msg.VideoSelected "" ->
( { model | selected = Nothing }, reloadVideoSource () )
Msg.VideoSelected path ->
( { model | selected = Just path }, reloadVideoSource () )

View File

@ -50,3 +50,38 @@ downloadLink path format =
filename : String -> FileFormat -> String
filename path format =
(Maybe.withDefault "unknown" <| List.head <| List.drop 1 <| String.split "/" path) ++ "." ++ format.extention
type alias ListConfig msg =
{ selected : Maybe String
, extensions : List FileFormat
, attrs : List (Html.Attribute msg)
}
downloadList : ListConfig msg -> Html.Html msg
downloadList config =
case config.selected of
Nothing ->
Html.div [] []
Just path ->
Html.div config.attrs <|
List.map (\e -> downloadLink path e) config.extensions
type alias PlayerConfig msg =
{ path : Maybe String
, extensions : List FileFormat
, attrs : List (Html.Attribute msg)
}
makePlayer : PlayerConfig msg -> Html.Html msg
makePlayer config =
case config.path of
Nothing ->
Html.div [] []
Just path ->
Html.video config.attrs (List.map (htmlSourceElem path) config.extensions)

View File

@ -1,21 +1,21 @@
module View exposing (..)
import Browser exposing (Document)
import Dropdown
import Html
import Html.Attributes
import Html.Events as Events
import Msg
import Types
import Model exposing (Model)
import Video
view : Types.Model -> Document Msg.Msg
view : Model -> Document Msg.Msg
view model =
{ title = "Minimal WebPlayer"
, body =
[ makeDropDown model.videos
, makePlayer model
, downloadList model
[ dropdown model.videos
, player model
, download model
, Html.footer []
[ Html.text "Handmade with love (and Vim and elm) ;)"
, Html.a
@ -28,59 +28,41 @@ view model =
}
empty : Html.Html Msg.Msg
empty =
Html.div [] []
makeDropDown : List Video.Video -> Html.Html Msg.Msg
makeDropDown videos =
dropdown : List Video.Video -> Html.Html Msg.Msg
dropdown videos =
if List.isEmpty videos then
empty
Html.div [] [ Html.text "Please wait..." ]
else
Html.select [ Events.onInput Msg.VideoSelected ] <|
makeOption { title = "Select one please ;)", path = "" }
:: List.map makeOption videos
Dropdown.view
{ options = List.map (\v -> { value = v.path, display = v.title }) videos
, selectEvent = Msg.VideoSelected
, default = { value = "", display = "Select one please ;)" }
}
makeOption : Video.Video -> Html.Html Msg.Msg
makeOption video =
Html.option
[ Html.Attributes.value video.path ]
[ Html.text video.title ]
player : Model -> Html.Html Msg.Msg
player model =
Video.makePlayer
{ path = model.selected
, extensions = model.extensions
, attrs =
[ Html.Attributes.width 960
, Html.Attributes.height 650
, Html.Attributes.controls True
]
}
makePlayer : Types.Model -> Html.Html Msg.Msg
makePlayer model =
case model.selected of
Nothing ->
empty
Just path ->
Html.video
[ Html.Attributes.width 960
, Html.Attributes.height 650
, Html.Attributes.controls True
]
<|
List.map (Video.htmlSourceElem path) model.extensions
downloadList : Types.Model -> Html.Html Msg.Msg
downloadList model =
let
selected =
download : Model -> Html.Html Msg.Msg
download model =
Video.downloadList
{ selected =
if not model.withDownload then
Nothing
else
model.selected
in
case selected of
Nothing ->
empty
Just path ->
Html.div [ Html.Attributes.class "downloadList" ] <|
List.map (\e -> Video.downloadLink path e) model.extensions
, extensions = model.extensions
, attrs = [ Html.Attributes.class "downloadList" ]
}