diff options
Diffstat (limited to 'Hero/Client.hs')
-rw-r--r-- | Hero/Client.hs | 259 |
1 files changed, 133 insertions, 126 deletions
diff --git a/Hero/Client.hs b/Hero/Client.hs index 9a8fa02..0472d48 100644 --- a/Hero/Client.hs +++ b/Hero/Client.hs @@ -1,6 +1,7 @@ -{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NoImplicitPrelude #-} + -- | Hero app frontend -- -- : exe mmc.js @@ -17,52 +18,55 @@ -- : dep ghcjs-base module Hero.Client where -import Hero.App ( Action(..) - , Comic(..) - , ComicReaderState(..) - , ComicReaderView(..) - , Model(..) - , AudioState(..) - , audioId - , chooseExperienceLink - , comicPlayerSpreadLink - , comicPlayerFullLink - , comicVideoLink - , handlers - , initModel - , the404 - , routes - ) -import qualified Network.RemoteData as Network -import Data.Aeson ( eitherDecodeStrict ) +import Data.Aeson (eitherDecodeStrict) import qualified Data.Set as Set import qualified GHC.Show as Legacy -import JavaScript.Web.XMLHttpRequest ( Request(..) - , Method(GET) - , RequestData(NoData) - , contents - , xhrByteString - ) -import Miso -import Miso.Effect.DOM (scrollIntoView) +import Hero.App + ( Action (..), + AudioState (..), + Comic (..), + ComicReaderState (..), + ComicReaderView (..), + Model (..), + audioId, + chooseExperienceLink, + comicPlayerFullLink, + comicPlayerSpreadLink, + comicVideoLink, + handlers, + initModel, + routes, + the404, + ) +import JavaScript.Web.XMLHttpRequest + ( Method (GET), + Request (..), + RequestData (NoData), + contents, + xhrByteString, + ) +import Miso +import Miso.Effect.DOM (scrollIntoView) import qualified Miso.FFI.Audio as Audio import qualified Miso.FFI.Document as Document import qualified Miso.FFI.Fullscreen as Fullscreen -import Miso.String -import Protolude +import Miso.String +import qualified Network.RemoteData as Network +import Protolude -- | Entry point for a miso application main :: IO () -main = miso $ \currentURI -> App { model = initModel currentURI, .. } +main = miso $ \currentURI -> App {model = initModel currentURI, ..} where - update = move - view = see - subs = [ uriSub HandleURI - , keyboardSub keynav - ] - events = defaultEvents + update = move + view = see + subs = + [ uriSub HandleURI, + keyboardSub keynav + ] + events = defaultEvents initialAction = FetchComics - mountPoint = Nothing + mountPoint = Nothing (∈) :: Ord a => a -> Set a -> Bool (∈) = Set.member @@ -70,17 +74,17 @@ main = miso $ \currentURI -> App { model = initModel currentURI, .. } -- | Keyboard navigation - maps keys to actions. keynav :: Set Int -> Action keynav ks - | 37 ∈ ks = PrevPage -- ^ left arrow - | 39 ∈ ks = NextPage -- ^ right arrow - | 191 ∈ ks = DumpModel -- ^ ? - | 32 ∈ ks = ToggleAudio audioId -- ^ SPC - | otherwise = NoOp + | 37 ∈ ks = PrevPage -- left arrow + | 39 ∈ ks = NextPage -- right arrow + | 191 ∈ ks = DumpModel -- ? + | 32 ∈ ks = ToggleAudio audioId -- SPC + | otherwise = NoOp see :: Model -> View Action see model = - case runRoute routes handlers uri model of - Left _ -> the404 model - Right v -> v + case runRoute routes handlers uri model of + Left _ -> the404 model + Right v -> v -- | Console-logging foreign import javascript unsafe "console.log($1);" @@ -88,101 +92,104 @@ foreign import javascript unsafe "console.log($1);" -- | Updates model, optionally introduces side effects move :: Action -> Model -> Effect Action Model -move NoOp model = noEff model +move NoOp model = noEff model move DumpModel model = model <# do - say $ ms $ Legacy.show model - pure NoOp -move (SelectExperience comic) model = model { cpState = ChooseExperience (comicId comic) 1 } - <# do pure $ ChangeURI $ chooseExperienceLink (comicId comic) 1 -move (StartReading comic) model = model { cpState = Reading Spread (comicId comic) 1 } - <# do pure $ ChangeURI $ comicPlayerSpreadLink (comicId comic) 1 -move (StartWatching comic) model = model { cpState = Watching (comicId comic) } - <# do pure $ ChangeURI $ comicVideoLink (comicId comic) 1 + say $ ms $ Legacy.show model + pure NoOp +move (SelectExperience comic) model = model {cpState = ChooseExperience (comicId comic) 1} + <# do pure $ ChangeURI $ chooseExperienceLink (comicId comic) 1 +move (StartReading comic) model = model {cpState = Reading Spread (comicId comic) 1} + <# do pure $ ChangeURI $ comicPlayerSpreadLink (comicId comic) 1 +move (StartWatching comic) model = model {cpState = Watching (comicId comic)} + <# do pure $ ChangeURI $ comicVideoLink (comicId comic) 1 move NextPage model = case cpState model of - Reading Spread id pg -> - model { cpState = Reading Spread id (pg+2) } <# do - pure $ ChangeURI $ comicPlayerSpreadLink id (pg+2) - Reading Full id pg -> - model { cpState = Reading Full id (pg+1) } <# do - pure $ ChangeURI $ comicPlayerFullLink id (pg+1) - Cover id -> - model { cpState = Reading Spread id 1 } <# do - pure $ ChangeURI $ comicPlayerSpreadLink id 1 - _ -> noEff model + Reading Spread id pg -> + model {cpState = Reading Spread id (pg + 2)} <# do + pure $ ChangeURI $ comicPlayerSpreadLink id (pg + 2) + Reading Full id pg -> + model {cpState = Reading Full id (pg + 1)} <# do + pure $ ChangeURI $ comicPlayerFullLink id (pg + 1) + Cover id -> + model {cpState = Reading Spread id 1} <# do + pure $ ChangeURI $ comicPlayerSpreadLink id 1 + _ -> noEff model move PrevPage model = case cpState model of - Reading Spread id pg -> - model { cpState = Reading Spread id (pg-2) } <# do - pure $ ChangeURI $ comicPlayerSpreadLink id (pg-2) - Reading Full id pg -> - model { cpState = Reading Full id (pg-1) } <# do - pure $ ChangeURI $ comicPlayerFullLink id (pg-1) - Cover _ -> noEff model - _ -> noEff model -move (ToggleZoom c pg) m = m { cpState = newState } <# do pure act - where - goto lnk = ChangeURI $ lnk (comicId c) pg - reading v = Reading v (comicId c) pg - (newState, act) = case cpState m of - Reading Full _ _ -> (reading Spread, goto comicPlayerSpreadLink) - Reading Spread _ _ -> (reading Full, goto comicPlayerFullLink) - x -> (x, NoOp) -move (ToggleInLibrary c) model = model { userLibrary = newLib } <# pure NoOp - where - newLib | c `elem` (userLibrary model) = - Protolude.filter (/= c) $ userLibrary model - | otherwise = c : (userLibrary model) -move (HandleURI u) model = model { uri = u } <# pure NoOp + Reading Spread id pg -> + model {cpState = Reading Spread id (pg -2)} <# do + pure $ ChangeURI $ comicPlayerSpreadLink id (pg -2) + Reading Full id pg -> + model {cpState = Reading Full id (pg -1)} <# do + pure $ ChangeURI $ comicPlayerFullLink id (pg -1) + Cover _ -> noEff model + _ -> noEff model +move (ToggleZoom c pg) m = m {cpState = newState} <# do pure act + where + goto lnk = ChangeURI $ lnk (comicId c) pg + reading v = Reading v (comicId c) pg + (newState, act) = case cpState m of + Reading Full _ _ -> (reading Spread, goto comicPlayerSpreadLink) + Reading Spread _ _ -> (reading Full, goto comicPlayerFullLink) + x -> (x, NoOp) +move (ToggleInLibrary c) model = model {userLibrary = newLib} <# pure NoOp + where + newLib + | c `elem` (userLibrary model) = + Protolude.filter (/= c) $ userLibrary model + | otherwise = c : (userLibrary model) +move (HandleURI u) model = model {uri = u} <# pure NoOp move (ChangeURI u) model = model <# do pushURI u pure NoOp -move FetchComics model = model <# (SetComics <$> fetchComics) -move (SetComics cs) model = noEff model { appComics = cs } -move (ToggleAudio i ) model = model { cpAudioState = newState } <# do +move FetchComics model = model <# (SetComics <$> fetchComics) +move (SetComics cs) model = noEff model {appComics = cs} +move (ToggleAudio i) model = model {cpAudioState = newState} <# do el <- Document.getElementById i toggle el pure NoOp - where - (newState, toggle) = case cpAudioState model of - Playing -> (Paused, Audio.pause) - Paused -> (Playing, Audio.play) -move ToggleFullscreen model = model { cpState = newState } <# do - el <- Document.querySelector "body" - -- TODO: check Document.fullscreenEnabled - -- https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenEnabled - _ <- toggle el - pure NoOp where - (toggle, newState) = case cpState model of - Reading Full c n -> (const Fullscreen.exit, Reading Full c n) - Reading Spread c n -> (Fullscreen.request, Reading Spread c n) - -- otherwise, do nothing: - x -> (pure, x) -move (SetMediaInfo x) model = model { dMediaInfo = x } <# do - case x of - Just Comic {comicId = id} -> - pure $ ScrollIntoView $ "comic-" <> ms id - Nothing -> - pure NoOp + (newState, toggle) = case cpAudioState model of + Playing -> (Paused, Audio.pause) + Paused -> (Playing, Audio.play) +move ToggleFullscreen model = model {cpState = newState} <# do + el <- Document.querySelector "body" + -- TODO: check Document.fullscreenEnabled + -- https://developer.mozilla.org/en-US/docs/Web/API/Document/fullscreenEnabled + _ <- toggle el + pure NoOp + where + (toggle, newState) = case cpState model of + Reading Full c n -> (const Fullscreen.exit, Reading Full c n) + Reading Spread c n -> (Fullscreen.request, Reading Spread c n) + -- otherwise, do nothing: + x -> (pure, x) +move (SetMediaInfo x) model = model {dMediaInfo = x} <# do + case x of + Just Comic {comicId = id} -> + pure $ ScrollIntoView $ "comic-" <> ms id + Nothing -> + pure NoOp move (ScrollIntoView id) model = model <# do - say $ ms $ Legacy.show id - scrollIntoView id - pure NoOp + say $ ms $ Legacy.show id + scrollIntoView id + pure NoOp fetchComics :: IO (Network.RemoteData MisoString [Comic]) fetchComics = do mjson <- contents <$> xhrByteString req case mjson of - Nothing -> + Nothing -> pure $ Network.Failure "Could not fetch comics from server." - Just json -> pure $ Network.fromEither - $ either (Left . ms) pure - $ eitherDecodeStrict json - where - req = Request - { reqMethod = GET - , reqURI = "/api/comic" -- FIXME: can we replace this hardcoding? - , reqLogin = Nothing - , reqHeaders = [] - , reqWithCredentials = False - , reqData = NoData - } + Just json -> + pure $ Network.fromEither + $ either (Left . ms) pure + $ eitherDecodeStrict json + where + req = + Request + { reqMethod = GET, + reqURI = "/api/comic", -- FIXME: can we replace this hardcoding? + reqLogin = Nothing, + reqHeaders = [], + reqWithCredentials = False, + reqData = NoData + } |