elm-twitch/src/Core.elm

322 lines
9.2 KiB
Elm
Raw Normal View History

2020-11-06 11:43:28 +01:00
module Core exposing (Model, Msg(..), Page(..), init, subscriptions, update)
2020-10-03 18:44:16 +02:00
2020-10-08 16:18:22 +02:00
import Browser
2020-10-04 20:20:16 +02:00
import Browser.Events as Events
2020-10-04 16:02:54 +02:00
import Browser.Navigation as Nav
2020-10-04 21:31:16 +02:00
import Dict exposing (Dict)
2020-10-04 20:20:16 +02:00
import Element
2020-10-16 12:03:07 +02:00
import Hover exposing (Hover)
2020-10-05 11:26:11 +02:00
import Http
2020-10-04 16:37:01 +02:00
import Ports
2020-10-15 16:47:10 +02:00
import Task
2020-10-04 15:24:04 +02:00
import Time
2020-10-28 16:57:08 +01:00
import TimeZone
2020-10-03 18:44:16 +02:00
import Twitch
import Url
2021-06-21 11:30:31 +02:00
import Video
import Video.Events
2020-10-03 18:44:16 +02:00
2020-10-04 13:15:57 +02:00
type alias Model =
{ playlists : List Twitch.Playlist
2020-10-04 15:24:04 +02:00
, zone : Time.Zone
2020-10-04 13:15:57 +02:00
, page : Page
2020-10-04 16:02:54 +02:00
, key : Nav.Key
2020-10-04 20:20:16 +02:00
, device : Element.Device
2020-10-16 12:03:07 +02:00
, time : Time.Posix
2020-11-06 12:19:42 +01:00
, currentDate : Time.Posix
2020-11-06 11:43:28 +01:00
, url : Url.Url
2021-04-03 23:33:01 +02:00
, darkMode : Bool
, darkSetting : Maybe Bool
2020-10-04 13:15:57 +02:00
}
type Page
2020-10-16 12:03:07 +02:00
= Home (Maybe (Hover Twitch.Playlist))
| Playlist Twitch.Playlist (Maybe (Hover Twitch.Video))
2021-06-21 11:30:31 +02:00
| Video Twitch.Playlist Twitch.Video Video.Video (Maybe (Hover Twitch.Video))
2020-10-03 18:44:16 +02:00
type Msg
= Noop
2020-10-15 16:47:10 +02:00
| PlaylistsReceived (List Twitch.Playlist)
2020-10-04 14:24:16 +02:00
| HomeClicked
2020-10-05 11:26:11 +02:00
| PlaylistClicked Twitch.Playlist
| VideoClicked Twitch.Playlist Twitch.Video
2020-10-04 16:02:54 +02:00
| UrlReceived Url.Url
2020-10-08 16:18:22 +02:00
| UrlRequested Browser.UrlRequest
2020-10-04 20:20:16 +02:00
| SizeReceived Int Int
2020-10-28 16:57:08 +01:00
| TimeZoneReceivedResult (Result TimeZone.Error ( String, Time.Zone ))
2020-10-15 16:47:10 +02:00
| TimeZoneReceived Time.Zone
2020-10-16 12:03:07 +02:00
| HoverPlaylist Twitch.Playlist
| HoverVideo Twitch.Video
| Unhover
| TimeReceived Time.Posix
2020-11-06 12:19:42 +01:00
| CurrentDateReceived Time.Posix
2021-04-03 23:33:01 +02:00
| DarkMode Bool
| DarkModeClicked
2021-06-21 11:30:31 +02:00
| VideoMsg Video.Msg
2020-10-03 18:44:16 +02:00
2021-04-03 23:33:01 +02:00
init : { width : Int, height : Int, darkMode : Bool, darkSetting : Maybe Bool } -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init { width, height, darkMode, darkSetting } url key =
2020-11-06 12:19:42 +01:00
( Model
[]
Time.utc
(Home Nothing)
key
(Element.classifyDevice { width = width, height = height })
(Time.millisToPosix 0)
(Time.millisToPosix 0)
url
2021-04-03 23:33:01 +02:00
darkMode
darkSetting
2020-10-15 16:47:10 +02:00
, Cmd.batch
2020-10-28 16:57:08 +01:00
[ Task.attempt TimeZoneReceivedResult TimeZone.getZone
2020-11-06 12:19:42 +01:00
, Task.perform CurrentDateReceived Time.now
2020-10-15 16:47:10 +02:00
, Twitch.fetchPlaylists resultToMsg
]
2020-10-03 18:44:16 +02:00
)
2020-10-04 13:15:57 +02:00
2020-10-05 11:26:11 +02:00
resultToMsg : Result Http.Error (List Twitch.Playlist) -> Msg
resultToMsg result =
case result of
Ok o ->
2020-10-15 16:47:10 +02:00
PlaylistsReceived o
2020-10-05 11:26:11 +02:00
2020-10-05 11:33:49 +02:00
Err _ ->
2020-10-05 11:26:11 +02:00
Noop
2020-11-06 11:43:28 +01:00
subscriptions : Model -> Sub Msg
2021-06-21 11:30:31 +02:00
subscriptions model =
2020-10-16 12:03:07 +02:00
Sub.batch
[ Events.onResize (\w h -> SizeReceived w h)
, Time.every 200 TimeReceived
2021-04-03 23:33:01 +02:00
, Ports.darkMode DarkMode
2021-06-21 11:30:31 +02:00
, Sub.map VideoMsg
(case model.page of
Video _ _ v _ ->
Video.Events.subs v
_ ->
Sub.none
)
2020-10-16 12:03:07 +02:00
]
2020-10-04 20:20:16 +02:00
2020-11-06 11:43:28 +01:00
update : Msg -> Model -> ( Model, Cmd Msg )
2020-10-04 13:15:57 +02:00
update msg model =
2020-11-06 12:19:42 +01:00
case msg of
Noop ->
2020-10-04 13:15:57 +02:00
( model, Cmd.none )
2020-11-06 12:19:42 +01:00
TimeZoneReceived z ->
( { model | zone = z }, Cmd.none )
2020-10-28 16:57:08 +01:00
2020-11-06 12:19:42 +01:00
TimeZoneReceivedResult (Ok ( _, zone )) ->
( { model | zone = zone }, Cmd.none )
2020-10-28 16:57:08 +01:00
2020-11-06 12:19:42 +01:00
TimeZoneReceivedResult (Err _) ->
2020-10-28 16:57:08 +01:00
( model, Task.perform TimeZoneReceived Time.here )
2020-11-06 12:19:42 +01:00
TimeReceived p ->
( { model | time = p }, Cmd.none )
2020-10-16 12:03:07 +02:00
2020-11-06 12:19:42 +01:00
CurrentDateReceived d ->
( { model | currentDate = d }, Cmd.none )
HoverPlaylist hover ->
case model.page of
2020-10-16 12:03:07 +02:00
Home Nothing ->
2020-11-06 12:19:42 +01:00
( { model | page = Home (Just (Hover.hover hover model.time)) }, Cmd.none )
2020-10-16 12:03:07 +02:00
_ ->
2020-11-06 12:19:42 +01:00
( model, Cmd.none )
2020-10-16 12:03:07 +02:00
2020-11-06 12:19:42 +01:00
HoverVideo hover ->
case model.page of
2020-10-16 12:03:07 +02:00
Playlist p Nothing ->
2020-11-06 12:19:42 +01:00
( { model | page = Playlist p (Just (Hover.hover hover model.time)) }, Cmd.none )
2020-10-16 12:03:07 +02:00
2021-06-21 11:30:31 +02:00
Video p v x Nothing ->
2021-06-22 09:21:37 +02:00
( { model | page = Video p v x (Just (Hover.hover hover model.time)) }, Cmd.none )
2020-10-16 12:03:07 +02:00
_ ->
2020-11-06 12:19:42 +01:00
( model, Cmd.none )
2020-10-16 12:03:07 +02:00
2020-11-06 12:19:42 +01:00
Unhover ->
case model.page of
2020-10-16 12:03:07 +02:00
Home _ ->
2020-11-06 12:19:42 +01:00
( { model | page = Home Nothing }, Cmd.none )
2020-10-16 12:03:07 +02:00
Playlist p _ ->
2020-11-06 12:19:42 +01:00
( { model | page = Playlist p Nothing }, Cmd.none )
2020-10-16 12:03:07 +02:00
2021-06-21 11:30:31 +02:00
Video p v x _ ->
( { model | page = Video p v x Nothing }, Cmd.none )
2020-10-16 12:03:07 +02:00
2020-11-06 12:19:42 +01:00
SizeReceived w h ->
( { model | device = Element.classifyDevice { width = w, height = h } }
2020-10-04 20:20:16 +02:00
, Cmd.none
)
2020-11-06 12:19:42 +01:00
PlaylistsReceived playlists ->
2020-10-04 16:02:54 +02:00
update
2020-11-06 12:19:42 +01:00
(UrlReceived model.url)
{ model | playlists = playlists, page = Home Nothing }
2020-11-06 11:43:28 +01:00
2020-11-06 12:19:42 +01:00
HomeClicked ->
2020-10-04 17:14:00 +02:00
( model
2020-11-06 12:19:42 +01:00
, Nav.pushUrl model.key "#"
2020-10-04 16:02:54 +02:00
)
2020-10-04 14:24:16 +02:00
2021-04-03 23:33:01 +02:00
DarkModeClicked ->
let
next =
nextDarkSetting model.darkSetting
in
( { model | darkSetting = next }, Ports.setDarkMode next )
2020-11-06 12:19:42 +01:00
PlaylistClicked playlist ->
2020-10-04 17:14:00 +02:00
( model
2020-11-06 12:19:42 +01:00
, Nav.pushUrl model.key ("#" ++ playlist.url)
2020-10-04 16:02:54 +02:00
)
2020-10-04 14:24:16 +02:00
2020-11-06 12:19:42 +01:00
VideoClicked playlist video ->
2020-10-04 17:14:00 +02:00
( model
2020-11-06 12:19:42 +01:00
, Nav.pushUrl model.key ("#" ++ playlist.url ++ video.url)
2020-10-04 16:02:54 +02:00
)
2020-11-06 12:19:42 +01:00
UrlReceived url ->
if List.isEmpty model.playlists then
( { model | url = url }, Cmd.none )
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
else
let
splits =
String.split "?" (Maybe.withDefault "" url.fragment)
2020-10-04 21:31:16 +02:00
2020-11-06 11:43:28 +01:00
( split, args ) =
case splits of
h1 :: h2 :: _ ->
( String.split "/" h1, parseQueryString h2 )
2020-10-04 21:31:16 +02:00
2020-11-06 11:43:28 +01:00
h1 :: _ ->
( String.split "/" h1, Dict.empty )
2020-10-04 21:31:16 +02:00
2020-11-06 11:43:28 +01:00
_ ->
( [], Dict.empty )
2020-10-04 21:31:16 +02:00
2020-11-06 11:43:28 +01:00
time =
case Dict.get "t" args of
Just "0" ->
Nothing
2020-10-04 21:31:16 +02:00
2020-11-06 11:43:28 +01:00
Just t ->
Just t
2020-10-04 21:31:16 +02:00
2020-11-06 11:43:28 +01:00
_ ->
Nothing
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
( playlistName, videoName ) =
case split of
p :: v :: _ ->
( Just (p ++ "/"), Just (v ++ "/") )
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
p :: _ ->
( Just (p ++ "/"), Nothing )
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
_ ->
( Nothing, Nothing )
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
playlist =
2020-11-06 12:19:42 +01:00
List.head (List.filter (\x -> Just x.url == playlistName) model.playlists)
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
video =
case playlist of
Just p ->
List.head (List.filter (\x -> Just x.url == videoName) p.videos)
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
_ ->
Nothing
2020-10-04 23:03:38 +02:00
2020-11-06 11:43:28 +01:00
( page, cmd ) =
case ( playlist, video ) of
( Just p, Just v ) ->
2021-06-21 11:30:31 +02:00
let
( el, videoCommand ) =
2021-12-09 17:34:53 +01:00
Video.fromConfig
{ url = "/videos/" ++ p.url ++ v.url ++ "/manifest.m3u8"
, id = "video"
, autoplay = True
, enableMiniatures = True
, startTime = Nothing
}
2021-06-21 11:30:31 +02:00
in
( Video p v el Nothing, Cmd.map VideoMsg videoCommand )
2020-10-04 16:02:54 +02:00
2020-11-06 11:43:28 +01:00
( Just p, Nothing ) ->
( Playlist p Nothing, Cmd.none )
2020-10-04 23:03:38 +02:00
2020-11-06 11:43:28 +01:00
_ ->
( Home Nothing, Cmd.none )
in
2021-06-21 11:30:31 +02:00
( { model | page = page }, cmd )
2020-11-06 11:43:28 +01:00
2020-11-06 12:19:42 +01:00
UrlRequested u ->
2020-10-08 16:18:22 +02:00
case u of
Browser.Internal url ->
2020-11-06 12:19:42 +01:00
( model, Nav.pushUrl model.key (Url.toString url) )
2020-10-08 16:18:22 +02:00
Browser.External s ->
( model, Nav.load s )
2021-04-03 23:33:01 +02:00
DarkMode dark ->
( { model | darkMode = dark }, Cmd.none )
2021-06-21 11:30:31 +02:00
VideoMsg vMsg ->
let
( newPage, newCommand ) =
case model.page of
Video a b v c ->
let
( newVideo, cmd ) =
Video.update vMsg v
in
( Video a b newVideo c, cmd )
_ ->
( model.page, Cmd.none )
in
( { model | page = newPage }, Cmd.map VideoMsg newCommand )
2020-10-04 21:31:16 +02:00
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))
2021-04-03 23:33:01 +02:00
nextDarkSetting : Maybe Bool -> Maybe Bool
nextDarkSetting current =
case current of
Nothing ->
Just True
Just True ->
Just False
Just False ->
Nothing