Mobile
This commit is contained in:
parent
a72c5c867c
commit
03651845d1
454
src/Views.elm
454
src/Views.elm
|
@ -4,13 +4,31 @@ import Browser
|
||||||
import Colors
|
import Colors
|
||||||
import Consts
|
import Consts
|
||||||
import Core
|
import Core
|
||||||
import Element
|
import Element exposing (Element)
|
||||||
|
import Element.Background as Background
|
||||||
|
import Element.Border as Border
|
||||||
import Element.Font as Font
|
import Element.Font as Font
|
||||||
import Views.Desktop as Desktop
|
import Element.Input as Input
|
||||||
|
import Element.Keyed as Keyed
|
||||||
|
import Html
|
||||||
|
import Html.Attributes
|
||||||
|
import Json.Encode as Encode
|
||||||
|
import Time
|
||||||
|
import TimeUtils
|
||||||
|
import Twitch
|
||||||
|
|
||||||
|
|
||||||
view : Core.FullModel -> Browser.Document Core.Msg
|
view : Core.FullModel -> Browser.Document Core.Msg
|
||||||
view model =
|
view model =
|
||||||
|
let
|
||||||
|
element =
|
||||||
|
case model of
|
||||||
|
Core.Unloaded _ _ _ ->
|
||||||
|
Element.none
|
||||||
|
|
||||||
|
Core.Loaded m ->
|
||||||
|
viewContent m
|
||||||
|
in
|
||||||
{ title = title model
|
{ title = title model
|
||||||
, body =
|
, body =
|
||||||
[ Element.layout
|
[ Element.layout
|
||||||
|
@ -18,7 +36,10 @@ view model =
|
||||||
, Font.size Consts.normalFontSize
|
, Font.size Consts.normalFontSize
|
||||||
, Font.family [ Font.typeface "Cantarell" ]
|
, Font.family [ Font.typeface "Cantarell" ]
|
||||||
]
|
]
|
||||||
(Desktop.view model)
|
(Element.column
|
||||||
|
[ Element.width Element.fill ]
|
||||||
|
[ topBar, element ]
|
||||||
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,3 +60,430 @@ title model =
|
||||||
|
|
||||||
Core.Video p v ->
|
Core.Video p v ->
|
||||||
Consts.url ++ " - " ++ p.name ++ " - " ++ v.name
|
Consts.url ++ " - " ++ p.name ++ " - " ++ v.name
|
||||||
|
|
||||||
|
|
||||||
|
viewContent : Core.Model -> Element Core.Msg
|
||||||
|
viewContent model =
|
||||||
|
case model.page of
|
||||||
|
Core.Home ->
|
||||||
|
playlistsView model.device model.playlists
|
||||||
|
|
||||||
|
Core.Playlist playlist ->
|
||||||
|
videoMiniaturesView model.device model.zone playlist
|
||||||
|
|
||||||
|
Core.Video playlist video ->
|
||||||
|
videoView model.device model.zone playlist video
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
, onPress = Just Core.HomeClicked
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
playlistsView : Element.Device -> List Twitch.Playlist -> Element Core.Msg
|
||||||
|
playlistsView device playlists =
|
||||||
|
let
|
||||||
|
empty =
|
||||||
|
Element.el [ Element.width Element.fill ] Element.none
|
||||||
|
|
||||||
|
views =
|
||||||
|
List.map playlistView playlists
|
||||||
|
|
||||||
|
grouped =
|
||||||
|
group (numberOfVideosPerRow device) 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 =
|
||||||
|
Element.image
|
||||||
|
[ Element.width Element.fill
|
||||||
|
, Element.height Element.fill
|
||||||
|
, Element.inFront inFront
|
||||||
|
]
|
||||||
|
{ description = "", src = Twitch.playlistMiniatureUrl playlist }
|
||||||
|
|
||||||
|
length =
|
||||||
|
List.length playlist.videos
|
||||||
|
|
||||||
|
label =
|
||||||
|
String.fromInt length
|
||||||
|
++ " video"
|
||||||
|
++ (if length > 1 then
|
||||||
|
"s"
|
||||||
|
|
||||||
|
else
|
||||||
|
""
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
]
|
||||||
|
|
||||||
|
display =
|
||||||
|
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
|
[ image
|
||||||
|
, Element.paragraph
|
||||||
|
[ Font.bold, Font.size Consts.titleFontSize ]
|
||||||
|
[ Element.text playlist.name ]
|
||||||
|
]
|
||||||
|
|
||||||
|
button =
|
||||||
|
Input.button [ Element.width Element.fill, Element.alignTop ]
|
||||||
|
{ label = display
|
||||||
|
, onPress = Just (Core.PlaylistClicked playlist)
|
||||||
|
}
|
||||||
|
in
|
||||||
|
button
|
||||||
|
|
||||||
|
|
||||||
|
videoMiniaturesView : Element.Device -> Time.Zone -> Twitch.Playlist -> Element Core.Msg
|
||||||
|
videoMiniaturesView device zone playlist =
|
||||||
|
let
|
||||||
|
empty =
|
||||||
|
Element.el [ Element.width Element.fill ] Element.none
|
||||||
|
|
||||||
|
views =
|
||||||
|
List.map (videoMiniatureView zone playlist) playlist.videos
|
||||||
|
|
||||||
|
grouped =
|
||||||
|
group (numberOfVideosPerRow device) 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
|
||||||
|
|
||||||
|
|
||||||
|
videoMiniature : Twitch.Video -> Element Core.Msg
|
||||||
|
videoMiniature video =
|
||||||
|
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
|
||||||
|
]
|
||||||
|
|
||||||
|
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
|
||||||
|
display =
|
||||||
|
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
|
[ videoMiniature video
|
||||||
|
, videoDescription zone video
|
||||||
|
]
|
||||||
|
|
||||||
|
button =
|
||||||
|
Input.button [ Element.width Element.fill, Element.alignTop ]
|
||||||
|
{ label = display
|
||||||
|
, onPress = Just (Core.VideoClicked playlist video)
|
||||||
|
}
|
||||||
|
in
|
||||||
|
button
|
||||||
|
|
||||||
|
|
||||||
|
videoInList : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg
|
||||||
|
videoInList zone playlist activeVideo video =
|
||||||
|
let
|
||||||
|
( msg, attr ) =
|
||||||
|
if video == activeVideo then
|
||||||
|
( Nothing
|
||||||
|
, [ Element.width Element.fill
|
||||||
|
, Background.color Colors.selected
|
||||||
|
, Border.color Colors.primary
|
||||||
|
, Border.width 2
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
else
|
||||||
|
( Just (Core.VideoClicked playlist video), [ Element.width Element.fill ] )
|
||||||
|
|
||||||
|
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
|
||||||
|
Input.button attr { label = label, onPress = msg }
|
||||||
|
|
||||||
|
|
||||||
|
videoView : Element.Device -> Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
||||||
|
videoView device zone playlist video =
|
||||||
|
let
|
||||||
|
( builder, contentPadding ) =
|
||||||
|
case device.class of
|
||||||
|
Element.Phone ->
|
||||||
|
( Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
|
, Element.paddingXY 10 0
|
||||||
|
)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
( Element.row [ Element.padding 10, Element.width Element.fill, Element.spacing 10 ]
|
||||||
|
, Element.padding 0
|
||||||
|
)
|
||||||
|
in
|
||||||
|
builder
|
||||||
|
[ Element.column
|
||||||
|
[ Element.width (Element.fillPortion 2)
|
||||||
|
, Element.spacing 10
|
||||||
|
, Element.alignTop
|
||||||
|
]
|
||||||
|
[ Keyed.el
|
||||||
|
[ Element.width Element.fill
|
||||||
|
, Element.height (Element.px 0)
|
||||||
|
, Element.htmlAttribute (Html.Attributes.style "padding-top" "56.25%")
|
||||||
|
, Element.htmlAttribute (Html.Attributes.style "position" "relative")
|
||||||
|
]
|
||||||
|
( video.url
|
||||||
|
, Element.html
|
||||||
|
(Html.video
|
||||||
|
[ Html.Attributes.id (Twitch.videoId video)
|
||||||
|
, Html.Attributes.class "video-js"
|
||||||
|
, Html.Attributes.class "vjs-default-skin"
|
||||||
|
, Html.Attributes.class "wf"
|
||||||
|
, Html.Attributes.property "data-setup" (Encode.string "{\"fluid\": true}")
|
||||||
|
, Html.Attributes.style "position" "absolute"
|
||||||
|
, Html.Attributes.style "top" "0"
|
||||||
|
, Html.Attributes.style "height" "100%"
|
||||||
|
, Html.Attributes.controls True
|
||||||
|
, Html.Attributes.autoplay True
|
||||||
|
]
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
, Element.paragraph
|
||||||
|
[ Font.size Consts.homeFontSize
|
||||||
|
, Font.bold
|
||||||
|
, contentPadding
|
||||||
|
]
|
||||||
|
[ Element.text video.name ]
|
||||||
|
, case video.date of
|
||||||
|
Just date ->
|
||||||
|
Element.paragraph
|
||||||
|
[ contentPadding, Font.size Consts.titleFontSize ]
|
||||||
|
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Element.none
|
||||||
|
]
|
||||||
|
, Element.column
|
||||||
|
[ contentPadding
|
||||||
|
, Element.alignTop
|
||||||
|
, Element.spacing 10
|
||||||
|
, Element.width (Element.fillPortion 1)
|
||||||
|
]
|
||||||
|
(List.map (videoInList zone playlist video) playlist.videos)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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 ]
|
||||||
|
, case video.date of
|
||||||
|
Just date ->
|
||||||
|
Element.paragraph
|
||||||
|
[ Font.color Colors.greyFont
|
||||||
|
]
|
||||||
|
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Element.none
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
formatTime : Int -> String
|
||||||
|
formatTime time =
|
||||||
|
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
|
||||||
|
++ ":"
|
||||||
|
++ minutesString
|
||||||
|
++ ":"
|
||||||
|
++ secondsString
|
||||||
|
|
||||||
|
|
||||||
|
toHours : Int -> Int
|
||||||
|
toHours i =
|
||||||
|
i // 3600
|
||||||
|
|
||||||
|
|
||||||
|
toMinutes : Int -> Int
|
||||||
|
toMinutes i =
|
||||||
|
modBy 3600 i // 60
|
||||||
|
|
||||||
|
|
||||||
|
toSeconds : Int -> Int
|
||||||
|
toSeconds i =
|
||||||
|
modBy 60 i
|
||||||
|
|
||||||
|
|
||||||
|
numberOfVideosPerRow : Element.Device -> Int
|
||||||
|
numberOfVideosPerRow device =
|
||||||
|
case device.class of
|
||||||
|
Element.Phone ->
|
||||||
|
1
|
||||||
|
|
||||||
|
Element.Tablet ->
|
||||||
|
3
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
4
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
List.reverse acc
|
||||||
|
|
||||||
|
else
|
||||||
|
let
|
||||||
|
groupHead =
|
||||||
|
List.take size list
|
||||||
|
|
||||||
|
groupTail =
|
||||||
|
List.drop size list
|
||||||
|
in
|
||||||
|
groupAux size groupTail (groupHead :: acc)
|
||||||
|
|
|
@ -1,427 +0,0 @@
|
||||||
module Views.Desktop exposing (view)
|
|
||||||
|
|
||||||
import Colors
|
|
||||||
import Consts
|
|
||||||
import Core
|
|
||||||
import Element exposing (Element)
|
|
||||||
import Element.Background as Background
|
|
||||||
import Element.Border as Border
|
|
||||||
import Element.Font as Font
|
|
||||||
import Element.Input as Input
|
|
||||||
import Element.Keyed as Keyed
|
|
||||||
import Html
|
|
||||||
import Html.Attributes
|
|
||||||
import Json.Encode as Encode
|
|
||||||
import Time
|
|
||||||
import TimeUtils
|
|
||||||
import Twitch
|
|
||||||
|
|
||||||
|
|
||||||
view : Core.FullModel -> Element Core.Msg
|
|
||||||
view model =
|
|
||||||
let
|
|
||||||
content =
|
|
||||||
case model of
|
|
||||||
Core.Unloaded _ _ _ ->
|
|
||||||
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
|
|
||||||
|
|
||||||
Core.Playlist playlist ->
|
|
||||||
videoMiniaturesView model.zone playlist
|
|
||||||
|
|
||||||
Core.Video playlist video ->
|
|
||||||
videoView model.zone playlist video
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
, onPress = Just Core.HomeClicked
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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 =
|
|
||||||
Element.image
|
|
||||||
[ Element.width Element.fill
|
|
||||||
, Element.height Element.fill
|
|
||||||
, Element.inFront inFront
|
|
||||||
]
|
|
||||||
{ description = "", src = Twitch.playlistMiniatureUrl playlist }
|
|
||||||
|
|
||||||
length =
|
|
||||||
List.length playlist.videos
|
|
||||||
|
|
||||||
label =
|
|
||||||
String.fromInt length
|
|
||||||
++ " video"
|
|
||||||
++ (if length > 1 then
|
|
||||||
"s"
|
|
||||||
|
|
||||||
else
|
|
||||||
""
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
]
|
|
||||||
|
|
||||||
display =
|
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
|
||||||
[ image
|
|
||||||
, Element.paragraph
|
|
||||||
[ Font.bold, Font.size Consts.titleFontSize ]
|
|
||||||
[ Element.text playlist.name ]
|
|
||||||
]
|
|
||||||
|
|
||||||
button =
|
|
||||||
Input.button [ Element.width Element.fill, Element.alignTop ]
|
|
||||||
{ label = display
|
|
||||||
, onPress = Just (Core.PlaylistClicked playlist)
|
|
||||||
}
|
|
||||||
in
|
|
||||||
button
|
|
||||||
|
|
||||||
|
|
||||||
videoMiniaturesView : Time.Zone -> Twitch.Playlist -> Element Core.Msg
|
|
||||||
videoMiniaturesView zone playlist =
|
|
||||||
let
|
|
||||||
empty =
|
|
||||||
Element.el [ Element.width Element.fill ] Element.none
|
|
||||||
|
|
||||||
views =
|
|
||||||
List.map (videoMiniatureView zone playlist) playlist.videos
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
videoMiniature : Twitch.Video -> Element Core.Msg
|
|
||||||
videoMiniature video =
|
|
||||||
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
|
|
||||||
]
|
|
||||||
|
|
||||||
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
|
|
||||||
display =
|
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
|
||||||
[ videoMiniature video
|
|
||||||
, videoDescription zone video
|
|
||||||
]
|
|
||||||
|
|
||||||
button =
|
|
||||||
Input.button [ Element.width Element.fill, Element.alignTop ]
|
|
||||||
{ label = display
|
|
||||||
, onPress = Just (Core.VideoClicked playlist video)
|
|
||||||
}
|
|
||||||
in
|
|
||||||
button
|
|
||||||
|
|
||||||
|
|
||||||
videoInList : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg
|
|
||||||
videoInList zone playlist activeVideo video =
|
|
||||||
let
|
|
||||||
( msg, attr ) =
|
|
||||||
if video == activeVideo then
|
|
||||||
( Nothing
|
|
||||||
, [ Element.width Element.fill
|
|
||||||
, Background.color Colors.selected
|
|
||||||
, Border.color Colors.primary
|
|
||||||
, Border.width 2
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
else
|
|
||||||
( Just (Core.VideoClicked playlist video), [ Element.width Element.fill ] )
|
|
||||||
|
|
||||||
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
|
|
||||||
Input.button attr { label = label, onPress = msg }
|
|
||||||
|
|
||||||
|
|
||||||
videoView : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
|
||||||
videoView zone playlist video =
|
|
||||||
Element.row [ Element.padding 10, Element.width Element.fill, Element.spacing 10 ]
|
|
||||||
[ Element.column
|
|
||||||
[ Element.width (Element.fillPortion 2)
|
|
||||||
, Element.spacing 10
|
|
||||||
, Element.alignTop
|
|
||||||
]
|
|
||||||
[ Keyed.el
|
|
||||||
[ Element.width Element.fill
|
|
||||||
, Element.height (Element.px 0)
|
|
||||||
, Element.htmlAttribute (Html.Attributes.style "padding-top" "56.25%")
|
|
||||||
, Element.htmlAttribute (Html.Attributes.style "position" "relative")
|
|
||||||
]
|
|
||||||
( video.url
|
|
||||||
, Element.html
|
|
||||||
(Html.video
|
|
||||||
[ Html.Attributes.id (Twitch.videoId video)
|
|
||||||
, Html.Attributes.class "video-js"
|
|
||||||
, Html.Attributes.class "vjs-default-skin"
|
|
||||||
, Html.Attributes.class "wf"
|
|
||||||
, Html.Attributes.property "data-setup" (Encode.string "{\"fluid\": true}")
|
|
||||||
, Html.Attributes.style "position" "absolute"
|
|
||||||
, Html.Attributes.style "top" "0"
|
|
||||||
, Html.Attributes.style "height" "100%"
|
|
||||||
, Html.Attributes.controls True
|
|
||||||
, Html.Attributes.autoplay True
|
|
||||||
]
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
, Element.paragraph
|
|
||||||
[ Font.size Consts.homeFontSize
|
|
||||||
, Font.bold
|
|
||||||
, Element.paddingEach { top = 10, left = 0, bottom = 0, right = 0 }
|
|
||||||
]
|
|
||||||
[ Element.text video.name ]
|
|
||||||
, case video.date of
|
|
||||||
Just date ->
|
|
||||||
Element.paragraph
|
|
||||||
[ Font.size Consts.titleFontSize ]
|
|
||||||
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Element.none
|
|
||||||
]
|
|
||||||
, Element.column [ Element.alignTop, Element.spacing 10, Element.width (Element.fillPortion 1) ]
|
|
||||||
(List.map (videoInList zone playlist video) playlist.videos)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
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 ]
|
|
||||||
, case video.date of
|
|
||||||
Just date ->
|
|
||||||
Element.paragraph
|
|
||||||
[ Font.color Colors.greyFont
|
|
||||||
]
|
|
||||||
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Element.none
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
formatTime : Int -> String
|
|
||||||
formatTime time =
|
|
||||||
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
|
|
||||||
++ ":"
|
|
||||||
++ minutesString
|
|
||||||
++ ":"
|
|
||||||
++ secondsString
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
List.reverse acc
|
|
||||||
|
|
||||||
else
|
|
||||||
let
|
|
||||||
groupHead =
|
|
||||||
List.take size list
|
|
||||||
|
|
||||||
groupTail =
|
|
||||||
List.drop size list
|
|
||||||
in
|
|
||||||
groupAux size groupTail (groupHead :: acc)
|
|
|
@ -1,427 +0,0 @@
|
||||||
module Views.Mobile exposing (view)
|
|
||||||
|
|
||||||
import Colors
|
|
||||||
import Consts
|
|
||||||
import Core
|
|
||||||
import Element exposing (Element)
|
|
||||||
import Element.Background as Background
|
|
||||||
import Element.Border as Border
|
|
||||||
import Element.Font as Font
|
|
||||||
import Element.Input as Input
|
|
||||||
import Element.Keyed as Keyed
|
|
||||||
import Html
|
|
||||||
import Html.Attributes
|
|
||||||
import Json.Encode as Encode
|
|
||||||
import Time
|
|
||||||
import TimeUtils
|
|
||||||
import Twitch
|
|
||||||
|
|
||||||
|
|
||||||
view : Core.FullModel -> Element Core.Msg
|
|
||||||
view model =
|
|
||||||
let
|
|
||||||
content =
|
|
||||||
case model of
|
|
||||||
Core.Unloaded _ _ ->
|
|
||||||
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
|
|
||||||
|
|
||||||
Core.Playlist playlist ->
|
|
||||||
videoMiniaturesView model.zone playlist
|
|
||||||
|
|
||||||
Core.Video playlist video ->
|
|
||||||
videoView model.zone playlist video
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
, onPress = Just Core.HomeClicked
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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 =
|
|
||||||
Element.image
|
|
||||||
[ Element.width Element.fill
|
|
||||||
, Element.height Element.fill
|
|
||||||
, Element.inFront inFront
|
|
||||||
]
|
|
||||||
{ description = "", src = Twitch.playlistMiniatureUrl playlist }
|
|
||||||
|
|
||||||
length =
|
|
||||||
List.length playlist.videos
|
|
||||||
|
|
||||||
label =
|
|
||||||
String.fromInt length
|
|
||||||
++ " video"
|
|
||||||
++ (if length > 1 then
|
|
||||||
"s"
|
|
||||||
|
|
||||||
else
|
|
||||||
""
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
]
|
|
||||||
|
|
||||||
display =
|
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
|
||||||
[ image
|
|
||||||
, Element.paragraph
|
|
||||||
[ Font.bold, Font.size Consts.titleFontSize ]
|
|
||||||
[ Element.text playlist.name ]
|
|
||||||
]
|
|
||||||
|
|
||||||
button =
|
|
||||||
Input.button [ Element.width Element.fill, Element.alignTop ]
|
|
||||||
{ label = display
|
|
||||||
, onPress = Just (Core.PlaylistClicked playlist)
|
|
||||||
}
|
|
||||||
in
|
|
||||||
button
|
|
||||||
|
|
||||||
|
|
||||||
videoMiniaturesView : Time.Zone -> Twitch.Playlist -> Element Core.Msg
|
|
||||||
videoMiniaturesView zone playlist =
|
|
||||||
let
|
|
||||||
empty =
|
|
||||||
Element.el [ Element.width Element.fill ] Element.none
|
|
||||||
|
|
||||||
views =
|
|
||||||
List.map (videoMiniatureView zone playlist) playlist.videos
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
videoMiniature : Twitch.Video -> Element Core.Msg
|
|
||||||
videoMiniature video =
|
|
||||||
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
|
|
||||||
]
|
|
||||||
|
|
||||||
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
|
|
||||||
display =
|
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
|
||||||
[ videoMiniature video
|
|
||||||
, videoDescription zone video
|
|
||||||
]
|
|
||||||
|
|
||||||
button =
|
|
||||||
Input.button [ Element.width Element.fill, Element.alignTop ]
|
|
||||||
{ label = display
|
|
||||||
, onPress = Just (Core.VideoClicked playlist video)
|
|
||||||
}
|
|
||||||
in
|
|
||||||
button
|
|
||||||
|
|
||||||
|
|
||||||
videoInList : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg
|
|
||||||
videoInList zone playlist activeVideo video =
|
|
||||||
let
|
|
||||||
( msg, attr ) =
|
|
||||||
if video == activeVideo then
|
|
||||||
( Nothing
|
|
||||||
, [ Element.width Element.fill
|
|
||||||
, Background.color Colors.selected
|
|
||||||
, Border.color Colors.primary
|
|
||||||
, Border.width 2
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
else
|
|
||||||
( Just (Core.VideoClicked playlist video), [ Element.width Element.fill ] )
|
|
||||||
|
|
||||||
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
|
|
||||||
Input.button attr { label = label, onPress = msg }
|
|
||||||
|
|
||||||
|
|
||||||
videoView : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
|
||||||
videoView zone playlist video =
|
|
||||||
Element.row [ Element.padding 10, Element.width Element.fill, Element.spacing 10 ]
|
|
||||||
[ Element.column
|
|
||||||
[ Element.width (Element.fillPortion 2)
|
|
||||||
, Element.spacing 10
|
|
||||||
, Element.alignTop
|
|
||||||
]
|
|
||||||
[ Keyed.el
|
|
||||||
[ Element.width Element.fill
|
|
||||||
, Element.height (Element.px 0)
|
|
||||||
, Element.htmlAttribute (Html.Attributes.style "padding-top" "56.25%")
|
|
||||||
, Element.htmlAttribute (Html.Attributes.style "position" "relative")
|
|
||||||
]
|
|
||||||
( video.url
|
|
||||||
, Element.html
|
|
||||||
(Html.video
|
|
||||||
[ Html.Attributes.id (Twitch.videoId video)
|
|
||||||
, Html.Attributes.class "video-js"
|
|
||||||
, Html.Attributes.class "vjs-default-skin"
|
|
||||||
, Html.Attributes.class "wf"
|
|
||||||
, Html.Attributes.property "data-setup" (Encode.string "{\"fluid\": true}")
|
|
||||||
, Html.Attributes.style "position" "absolute"
|
|
||||||
, Html.Attributes.style "top" "0"
|
|
||||||
, Html.Attributes.style "height" "100%"
|
|
||||||
, Html.Attributes.controls True
|
|
||||||
, Html.Attributes.autoplay True
|
|
||||||
]
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
, Element.paragraph
|
|
||||||
[ Font.size Consts.homeFontSize
|
|
||||||
, Font.bold
|
|
||||||
, Element.paddingEach { top = 10, left = 0, bottom = 0, right = 0 }
|
|
||||||
]
|
|
||||||
[ Element.text video.name ]
|
|
||||||
, case video.date of
|
|
||||||
Just date ->
|
|
||||||
Element.paragraph
|
|
||||||
[ Font.size Consts.titleFontSize ]
|
|
||||||
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Element.none
|
|
||||||
]
|
|
||||||
, Element.column [ Element.alignTop, Element.spacing 10, Element.width (Element.fillPortion 1) ]
|
|
||||||
(List.map (videoInList zone playlist video) playlist.videos)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
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 ]
|
|
||||||
, case video.date of
|
|
||||||
Just date ->
|
|
||||||
Element.paragraph
|
|
||||||
[ Font.color Colors.greyFont
|
|
||||||
]
|
|
||||||
[ Element.text ("Diffusé le " ++ formatDate zone date) ]
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Element.none
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
formatTime : Int -> String
|
|
||||||
formatTime time =
|
|
||||||
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
|
|
||||||
++ ":"
|
|
||||||
++ minutesString
|
|
||||||
++ ":"
|
|
||||||
++ secondsString
|
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
List.reverse acc
|
|
||||||
|
|
||||||
else
|
|
||||||
let
|
|
||||||
groupHead =
|
|
||||||
List.take size list
|
|
||||||
|
|
||||||
groupTail =
|
|
||||||
List.drop size list
|
|
||||||
in
|
|
||||||
groupAux size groupTail (groupHead :: acc)
|
|
Loading…
Reference in New Issue