diff --git a/src/Views.elm b/src/Views.elm index 73027cb..d5f0944 100644 --- a/src/Views.elm +++ b/src/Views.elm @@ -4,13 +4,31 @@ import Browser import Colors import Consts import Core -import Element +import Element exposing (Element) +import Element.Background as Background +import Element.Border as Border 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 model = + let + element = + case model of + Core.Unloaded _ _ _ -> + Element.none + + Core.Loaded m -> + viewContent m + in { title = title model , body = [ Element.layout @@ -18,7 +36,10 @@ view model = , Font.size Consts.normalFontSize , 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 -> 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) diff --git a/src/Views/Desktop.elm b/src/Views/Desktop.elm deleted file mode 100644 index 014e7d3..0000000 --- a/src/Views/Desktop.elm +++ /dev/null @@ -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) diff --git a/src/Views/Mobile.elm b/src/Views/Mobile.elm deleted file mode 100644 index ca8db30..0000000 --- a/src/Views/Mobile.elm +++ /dev/null @@ -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)