elm-twitch/src/Views.elm

428 lines
12 KiB
Elm
Raw Normal View History

2020-10-03 18:44:16 +02:00
module Views exposing (view)
import Browser
2020-10-04 13:15:57 +02:00
import Colors
import Consts
2020-10-03 18:44:16 +02:00
import Core
2020-10-04 13:15:57 +02:00
import Element exposing (Element)
import Element.Background as Background
import Element.Border as Border
import Element.Font as Font
import Element.Input as Input
2020-10-04 17:06:03 +02:00
import Element.Keyed as Keyed
2020-10-04 16:37:01 +02:00
import Html
import Html.Attributes
import Json.Encode as Encode
2020-10-04 15:24:04 +02:00
import Time
import TimeUtils
2020-10-04 13:15:57 +02:00
import Twitch
2020-10-03 18:44:16 +02:00
2020-10-04 13:15:57 +02:00
view : Core.FullModel -> Browser.Document Core.Msg
2020-10-03 18:44:16 +02:00
view model =
2020-10-04 13:15:57 +02:00
{ title = Consts.url
2020-10-04 15:24:04 +02:00
, body =
[ Element.layout [ Font.color Colors.blackFont, Font.size Consts.normalFontSize ]
(viewContent model)
]
2020-10-03 18:44:16 +02:00
}
2020-10-04 13:15:57 +02:00
viewContent : Core.FullModel -> Element Core.Msg
viewContent model =
let
content =
case model of
2020-10-04 16:02:54 +02:00
Core.Unloaded _ _ ->
2020-10-04 13:15:57 +02:00
Element.none
Core.Loaded submodel ->
mainView submodel
in
Element.column [ Element.width Element.fill ] [ topBar, content ]
mainView : Core.Model -> Element Core.Msg
mainView model =
case model.page of
Core.Home ->
playlistsView model.playlists
2020-10-04 14:24:16 +02:00
Core.Playlist playlist ->
2020-10-04 15:24:04 +02:00
videoMiniaturesView model.zone playlist
2020-10-04 14:24:16 +02:00
Core.Video playlist video ->
2020-10-04 15:24:04 +02:00
videoView model.zone playlist video
2020-10-04 14:24:16 +02:00
2020-10-04 13:15:57 +02:00
topBar : Element Core.Msg
topBar =
Element.row
[ Element.width Element.fill
, Background.color Colors.primary
, Font.color Colors.white
, Font.size Consts.homeFontSize
]
[ homeButton ]
homeButton : Element Core.Msg
homeButton =
Input.button
[ Element.padding Consts.homePadding
, Element.height Element.fill
, Element.mouseOver [ Background.color Colors.primaryOver ]
, Font.bold
]
{ label = Element.text Consts.name
2020-10-04 14:24:16 +02:00
, onPress = Just Core.HomeClicked
2020-10-04 13:15:57 +02:00
}
playlistsView : List Twitch.Playlist -> Element Core.Msg
playlistsView playlists =
let
empty =
Element.el [ Element.width Element.fill ] Element.none
views =
List.map playlistView playlists
grouped =
group 4 views
rows =
grouped
|> List.map (\x -> List.map (Maybe.withDefault empty) x)
|> List.map (Element.row [ Element.spacing 10, Element.width Element.fill ])
final =
Element.column [ Element.padding 10, Element.spacing 10, Element.width Element.fill ] rows
in
final
playlistView : Twitch.Playlist -> Element Core.Msg
playlistView playlist =
let
image =
2020-10-04 14:24:16 +02:00
Element.image
[ Element.width Element.fill
, Element.height Element.fill
, Element.inFront inFront
]
2020-10-04 13:15:57 +02:00
{ description = "", src = Twitch.playlistMiniatureUrl playlist }
length =
List.length playlist.videos
label =
String.fromInt length
++ " video"
++ (if length > 1 then
"s"
else
""
)
inFront =
2020-10-04 14:24:16 +02:00
Element.text label
|> Element.el
[ Background.color Colors.greyBackground
, Border.rounded 5
, Element.padding 5
, Font.color Colors.white
]
|> Element.el
[ Element.alignBottom
, Element.alignRight
, Element.padding 5
]
display =
Element.column [ Element.width Element.fill, Element.spacing 10 ]
[ image
, Element.paragraph
2020-10-04 15:24:04 +02:00
[ Font.bold, Font.size Consts.titleFontSize ]
2020-10-04 14:24:16 +02:00
[ Element.text playlist.name ]
2020-10-04 13:15:57 +02:00
]
2020-10-04 14:24:16 +02:00
button =
Input.button [ Element.width Element.fill, Element.alignTop ]
{ label = display
, onPress = Just (Core.PlaylistClicked playlist)
}
in
button
2020-10-04 15:24:04 +02:00
videoMiniaturesView : Time.Zone -> Twitch.Playlist -> Element Core.Msg
videoMiniaturesView zone playlist =
2020-10-04 14:24:16 +02:00
let
empty =
Element.el [ Element.width Element.fill ] Element.none
views =
2020-10-04 15:24:04 +02:00
List.map (videoMiniatureView zone playlist) playlist.videos
2020-10-04 14:24:16 +02:00
grouped =
group 4 views
rows =
grouped
|> List.map (\x -> List.map (Maybe.withDefault empty) x)
|> List.map (Element.row [ Element.spacing 10, Element.width Element.fill ])
final =
Element.column [ Element.padding 10, Element.spacing 10, Element.width Element.fill ] rows
in
final
2020-10-04 15:24:04 +02:00
videoMiniature : Twitch.Video -> Element Core.Msg
videoMiniature video =
2020-10-04 14:24:16 +02:00
let
inFront =
Element.text label
|> Element.el
[ Background.color Colors.greyBackground
, Border.rounded 5
, Element.padding 5
, Font.color Colors.white
]
|> Element.el
[ Element.alignBottom
, Element.alignRight
, Element.padding 5
]
2020-10-04 13:15:57 +02:00
2020-10-04 15:24:04 +02:00
image =
Element.image
[ Element.width Element.fill
, Element.height Element.fill
, Element.inFront inFront
]
{ description = "", src = Twitch.videoMiniatureUrl video }
label =
formatTime video.duration
in
image
videoMiniatureView : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
videoMiniatureView zone playlist video =
let
2020-10-04 13:15:57 +02:00
display =
Element.column [ Element.width Element.fill, Element.spacing 10 ]
2020-10-04 15:24:04 +02:00
[ videoMiniature video
, videoDescription zone video
2020-10-04 13:15:57 +02:00
]
button =
Input.button [ Element.width Element.fill, Element.alignTop ]
{ label = display
2020-10-04 14:24:16 +02:00
, onPress = Just (Core.VideoClicked playlist video)
2020-10-04 13:15:57 +02:00
}
in
button
2020-10-04 17:06:03 +02:00
videoInList : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg
videoInList zone playlist activeVideo video =
2020-10-04 16:37:01 +02:00
let
2020-10-04 18:00:16 +02:00
( msg, attr ) =
2020-10-04 17:06:03 +02:00
if video == activeVideo then
2020-10-04 18:00:16 +02:00
( Nothing
, [ Element.width Element.fill
, Background.color Colors.selected
, Border.color Colors.primary
, Border.width 2
]
)
2020-10-04 17:06:03 +02:00
else
2020-10-04 18:00:16 +02:00
( Just (Core.VideoClicked playlist video), [ Element.width Element.fill ] )
2020-10-04 17:06:03 +02:00
2020-10-04 16:37:01 +02:00
label =
Element.row [ Element.width Element.fill, Element.spacing 10 ]
[ Element.el [ Element.width (Element.fillPortion 2) ]
(videoMiniature video)
, Element.el [ Element.width (Element.fillPortion 3), Element.paddingXY 0 10, Element.alignTop ]
(videoDescription zone video)
]
in
2020-10-04 18:00:16 +02:00
Input.button attr { label = label, onPress = msg }
2020-10-04 15:24:04 +02:00
videoView : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
videoView zone playlist video =
2020-10-04 15:28:05 +02:00
Element.row [ Element.padding 10, Element.width Element.fill, Element.spacing 10 ]
2020-10-04 16:37:01 +02:00
[ Element.column
[ Element.width (Element.fillPortion 2)
2020-10-04 18:00:16 +02:00
, Element.spacing 10
2020-10-04 16:37:01 +02:00
]
2020-10-04 18:00:16 +02:00
[ Keyed.el
[ Element.width Element.fill
, Element.height (Element.px 0)
2020-10-04 18:03:09 +02:00
, Element.htmlAttribute (Html.Attributes.style "padding-top" "56.25%")
2020-10-04 18:00:16 +02:00
, Element.htmlAttribute (Html.Attributes.style "position" "relative")
]
2020-10-04 17:06:03 +02:00
( video.url
, Element.html
(Html.video
[ Html.Attributes.id (Twitch.videoId video)
2020-10-04 18:00:16 +02:00
, Html.Attributes.class "video-js"
, Html.Attributes.class "vjs-default-skin"
, Html.Attributes.class "wf"
2020-10-04 17:06:03 +02:00
, Html.Attributes.property "data-setup" (Encode.string "{\"fluid\": true}")
2020-10-04 18:00:16 +02:00
, Html.Attributes.style "position" "absolute"
, Html.Attributes.style "top" "0"
, Html.Attributes.style "height" "100%"
2020-10-04 17:06:03 +02:00
, Html.Attributes.controls True
, Html.Attributes.autoplay True
]
[]
)
2020-10-04 16:37:01 +02:00
)
2020-10-04 15:24:04 +02:00
, Element.paragraph
[ Font.size Consts.homeFontSize
, Font.bold
2020-10-04 16:37:01 +02:00
, Element.paddingEach { top = 10, left = 0, bottom = 0, right = 0 }
2020-10-04 15:24:04 +02:00
]
[ Element.text video.name ]
, Element.paragraph
[ Font.size Consts.titleFontSize ]
[ Element.text ("Diffusé le " ++ formatDate zone video.date) ]
]
, Element.column [ Element.alignTop, Element.spacing 10, Element.width (Element.fillPortion 1) ]
2020-10-04 17:06:03 +02:00
(List.map (videoInList zone playlist video) playlist.videos)
2020-10-04 15:24:04 +02:00
]
videoDescription : Time.Zone -> Twitch.Video -> Element Core.Msg
videoDescription zone video =
Element.column [ Element.spacing 10 ]
[ Element.paragraph
[ Font.bold
, Font.size Consts.titleFontSize
]
[ Element.text video.name ]
, Element.paragraph
[ Font.color Colors.greyFont
2020-10-04 14:24:16 +02:00
]
2020-10-04 15:24:04 +02:00
[ Element.text ("Diffusé le " ++ formatDate zone video.date) ]
2020-10-04 14:24:16 +02:00
]
2020-10-04 15:24:04 +02:00
formatDate : Time.Zone -> Time.Posix -> String
formatDate zone time =
let
day =
Time.toDay zone time |> String.fromInt |> TimeUtils.pad2
month =
Time.toMonth zone time |> TimeUtils.monthToString
year =
Time.toYear zone time |> String.fromInt |> TimeUtils.pad2
hours =
Time.toHour zone time |> String.fromInt
minutes =
Time.toMinute zone time |> String.fromInt |> TimeUtils.pad2
in
day ++ "/" ++ month ++ "/" ++ year ++ " à " ++ hours ++ "h" ++ minutes
2020-10-04 13:15:57 +02:00
formatTime : Int -> String
formatTime time =
2020-10-04 14:24:16 +02:00
let
hours =
toHours time
minutes =
toMinutes time
seconds =
toSeconds time
hoursString =
String.fromInt hours
minutesString =
if minutes < 10 then
"0" ++ String.fromInt minutes
else
String.fromInt minutes
secondsString =
if seconds < 10 then
"0" ++ String.fromInt seconds
else
String.fromInt seconds
in
hoursString
2020-10-04 13:15:57 +02:00
++ ":"
2020-10-04 14:24:16 +02:00
++ minutesString
2020-10-04 13:15:57 +02:00
++ ":"
2020-10-04 14:24:16 +02:00
++ secondsString
2020-10-04 13:15:57 +02:00
toHours : Int -> Int
toHours i =
i // 3600
toMinutes : Int -> Int
toMinutes i =
modBy 3600 i // 60
toSeconds : Int -> Int
toSeconds i =
modBy 60 i
group : Int -> List a -> List (List (Maybe a))
group size list =
let
grouped =
List.map (List.map Just) (groupAux size list [])
groupedRev =
List.reverse grouped
( firstFixed, tail ) =
case groupedRev of
h :: t ->
( List.concat [ h, List.repeat (size - List.length h) Nothing ], t )
[] ->
( [], [] )
fixed =
(firstFixed :: tail) |> List.reverse
in
fixed
groupAux : Int -> List a -> List (List a) -> List (List a)
groupAux size list acc =
if List.isEmpty list then
2020-10-04 14:24:16 +02:00
List.reverse acc
2020-10-04 13:15:57 +02:00
else
let
groupHead =
List.take size list
groupTail =
List.drop size list
in
groupAux size groupTail (groupHead :: acc)