elm-twitch/src/Core.elm

196 lines
5.3 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 Http
import Ports
import Time
import Twitch
import Url
type FullModel
= Unloaded Element.Device Url.Url Nav.Key
| Loaded Model
type alias Model =
{ playlists : List Twitch.Playlist
, zone : Time.Zone
, page : Page
, key : Nav.Key
, device : Element.Device
}
type Page
= Home
| Playlist Twitch.Playlist
| Video Twitch.Playlist Twitch.Video
type Msg
= Noop
| PlaylistsReceived ( List Twitch.Playlist, Time.Zone )
| HomeClicked
| PlaylistClicked Twitch.Playlist
| VideoClicked Twitch.Playlist Twitch.Video
| UrlReceived Url.Url
| UrlRequested Browser.UrlRequest
| SizeReceived Int Int
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
, Twitch.fetchPlaylists resultToMsg
)
resultToMsg : Result Http.Error (List Twitch.Playlist) -> Msg
resultToMsg result =
case result of
Ok o ->
PlaylistsReceived ( o, Time.utc )
Err _ ->
Noop
subscriptions : FullModel -> Sub Msg
subscriptions _ =
Events.onResize (\w h -> SizeReceived w h)
update : Msg -> FullModel -> ( FullModel, Cmd Msg )
update msg model =
case ( msg, model ) of
( Noop, _ ) ->
( model, Cmd.none )
( SizeReceived w h, Loaded m ) ->
( Loaded { m | device = Element.classifyDevice { width = w, height = h } }
, Cmd.none
)
( PlaylistsReceived ( playlists, zone ), Unloaded device url key ) ->
update
(UrlReceived url)
(Loaded { key = key, playlists = playlists, zone = zone, page = Home, device = device })
( 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
, Ports.registerVideo ( Twitch.videoId v, "videos/" ++ p.url ++ v.url, time )
)
( Just p, Nothing ) ->
( Playlist p, Cmd.none )
_ ->
( Home, 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))