elm-twitch/src/Core.elm

257 lines
7.2 KiB
Elm

module Core exposing (FullModel(..), Model, Msg(..), Page(..), init, subscriptions, update)
import Browser
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
import Time
import Twitch
import Url
type FullModel
= Unloaded Element.Device Url.Url Nav.Key Time.Zone
| Loaded Model
type alias Model =
{ playlists : List Twitch.Playlist
, zone : Time.Zone
, page : Page
, key : Nav.Key
, device : Element.Device
, time : Time.Posix
}
type Page
= Home (Maybe (Hover Twitch.Playlist))
| Playlist Twitch.Playlist (Maybe (Hover Twitch.Video))
| Video Twitch.Playlist Twitch.Video (Maybe (Hover Twitch.Video))
type Msg
= Noop
| PlaylistsReceived (List Twitch.Playlist)
| HomeClicked
| PlaylistClicked Twitch.Playlist
| VideoClicked Twitch.Playlist Twitch.Video
| UrlReceived Url.Url
| 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 )
init { width, height } url key =
( Unloaded (Element.classifyDevice { width = width, height = height }) url key Time.utc
, Cmd.batch
[ Task.perform TimeZoneReceived Time.here
, Twitch.fetchPlaylists resultToMsg
]
)
resultToMsg : Result Http.Error (List Twitch.Playlist) -> Msg
resultToMsg result =
case result of
Ok o ->
PlaylistsReceived o
Err _ ->
Noop
subscriptions : FullModel -> Sub Msg
subscriptions _ =
Sub.batch
[ Events.onResize (\w h -> SizeReceived w h)
, Time.every 200 TimeReceived
]
update : Msg -> FullModel -> ( FullModel, Cmd Msg )
update msg model =
case ( msg, model ) of
( Noop, _ ) ->
( model, Cmd.none )
( 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 )
( SizeReceived w h, Loaded m ) ->
( Loaded { m | device = Element.classifyDevice { width = w, height = h } }
, Cmd.none
)
( PlaylistsReceived playlists, Unloaded device url key zone ) ->
update
(UrlReceived url)
(Loaded
{ key = key
, playlists = playlists
, zone = zone
, page = Home Nothing
, device = device
, time = Time.millisToPosix 0
}
)
( HomeClicked, Loaded m ) ->
( model
, Nav.pushUrl m.key "#"
)
( PlaylistClicked playlist, Loaded m ) ->
( model
, Nav.pushUrl m.key ("#" ++ playlist.url)
)
( VideoClicked playlist video, Loaded m ) ->
( model
, Nav.pushUrl m.key ("#" ++ playlist.url ++ video.url)
)
( UrlReceived url, Loaded m ) ->
let
splits =
String.split "?" (Maybe.withDefault "" url.fragment)
( split, args ) =
case splits of
h1 :: h2 :: _ ->
( String.split "/" h1, parseQueryString h2 )
h1 :: _ ->
( String.split "/" h1, Dict.empty )
_ ->
( [], Dict.empty )
time =
case Maybe.map String.toInt (Dict.get "t" args) of
Just (Just 0) ->
Nothing
Just (Just t) ->
Just t
_ ->
Nothing
( playlistName, videoName ) =
case split of
p :: v :: _ ->
( Just (p ++ "/"), Just (v ++ "/") )
p :: _ ->
( Just (p ++ "/"), Nothing )
_ ->
( Nothing, Nothing )
playlist =
List.head (List.filter (\x -> Just x.url == playlistName) m.playlists)
video =
case playlist of
Just p ->
List.head (List.filter (\x -> Just x.url == videoName) p.videos)
_ ->
Nothing
( page, cmd ) =
case ( playlist, video ) of
( Just p, Just v ) ->
( Video p v Nothing
, Ports.registerVideo ( Twitch.videoId v, "videos/" ++ p.url ++ v.url, time )
)
( Just p, Nothing ) ->
( Playlist p Nothing, Cmd.none )
_ ->
( Home Nothing, Cmd.none )
extraCmd =
case page of
Video _ _ _ ->
Cmd.none
_ ->
Ports.eraseVideo ()
in
( Loaded { m | page = page }, Cmd.batch [ cmd, extraCmd ] )
( UrlRequested u, Loaded m ) ->
case u of
Browser.Internal url ->
( model, Nav.pushUrl m.key (Url.toString url) )
Browser.External s ->
( model, Nav.load s )
_ ->
( model, Cmd.none )
splitter : String -> Maybe ( String, String )
splitter input =
case String.split "=" input of
h :: t ->
Just ( h, String.join "=" t )
_ ->
Nothing
parseQueryString : String -> Dict String String
parseQueryString input =
Dict.fromList (List.filterMap splitter (String.split "&" input))