Browsing the pages work
This commit is contained in:
parent
0215d94be9
commit
5872e50493
3
elm.json
3
elm.json
|
@ -15,7 +15,8 @@
|
||||||
"elm/time": "1.0.0",
|
"elm/time": "1.0.0",
|
||||||
"elm/url": "1.0.0",
|
"elm/url": "1.0.0",
|
||||||
"jims/html-parser": "1.0.0",
|
"jims/html-parser": "1.0.0",
|
||||||
"mdgriffith/elm-ui": "1.1.8"
|
"mdgriffith/elm-ui": "1.1.8",
|
||||||
|
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
|
||||||
},
|
},
|
||||||
"indirect": {
|
"indirect": {
|
||||||
"elm/bytes": "1.0.8",
|
"elm/bytes": "1.0.8",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
module Colors exposing (blackFont, greyBackground, primary, primaryOver, white)
|
module Colors exposing (blackFont, greyBackground, greyFont, primary, primaryOver, white)
|
||||||
|
|
||||||
import Element
|
import Element
|
||||||
|
|
||||||
|
@ -26,3 +26,8 @@ greyBackground =
|
||||||
blackFont : Element.Color
|
blackFont : Element.Color
|
||||||
blackFont =
|
blackFont =
|
||||||
Element.rgb255 54 54 54
|
Element.rgb255 54 54 54
|
||||||
|
|
||||||
|
|
||||||
|
greyFont : Element.Color
|
||||||
|
greyFont =
|
||||||
|
Element.rgb255 128 128 128
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
module Consts exposing (homeFontSize, homePadding, name, url)
|
module Consts exposing
|
||||||
|
( homeFontSize
|
||||||
|
, homePadding
|
||||||
|
, name
|
||||||
|
, normalFontSize
|
||||||
|
, titleFontSize
|
||||||
|
, url
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
url : String
|
url : String
|
||||||
|
@ -11,6 +18,16 @@ name =
|
||||||
url
|
url
|
||||||
|
|
||||||
|
|
||||||
|
normalFontSize : Int
|
||||||
|
normalFontSize =
|
||||||
|
13
|
||||||
|
|
||||||
|
|
||||||
|
titleFontSize : Int
|
||||||
|
titleFontSize =
|
||||||
|
15
|
||||||
|
|
||||||
|
|
||||||
homeFontSize : Int
|
homeFontSize : Int
|
||||||
homeFontSize =
|
homeFontSize =
|
||||||
25
|
25
|
||||||
|
|
20
src/Core.elm
20
src/Core.elm
|
@ -3,6 +3,7 @@ module Core exposing (FullModel(..), Model, Msg(..), Page(..), init, update)
|
||||||
import Browser.Navigation
|
import Browser.Navigation
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
import Task
|
import Task
|
||||||
|
import Time
|
||||||
import Twitch
|
import Twitch
|
||||||
import Url
|
import Url
|
||||||
|
|
||||||
|
@ -14,6 +15,7 @@ type FullModel
|
||||||
|
|
||||||
type alias Model =
|
type alias Model =
|
||||||
{ playlists : List Twitch.Playlist
|
{ playlists : List Twitch.Playlist
|
||||||
|
, zone : Time.Zone
|
||||||
, page : Page
|
, page : Page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,7 +28,7 @@ type Page
|
||||||
|
|
||||||
type Msg
|
type Msg
|
||||||
= Noop
|
= Noop
|
||||||
| PlaylistsReceived (List Twitch.Playlist)
|
| PlaylistsReceived ( List Twitch.Playlist, Time.Zone )
|
||||||
| HomeClicked
|
| HomeClicked
|
||||||
| PlaylistClicked Twitch.Playlist
|
| PlaylistClicked Twitch.Playlist
|
||||||
| VideoClicked Twitch.Playlist Twitch.Video
|
| VideoClicked Twitch.Playlist Twitch.Video
|
||||||
|
@ -45,17 +47,17 @@ update msg model =
|
||||||
( Noop, _ ) ->
|
( Noop, _ ) ->
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
||||||
( PlaylistsReceived playlists, _ ) ->
|
( PlaylistsReceived ( playlists, zone ), _ ) ->
|
||||||
( Loaded { playlists = playlists, page = Home }, Cmd.none )
|
( Loaded { playlists = playlists, zone = zone, page = Home }, Cmd.none )
|
||||||
|
|
||||||
( HomeClicked, Loaded { playlists } ) ->
|
( HomeClicked, Loaded m ) ->
|
||||||
( Loaded { playlists = playlists, page = Home }, Cmd.none )
|
( Loaded { m | page = Home }, Cmd.none )
|
||||||
|
|
||||||
( PlaylistClicked playlist, Loaded { playlists } ) ->
|
( PlaylistClicked playlist, Loaded m ) ->
|
||||||
( Loaded { playlists = playlists, page = Playlist playlist }, Cmd.none )
|
( Loaded { m | page = Playlist playlist }, Cmd.none )
|
||||||
|
|
||||||
( VideoClicked playlist video, Loaded { playlists } ) ->
|
( VideoClicked playlist video, Loaded m ) ->
|
||||||
( Loaded { playlists = playlists, page = Video playlist video }, Cmd.none )
|
( Loaded { m | page = Video playlist video }, Cmd.none )
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
( model, Cmd.none )
|
( model, Cmd.none )
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
module TimeUtils exposing (monthToString, pad2)
|
||||||
|
|
||||||
|
import Time
|
||||||
|
|
||||||
|
|
||||||
|
pad2 : String -> String
|
||||||
|
pad2 input =
|
||||||
|
if String.length input == 1 then
|
||||||
|
"0" ++ input
|
||||||
|
|
||||||
|
else
|
||||||
|
input
|
||||||
|
|
||||||
|
|
||||||
|
monthToString : Time.Month -> String
|
||||||
|
monthToString month =
|
||||||
|
case month of
|
||||||
|
Time.Jan ->
|
||||||
|
"01"
|
||||||
|
|
||||||
|
Time.Feb ->
|
||||||
|
"02"
|
||||||
|
|
||||||
|
Time.Mar ->
|
||||||
|
"03"
|
||||||
|
|
||||||
|
Time.Apr ->
|
||||||
|
"04"
|
||||||
|
|
||||||
|
Time.May ->
|
||||||
|
"05"
|
||||||
|
|
||||||
|
Time.Jun ->
|
||||||
|
"06"
|
||||||
|
|
||||||
|
Time.Jul ->
|
||||||
|
"07"
|
||||||
|
|
||||||
|
Time.Aug ->
|
||||||
|
"08"
|
||||||
|
|
||||||
|
Time.Sep ->
|
||||||
|
"09"
|
||||||
|
|
||||||
|
Time.Oct ->
|
||||||
|
"10"
|
||||||
|
|
||||||
|
Time.Nov ->
|
||||||
|
"11"
|
||||||
|
|
||||||
|
Time.Dec ->
|
||||||
|
"12"
|
|
@ -8,8 +8,10 @@ module Twitch exposing
|
||||||
|
|
||||||
import Html.Parser
|
import Html.Parser
|
||||||
import Http
|
import Http
|
||||||
|
import Iso8601
|
||||||
import Json.Decode as Decode
|
import Json.Decode as Decode
|
||||||
import Task exposing (Task)
|
import Task exposing (Task)
|
||||||
|
import Time
|
||||||
|
|
||||||
|
|
||||||
type alias Playlist =
|
type alias Playlist =
|
||||||
|
@ -23,6 +25,7 @@ type alias Video =
|
||||||
{ name : String
|
{ name : String
|
||||||
, url : String
|
, url : String
|
||||||
, duration : Int
|
, duration : Int
|
||||||
|
, date : Time.Posix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,9 +66,17 @@ sortPlaylists playlists =
|
||||||
List.sortBy .url (List.map sortPlaylist playlists) |> List.reverse
|
List.sortBy .url (List.map sortPlaylist playlists) |> List.reverse
|
||||||
|
|
||||||
|
|
||||||
fetchPlaylists : Task x (List Playlist)
|
fetchPlaylists : Task x ( List Playlist, Time.Zone )
|
||||||
fetchPlaylists =
|
fetchPlaylists =
|
||||||
fetchPlaylistPath |> Task.andThen fetchPlaylistsMapper |> Task.map sortPlaylists
|
fetchPlaylistPath
|
||||||
|
|> Task.andThen fetchPlaylistsMapper
|
||||||
|
|> Task.map sortPlaylists
|
||||||
|
|> Task.andThen fetchTimezone
|
||||||
|
|
||||||
|
|
||||||
|
fetchTimezone : List Playlist -> Task x ( List Playlist, Time.Zone )
|
||||||
|
fetchTimezone playlists =
|
||||||
|
Task.map (\zone -> ( playlists, zone )) Time.here
|
||||||
|
|
||||||
|
|
||||||
fetchPlaylistPath : Task x (List String)
|
fetchPlaylistPath : Task x (List String)
|
||||||
|
@ -129,10 +140,10 @@ parseVideo url result =
|
||||||
Ok v
|
Ok v
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Ok { name = "", url = url, duration = 0 }
|
Ok { name = "", url = url, duration = 0, date = Time.millisToPosix 0 }
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Ok { name = "", url = url, duration = 0 }
|
Ok { name = "", url = url, duration = 0, date = Time.millisToPosix 0 }
|
||||||
|
|
||||||
|
|
||||||
fetchPlaylistsMapper : List String -> Task x (List Playlist)
|
fetchPlaylistsMapper : List String -> Task x (List Playlist)
|
||||||
|
@ -221,6 +232,7 @@ decodePlaylistName =
|
||||||
|
|
||||||
decodeVideo : String -> Decode.Decoder Video
|
decodeVideo : String -> Decode.Decoder Video
|
||||||
decodeVideo url =
|
decodeVideo url =
|
||||||
Decode.map2 (\x y -> Video x url y)
|
Decode.map3 (\x y -> Video x url y)
|
||||||
(Decode.field "title" Decode.string)
|
(Decode.field "title" Decode.string)
|
||||||
(Decode.map Basics.round (Decode.field "duration" Decode.float))
|
(Decode.map Basics.round (Decode.field "duration" Decode.float))
|
||||||
|
(Decode.field "date" Iso8601.decoder)
|
||||||
|
|
128
src/Views.elm
128
src/Views.elm
|
@ -9,13 +9,18 @@ import Element.Background as Background
|
||||||
import Element.Border as Border
|
import Element.Border as Border
|
||||||
import Element.Font as Font
|
import Element.Font as Font
|
||||||
import Element.Input as Input
|
import Element.Input as Input
|
||||||
|
import Time
|
||||||
|
import TimeUtils
|
||||||
import Twitch
|
import Twitch
|
||||||
|
|
||||||
|
|
||||||
view : Core.FullModel -> Browser.Document Core.Msg
|
view : Core.FullModel -> Browser.Document Core.Msg
|
||||||
view model =
|
view model =
|
||||||
{ title = Consts.url
|
{ title = Consts.url
|
||||||
, body = [ Element.layout [] (viewContent model) ]
|
, body =
|
||||||
|
[ Element.layout [ Font.color Colors.blackFont, Font.size Consts.normalFontSize ]
|
||||||
|
(viewContent model)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,10 +45,10 @@ mainView model =
|
||||||
playlistsView model.playlists
|
playlistsView model.playlists
|
||||||
|
|
||||||
Core.Playlist playlist ->
|
Core.Playlist playlist ->
|
||||||
videoMiniaturesView playlist
|
videoMiniaturesView model.zone playlist
|
||||||
|
|
||||||
Core.Video playlist video ->
|
Core.Video playlist video ->
|
||||||
videoView playlist video
|
videoView model.zone playlist video
|
||||||
|
|
||||||
|
|
||||||
topBar : Element Core.Msg
|
topBar : Element Core.Msg
|
||||||
|
@ -135,7 +140,7 @@ playlistView playlist =
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
[ image
|
[ image
|
||||||
, Element.paragraph
|
, Element.paragraph
|
||||||
[ Font.bold, Font.color Colors.blackFont ]
|
[ Font.bold, Font.size Consts.titleFontSize ]
|
||||||
[ Element.text playlist.name ]
|
[ Element.text playlist.name ]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -148,14 +153,14 @@ playlistView playlist =
|
||||||
button
|
button
|
||||||
|
|
||||||
|
|
||||||
videoMiniaturesView : Twitch.Playlist -> Element Core.Msg
|
videoMiniaturesView : Time.Zone -> Twitch.Playlist -> Element Core.Msg
|
||||||
videoMiniaturesView playlist =
|
videoMiniaturesView zone playlist =
|
||||||
let
|
let
|
||||||
empty =
|
empty =
|
||||||
Element.el [ Element.width Element.fill ] Element.none
|
Element.el [ Element.width Element.fill ] Element.none
|
||||||
|
|
||||||
views =
|
views =
|
||||||
List.map (videoMiniatureView playlist) playlist.videos
|
List.map (videoMiniatureView zone playlist) playlist.videos
|
||||||
|
|
||||||
grouped =
|
grouped =
|
||||||
group 4 views
|
group 4 views
|
||||||
|
@ -171,20 +176,9 @@ videoMiniaturesView playlist =
|
||||||
final
|
final
|
||||||
|
|
||||||
|
|
||||||
videoMiniatureView : Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
videoMiniature : Twitch.Video -> Element Core.Msg
|
||||||
videoMiniatureView playlist video =
|
videoMiniature video =
|
||||||
let
|
let
|
||||||
image =
|
|
||||||
Element.image
|
|
||||||
[ Element.width Element.fill
|
|
||||||
, Element.height Element.fill
|
|
||||||
, Element.inFront inFront
|
|
||||||
]
|
|
||||||
{ description = "", src = Twitch.videoMiniatureUrl video }
|
|
||||||
|
|
||||||
label =
|
|
||||||
formatTime video.duration
|
|
||||||
|
|
||||||
inFront =
|
inFront =
|
||||||
Element.text label
|
Element.text label
|
||||||
|> Element.el
|
|> Element.el
|
||||||
|
@ -199,12 +193,27 @@ videoMiniatureView playlist video =
|
||||||
, Element.padding 5
|
, 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 =
|
display =
|
||||||
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
Element.column [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
[ image
|
[ videoMiniature video
|
||||||
, Element.paragraph
|
, videoDescription zone video
|
||||||
[ Font.bold, Font.color Colors.blackFont ]
|
|
||||||
[ Element.text video.name ]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
button =
|
button =
|
||||||
|
@ -216,17 +225,72 @@ videoMiniatureView playlist video =
|
||||||
button
|
button
|
||||||
|
|
||||||
|
|
||||||
videoView : Twitch.Playlist -> Twitch.Video -> Element Core.Msg
|
videoInList : Time.Zone -> Twitch.Video -> Element Core.Msg
|
||||||
videoView playlist video =
|
videoInList zone video =
|
||||||
Element.row [ Element.padding 10, Element.width Element.fill, Element.spacing 20 ]
|
Element.row [ Element.width Element.fill, Element.spacing 10 ]
|
||||||
[ Element.image [ Element.width (Element.fillPortion 2) ]
|
[ Element.el [ Element.width (Element.fillPortion 1) ]
|
||||||
{ description = "", src = Twitch.videoMiniatureUrl video }
|
(videoMiniature video)
|
||||||
, Element.column [ Element.alignTop, Element.width (Element.fillPortion 1) ]
|
, Element.el [ Element.width (Element.fillPortion 2), Element.paddingXY 0 10, Element.alignTop ]
|
||||||
[ Element.text "sup"
|
(videoDescription zone video)
|
||||||
]
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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 20 ]
|
||||||
|
[ Element.column [ Element.spacing 10, Element.width (Element.fillPortion 2) ]
|
||||||
|
[ Element.image [ Element.width Element.fill ]
|
||||||
|
{ description = "", src = Twitch.videoMiniatureUrl video }
|
||||||
|
, Element.paragraph
|
||||||
|
[ Font.size Consts.homeFontSize
|
||||||
|
, Font.bold
|
||||||
|
]
|
||||||
|
[ 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) ]
|
||||||
|
(List.map (videoInList zone) 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 ]
|
||||||
|
, Element.paragraph
|
||||||
|
[ Font.color Colors.greyFont
|
||||||
|
]
|
||||||
|
[ Element.text ("Diffusé le " ++ formatDate zone video.date) ]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
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 : Int -> String
|
||||||
formatTime time =
|
formatTime time =
|
||||||
let
|
let
|
||||||
|
|
Loading…
Reference in New Issue