From 948df5da9a09b68b6c73ceefe27760baae345c26 Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Fri, 16 Oct 2020 12:03:07 +0200 Subject: [PATCH] Change miniatures when hover --- src/Core.elm | 68 +++++++++++++++++++++++++++++++++------ src/Hover.elm | 14 ++++++++ src/Twitch.elm | 50 +++++++++++++++++++++++++---- src/Views.elm | 86 ++++++++++++++++++++++++++++++-------------------- 4 files changed, 169 insertions(+), 49 deletions(-) create mode 100644 src/Hover.elm diff --git a/src/Core.elm b/src/Core.elm index 7e19c5f..9cbfecc 100644 --- a/src/Core.elm +++ b/src/Core.elm @@ -5,6 +5,7 @@ import Browser.Events as Events import Browser.Navigation as Nav import Dict exposing (Dict) import Element +import Hover exposing (Hover) import Http import Ports import Task @@ -24,13 +25,14 @@ type alias Model = , page : Page , key : Nav.Key , device : Element.Device + , time : Time.Posix } type Page - = Home - | Playlist Twitch.Playlist - | Video Twitch.Playlist Twitch.Video + = Home (Maybe (Hover Twitch.Playlist)) + | Playlist Twitch.Playlist (Maybe (Hover Twitch.Video)) + | Video Twitch.Playlist Twitch.Video (Maybe (Hover Twitch.Video)) type Msg @@ -43,6 +45,10 @@ type Msg | UrlRequested Browser.UrlRequest | SizeReceived Int Int | TimeZoneReceived Time.Zone + | HoverPlaylist Twitch.Playlist + | HoverVideo Twitch.Video + | Unhover + | TimeReceived Time.Posix init : { width : Int, height : Int } -> Url.Url -> Nav.Key -> ( FullModel, Cmd Msg ) @@ -67,7 +73,10 @@ resultToMsg result = subscriptions : FullModel -> Sub Msg subscriptions _ = - Events.onResize (\w h -> SizeReceived w h) + Sub.batch + [ Events.onResize (\w h -> SizeReceived w h) + , Time.every 200 TimeReceived + ] update : Msg -> FullModel -> ( FullModel, Cmd Msg ) @@ -79,6 +88,39 @@ update msg model = ( TimeZoneReceived z, Unloaded d u k _ ) -> ( Unloaded d u k z, Cmd.none ) + ( TimeReceived p, Loaded m ) -> + ( Loaded { m | time = p }, Cmd.none ) + + ( HoverPlaylist hover, Loaded m ) -> + case m.page of + Home Nothing -> + ( Loaded { m | page = Home (Just (Hover.hover hover m.time)) }, Cmd.none ) + + _ -> + ( Loaded m, Cmd.none ) + + ( HoverVideo hover, Loaded m ) -> + case m.page of + Playlist p Nothing -> + ( Loaded { m | page = Playlist p (Just (Hover.hover hover m.time)) }, Cmd.none ) + + Video p v Nothing -> + ( Loaded { m | page = Video p v (Just (Hover.hover v m.time)) }, Cmd.none ) + + _ -> + ( Loaded m, Cmd.none ) + + ( Unhover, Loaded m ) -> + case m.page of + Home _ -> + ( Loaded { m | page = Home Nothing }, Cmd.none ) + + Playlist p _ -> + ( Loaded { m | page = Playlist p Nothing }, Cmd.none ) + + Video p v _ -> + ( Loaded { m | page = Video p v Nothing }, Cmd.none ) + ( TimeZoneReceived z, Loaded m ) -> ( Loaded { m | zone = z }, Cmd.none ) @@ -90,7 +132,15 @@ update msg model = ( PlaylistsReceived playlists, Unloaded device url key zone ) -> update (UrlReceived url) - (Loaded { key = key, playlists = playlists, zone = zone, page = Home, device = device }) + (Loaded + { key = key + , playlists = playlists + , zone = zone + , page = Home Nothing + , device = device + , time = Time.millisToPosix 0 + } + ) ( HomeClicked, Loaded m ) -> ( model @@ -159,19 +209,19 @@ update msg model = ( page, cmd ) = case ( playlist, video ) of ( Just p, Just v ) -> - ( Video p v + ( Video p v Nothing , Ports.registerVideo ( Twitch.videoId v, "videos/" ++ p.url ++ v.url, time ) ) ( Just p, Nothing ) -> - ( Playlist p, Cmd.none ) + ( Playlist p Nothing, Cmd.none ) _ -> - ( Home, Cmd.none ) + ( Home Nothing, Cmd.none ) extraCmd = case page of - Video _ _ -> + Video _ _ _ -> Cmd.none _ -> diff --git a/src/Hover.elm b/src/Hover.elm new file mode 100644 index 0000000..382185f --- /dev/null +++ b/src/Hover.elm @@ -0,0 +1,14 @@ +module Hover exposing (..) + +import Time + + +type alias Hover a = + { element : a + , time : Time.Posix + } + + +hover : a -> Time.Posix -> Hover a +hover element time = + { element = element, time = time } diff --git a/src/Twitch.elm b/src/Twitch.elm index 9574457..8fb9a38 100644 --- a/src/Twitch.elm +++ b/src/Twitch.elm @@ -9,6 +9,7 @@ module Twitch exposing , videoName ) +import Hover exposing (Hover) import Http import Iso8601 import Json.Decode as Decode @@ -70,9 +71,22 @@ videoId video = String.dropLeft 1 video.url |> String.replace "/" "-" -playlistMiniatureUrl : Playlist -> String -playlistMiniatureUrl playlist = - case List.head playlist.videos of +playlistMiniatureUrl : Time.Posix -> Maybe (Hover Playlist) -> Playlist -> String +playlistMiniatureUrl currentTime hover playlist = + let + skip = + case hover of + Nothing -> + 0 + + Just { element, time } -> + if element == playlist then + modBy (List.length playlist.videos) ((Time.posixToMillis currentTime - Time.posixToMillis time) // 1000) + + else + 0 + in + case List.head (List.drop skip playlist.videos) of Just v -> "videos/" ++ playlist.url ++ v.url ++ "miniature-050.png" @@ -80,6 +94,30 @@ playlistMiniatureUrl playlist = "" -videoMiniatureUrl : Playlist -> Video -> String -videoMiniatureUrl playlist video = - "videos/" ++ playlist.url ++ video.url ++ "miniature-050.png" +videoMiniatureUrl : Time.Posix -> Maybe (Hover Video) -> Playlist -> Video -> String +videoMiniatureUrl currentTime hover playlist video = + let + index = + case hover of + Nothing -> + 5 + + Just { element, time } -> + if element == video then + modBy 11 ((Time.posixToMillis currentTime - Time.posixToMillis time) // 1000) + + else + 5 + + indexString = + case index of + 10 -> + "100" + + 0 -> + "000" + + n -> + "0" ++ String.fromInt n ++ "0" + in + "videos/" ++ playlist.url ++ video.url ++ "miniature-" ++ indexString ++ ".png" diff --git a/src/Views.elm b/src/Views.elm index a00b703..097be26 100644 --- a/src/Views.elm +++ b/src/Views.elm @@ -7,9 +7,11 @@ import Core import Element exposing (Element) import Element.Background as Background import Element.Border as Border +import Element.Events as Events import Element.Font as Font import Element.Input as Input import Element.Keyed as Keyed +import Hover exposing (Hover) import Html import Html.Attributes import Json.Encode as Encode @@ -53,27 +55,27 @@ title model = Core.Loaded m -> case m.page of - Core.Home -> + Core.Home _ -> Consts.url - Core.Playlist p -> + Core.Playlist p _ -> Consts.url ++ " - " ++ p.name - Core.Video p v -> + 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.Home hover -> + playlistsView model.device model.playlists model.time hover - Core.Playlist playlist -> - videoMiniaturesView model.device model.zone playlist + Core.Playlist playlist hover -> + videoMiniaturesView model.device model.zone model.time hover playlist - Core.Video playlist video -> - videoView model.device model.zone playlist video + Core.Video playlist video hover -> + videoView model.device model.zone model.time hover playlist video topBar : Element Core.Msg @@ -99,14 +101,14 @@ homeButton = } -playlistsView : Element.Device -> List Twitch.Playlist -> Element Core.Msg -playlistsView device playlists = +playlistsView : Element.Device -> List Twitch.Playlist -> Time.Posix -> Maybe (Hover Twitch.Playlist) -> Element Core.Msg +playlistsView device playlists time hover = let empty = Element.el [ Element.width Element.fill ] Element.none views = - List.map playlistView playlists + List.map (playlistView time hover) playlists grouped = group (numberOfVideosPerRow device) views @@ -122,15 +124,23 @@ playlistsView device playlists = final -playlistView : Twitch.Playlist -> Element Core.Msg -playlistView playlist = +playlistView : Time.Posix -> Maybe (Hover Twitch.Playlist) -> Twitch.Playlist -> Element Core.Msg +playlistView time hover playlist = let + key = + Twitch.playlistMiniatureUrl time Nothing playlist + src = - Twitch.playlistMiniatureUrl playlist + Twitch.playlistMiniatureUrl time hover playlist image = - Keyed.el [ Element.width Element.fill, Element.height Element.fill ] - ( src + Keyed.el + [ Element.width Element.fill + , Element.height Element.fill + , Events.onMouseEnter (Core.HoverPlaylist playlist) + , Events.onMouseLeave Core.Unhover + ] + ( key , Element.image [ Element.width Element.fill , Element.height Element.fill @@ -187,14 +197,14 @@ playlistView playlist = button -videoMiniaturesView : Element.Device -> Time.Zone -> Twitch.Playlist -> Element Core.Msg -videoMiniaturesView device zone playlist = +videoMiniaturesView : Element.Device -> Time.Zone -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Element Core.Msg +videoMiniaturesView device zone time hover playlist = let empty = Element.el [ Element.width Element.fill ] Element.none views = - List.map (videoMiniatureView zone playlist) playlist.videos + List.map (videoMiniatureView zone time hover playlist) playlist.videos grouped = group (numberOfVideosPerRow device) views @@ -210,8 +220,8 @@ videoMiniaturesView device zone playlist = final -videoMiniature : Twitch.Playlist -> Twitch.Video -> Element Core.Msg -videoMiniature playlist video = +videoMiniature : Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg +videoMiniature time hover playlist video = let inFront = Element.text label @@ -227,12 +237,20 @@ videoMiniature playlist video = , Element.padding 5 ] + key = + Twitch.videoMiniatureUrl time Nothing playlist video + src = - Twitch.videoMiniatureUrl playlist video + Twitch.videoMiniatureUrl time hover playlist video image = - Keyed.el [ Element.width Element.fill, Element.height Element.fill ] - ( src + Keyed.el + [ Element.width Element.fill + , Element.height Element.fill + , Events.onMouseEnter (Core.HoverVideo video) + , Events.onMouseLeave Core.Unhover + ] + ( key , Element.image [ Element.width Element.fill , Element.height Element.fill @@ -247,12 +265,12 @@ videoMiniature playlist video = image -videoMiniatureView : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg -videoMiniatureView zone playlist video = +videoMiniatureView : Time.Zone -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg +videoMiniatureView zone time hover playlist video = let display = Element.column [ Element.width Element.fill, Element.spacing 10 ] - [ videoMiniature playlist video + [ videoMiniature time hover playlist video , videoDescription zone video ] @@ -265,13 +283,13 @@ videoMiniatureView zone playlist video = button -videoInList : Time.Zone -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg -videoInList zone playlist activeVideo video = +videoInList : Time.Zone -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Twitch.Video -> Element Core.Msg +videoInList zone time hover playlist activeVideo video = let label = Element.row [ Element.width Element.fill, Element.spacing 10 ] [ Element.el [ Element.width (Element.fillPortion 2) ] - (videoMiniature playlist video) + (videoMiniature time hover playlist video) , Element.el [ Element.width (Element.fillPortion 3), Element.paddingXY 0 10, Element.alignTop ] (videoDescription zone video) ] @@ -292,8 +310,8 @@ videoInList zone playlist activeVideo video = } -videoView : Element.Device -> Time.Zone -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg -videoView device zone playlist video = +videoView : Element.Device -> Time.Zone -> Time.Posix -> Maybe (Hover Twitch.Video) -> Twitch.Playlist -> Twitch.Video -> Element Core.Msg +videoView device zone time hover playlist video = let ( builder, contentPadding ) = case device.class of @@ -360,7 +378,7 @@ videoView device zone playlist video = , Element.height Element.fill , Element.scrollbarY ] - (List.map (videoInList zone playlist video) playlist.videos) + (List.map (videoInList zone time hover playlist video) playlist.videos) ]