summaryrefslogtreecommitdiff
path: root/Com/MusicMeetsComics/App.hs
diff options
context:
space:
mode:
Diffstat (limited to 'Com/MusicMeetsComics/App.hs')
-rw-r--r--Com/MusicMeetsComics/App.hs748
1 files changed, 0 insertions, 748 deletions
diff --git a/Com/MusicMeetsComics/App.hs b/Com/MusicMeetsComics/App.hs
deleted file mode 100644
index 3fa237d..0000000
--- a/Com/MusicMeetsComics/App.hs
+++ /dev/null
@@ -1,748 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE NoImplicitPrelude #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes #-}
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeOperators #-}
-{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
-module Com.MusicMeetsComics.App where
-
-import Alpha
-import qualified Clay
-import qualified Com.MusicMeetsComics.Assets as Assets
-import Com.MusicMeetsComics.Look as Look
-import Com.MusicMeetsComics.Look.Typography
-import Com.Simatime.Network
-import Data.Aeson ( ToJSON(..)
- , FromJSON(..)
- , genericToJSON
- , genericParseJSON
- , defaultOptions
- )
-import qualified Data.List as List
-import qualified Data.List.Split as List
-import Data.Proxy ( Proxy(..) )
-import Data.String
-import Data.String.Quote
-import Data.Text ( Text, replace, toLower )
-import GHC.Generics ( Generic )
-import qualified GHC.Show as Legacy
-import Miso
-import qualified Miso (for_)
-import Miso.String
-import Protolude hiding (replace)
-import Servant.API ( Capture
- , URI(..)
- , safeLink
- , (:<|>)(..)
- , (:>)
- )
-import Servant.Links ( linkURI )
-
-crossorigin_ :: MisoString -> Attribute action
-crossorigin_ = textProp "crossorigin"
-
--- | The css id for controling music in the comic player.
-audioId :: MisoString
-audioId = "audioSource"
-
--- | Like 'onClick' but prevents the default action from triggering. Use this to
--- overide 'a_' links, for example.
-onPreventClick :: Action -> Attribute Action
-onPreventClick action =
- onWithOptions Miso.defaultOptions { preventDefault = True }
- "click" emptyDecoder (\() -> action)
-
--- TODO: make ComicId a hashid https://hackage.haskell.org/package/hashids-1.0.2.4/docs/Web-Hashids.html
-type ComicId = String
-
--- | Class for turning different string types to snakeCase.
-class CanSnakeCase str where
- snake :: str -> str
-
-instance CanSnakeCase Text where
- snake = Data.Text.replace " " "-" . Data.Text.toLower
-
--- | Used for looking up images on S3, mostly
-comicSlug :: Comic -> Text
-comicSlug Comic{..} = snake comicName <> "-" <> comicIssue
-
-data Comic = Comic
- { comicId :: ComicId
- , comicPages :: Integer
- , comicName :: Text
- , comicIssue :: Text -- ^ Ideally this would be a dynamic number-like type
- , comicDescription :: Text
- } deriving (Show, Eq, Generic)
-
-instance ToJSON Comic where
- toJSON = genericToJSON Data.Aeson.defaultOptions
-
-instance FromJSON Comic where
- parseJSON = genericParseJSON Data.Aeson.defaultOptions
-
--- | Class for rendering media objects in different ways.
-class IsMediaObject o where
- -- | Render a thumbnail for use in a shelf, or otherwise.
- thumbnail :: o -> View Action
- -- | Render a featured banner.
- feature :: o -> Library -> View Action
- -- | Media info view
- info :: o -> Library -> View Action
-
-instance IsMediaObject Comic where
- thumbnail c@Comic{..} = li_ []
- [ a_
- [ class_ "comic grow clickable"
- , id_ $ "comic-" <> ms comicId
- , onClick $ SetMediaInfo $ Just c
- ]
- [ img_ [ src_ $ ms $ Assets.demo <> comicSlug c <> ".png" ]
- , span_ [] [ text $ "Issue #" <> ms comicIssue ]
- , span_ [] [ text $ ms comicName ]
- ]
- ]
- feature comic lib = div_ [ id_ "featured-comic" ]
- [ img_ [ id_ "featured-banner", src_ $ ms $ Assets.demo <> "feature-banner.png" ]
- , div_ [ id_ "featured-content" ]
- [ div_ [ class_ "hero-original", css wide ]
- [ span_ [ css thicc ] [ text "Herø" ]
- , span_ [ css euro ] [ text " Original" ]
- ]
- , div_ [ class_ "comic-logo" ]
- [ img_ [ src_ $ ms $ Assets.demo <> comicSlug comic <> "-logo.png" ] ]
- , div_ [ class_ "comic-action-menu" ] $ el <$> [ Watch comic, Read comic, Save comic lib ]
- , p_ [ class_ "description" ]
- [ text . ms $ comicDescription comic
- ]
- ]
- ]
- info c@Comic {..} lib = div_ [ class_ "media-info", css euro ]
- [ div_ [ class_ "media-info-meta" ]
- [ column [ img_ [ src_ $ ms $ Assets.demo <> "dmc-widethumb.png" ] ]
- , column
- [ span_ [ style_ title ] [ text $ ms comicName ]
- , span_ [ style_ subtitle ] [ text $ "Issue #" <> ms comicIssue ]
- , span_ [] [ text "Released: " ]
- , span_ [] [ text $ "Pages: " <> ms (show comicPages :: String) ]
- ]
- ]
- , div_ [ class_ "media-info-summary" ]
- [ p_ [ style_ $ uppercase <> bold <> Look.expanded <> "font-size" =: ".8rem" ]
- [ text "Summary" ]
- , p_ [] [ text $ ms comicDescription ]
- ]
- , div_ [ class_ "media-info-actions" ] $ el <$> [ Save c lib, Read c, Watch c ]
- -- , row [ text "credits" ]
- ]
- where
- title = "color" =: "red" <> "font-size" =: "1.6rem" <> uppercase
- <> "line-height" =: "100%" <> Look.condensed <> bold
- subtitle = "color" =: "#fff" <> "font-size" =: "1.2rem" <> bold <> Look.condensed
-
-
-type ZoomModel = Int
-
--- | All the buttons.
-data Button
- = Watch Comic | Read Comic | Save Comic Library
- | SaveIcon Comic Library
- | ZoomIcon ZoomModel Comic Page
- | PlayPause MisoString AudioState
- | Arrow Action
-
--- | Class for defining general, widely used elements in the heroverse.
-class Elemental v where el :: v -> View Action
-
--- TODO: what if I just did this on all actions?
--- then I could e.g. `el $ ToggleAudio audioId audioState`
-instance Elemental Button where
- el (PlayPause id model) = button_
- [ class_ "button is-large icon"
- , onClick $ ToggleAudio id
- ]
- [ i_ [ class_ $ "fa " <> icon ][]]
- where
- icon = case model of
- Paused -> "fa-play-circle"
- Playing -> "fa-pause-circle"
- el (Arrow act) = button_
- [class_ "button is-large turn-page", onClick act]
- [ img_ [src_ $ ms $ Assets.demo <> image <> ".png"]]
- where image = case act of
- PrevPage -> "prev-page"
- NextPage -> "next-page"
- _ -> "prev-page"
- el (Save c lib) =
- if c `elem` lib then -- in library
- a_ [ class_ $ "wrs-button saved", onClick $ ToggleInLibrary c ]
- [ img_ [ src_ $ ms $ Assets.icon <> "save.svg" ]
- , span_ [] [ text "saved" ]
- ]
- else -- not in library
- a_ [ class_ $ "wrs-button", onClick $ ToggleInLibrary c ]
- [ img_ [ src_ $ ms $ Assets.icon <> "save.svg" ]
- , span_ [] [ text "save" ]
- ]
- el (SaveIcon c lib) =
- if c `elem` lib then -- in library
- button_
- [ class_ "button is-large has-background-black"
- , onClick $ ToggleInLibrary c
- ]
- [ img_ [ src_ $ ms $ Assets.demo <> "library-add.png" ] ]
- else -- not in library
- button_
- [ class_ "button is-large has-background-black-bis"
- , onClick $ ToggleInLibrary c
- ]
- [ img_ [ src_ $ ms $ Assets.demo <> "library-add.png" ] ]
-
- el (ZoomIcon zmodel comic page) = button_
- [ id_ "zoom-button", class_ "button is-large"
- , onClick $ ToggleZoom comic page
- ]
- [ img_ [ src_ $ ms $ Assets.demo <> "zoom.png" ]
- , input_
- [ type_ "range", min_ "0", max_ "100", disabled_ True
- , value_ $ ms (show zmodel :: String)
- , class_ "ctrl", id_ "zoom"
- ]
- , label_
- [ class_ "ctrl", Miso.for_ "zoom" ]
- [ text $ ms $ (show zmodel :: String) ++ "%" ]
- ]
-
- el (Read c) = a_ [ class_ $ "wrs-button", onClick $ SelectExperience c ]
- [ img_ [ src_ $ ms $ Assets.icon <> "read.svg" ]
- , span_ [] [ text "read" ]
- ]
-
- el (Watch c) = a_ [ class_ $ "wrs-button", onClick $ StartWatching c ]
- [ img_ [ src_ $ ms $ Assets.icon <> "watch.svg" ]
- , span_ [] [ text "watch" ]
- ]
-
-data AudioState = Playing | Paused
- deriving (Show, Eq)
-
-type Library = [Comic]
-
-data ComicReaderState
- = NotReading
- | Cover ComicId
- | ChooseExperience ComicId Page
- | Reading ComicReaderView ComicId Page
- | Watching ComicId
- deriving (Show, Eq)
-
-findComic :: ComicId -> [Comic] -> Maybe Comic
-findComic id ls = List.find (\c -> comicId c == id) ls
-
--- | Main model for the app.
---
--- Try to prefix component-specific state with the component initials: 'd' for
--- discover, 'cp' for comic player.
-data Model = Model
- { uri :: URI
- , appComics :: RemoteData MisoString [Comic]
- , userLibrary :: Library
- , dMediaInfo :: Maybe Comic
- , cpState :: ComicReaderState
- , cpAudioState :: AudioState
- , zoomModel :: ZoomModel
- } deriving (Show, Eq)
-
-initModel :: URI -> Model
-initModel uri_ =
- Model { uri = uri_
- , appComics = NotAsked
- , dMediaInfo = Nothing
- , userLibrary = Protolude.empty
- , cpState = detectPlayerState uri_
- , cpAudioState = Paused
- , zoomModel = 100
- }
-
--- | Hacky way to initialize the 'ComicReaderState' from the URI.
-detectPlayerState :: URI -> ComicReaderState
-detectPlayerState u = case List.splitOn "/" $ uriPath u of
- ["", "comic", id, pg, "experience"] -> ChooseExperience id $ toPage pg
- ["", "comic", id, _, "video"] -> Watching id
- ["", "comic", id, pg, "full"] -> Reading Full id $ toPage pg
- ["", "comic", id, pg] -> Reading Spread id $ toPage pg
- ["", "comic", id] -> Cover id
- _ -> NotReading
- where
- toPage pg = fromMaybe 1 (readMaybe pg :: Maybe Page)
-
-type Page = Int
-
-data Action
- = NoOp
- -- comic player stuff
- | SelectExperience Comic
- | StartReading Comic
- | StartWatching Comic
- | NextPage
- | PrevPage
- | ToggleZoom Comic Page
- | ToggleAudio MisoString
- | FetchComics
- | SetComics (RemoteData MisoString [Comic])
- | ToggleFullscreen
- -- discover stuff
- | SetMediaInfo (Maybe Comic)
- | ToggleInLibrary Comic
- -- app stuff
- | ScrollIntoView MisoString
- | HandleURI URI
- | ChangeURI URI
- | DumpModel
- deriving (Show, Eq)
-
-type Discover = "discover" :> View Action
-
-type Home =
- View Action
-
-type ComicCover =
- "comic"
- :> Capture "comicId" ComicId
- :> View Action
-
-type ComicReaderSpread =
- "comic"
- :> Capture "id" ComicId
- :> Capture "page" Page
- :> View Action
-
-type ComicReaderFull =
- "comic"
- :> Capture "id" ComicId
- :> Capture "page" Page
- :> "full"
- :> View Action
-
-type ComicVideo =
- "comic"
- :> Capture "id" ComicId
- :> Capture "page" Page
- :> "video"
- :> View Action
-
-type ChooseExperience =
- "comic"
- :> Capture "id" ComicId
- :> Capture "page" Page
- :> "experience"
- :> View Action
-
-type Login =
- "login" :> View Action
-
-type ClientRoutes = Home
- :<|> ComicCover :<|> ComicReaderSpread :<|> ComicReaderFull :<|> ComicVideo
- :<|> Login :<|> Discover :<|> ChooseExperience
-
-handlers = home
- :<|> comicCover :<|> comicPlayer :<|> comicPlayer :<|> comicPlayer
- :<|> login :<|> discover :<|> comicPlayer
-
-routes :: Proxy ClientRoutes
-routes = Proxy
-
-comicPlayerSpreadProxy :: Proxy ComicReaderSpread
-comicPlayerSpreadProxy = Proxy
-
-comicPlayerFullProxy :: Proxy ComicReaderFull
-comicPlayerFullProxy = Proxy
-
-chooseExperienceProxy :: Proxy ChooseExperience
-chooseExperienceProxy = Proxy
-
-comicProxy :: Proxy ComicCover
-comicProxy = Proxy
-
-comicVideoProxy :: Proxy ComicVideo
-comicVideoProxy = Proxy
-
-homeProxy :: Proxy Home
-homeProxy = Proxy
-
-loginProxy :: Proxy Login
-loginProxy = Proxy
-
-discoverProxy :: Proxy Discover
-discoverProxy = Proxy
-
-home :: Model -> View Action
-home = login
-
-discover :: Model -> View Action
-discover model@(Model { userLibrary = lib}) = template "discover"
- [ topbar
- , main_ [id_ "app-body"] $ case appComics model of
- NotAsked -> [loading]
- Loading -> [loading]
- Failure _ -> [nocomics]
- Success [] -> [nocomics]
- Success (comic:rest) ->
- [ feature comic lib
- , shelf "Recent Releases" (comic:rest)
- , maybeView (flip info lib) $ dMediaInfo model
- ]
- , appmenu
- , discoverFooter
- ]
-
--- | If 'View' had a 'Monoid' instance, then '(text "")' could just be 'mempty'
-maybeView :: (a -> View action) -> Maybe a -> View action
-maybeView f obj = maybe (text "") f obj
-
-mediaInfo :: Maybe Comic -> Library -> View Action
-mediaInfo Nothing _ = text ""
-mediaInfo (Just comic) lib = div_ [ class_ "media-info" ] [ info comic lib ]
-
-appmenu :: View Action
-appmenu = aside_ [ id_ "appmenu" ] $ btn </ links
- where
- links = [ (discoverLink, "discover.svg", "discover")
- , (homeLink, "save.svg", "library")
- , (homeLink, "watch.svg", "videos")
- , (comicLink "1", "read.svg", "comics")
- , (homeLink, "listen.svg", "music")
- ]
- btn (lnk,img,label) = a_
- [ class_ "button"
- , onPreventClick $ ChangeURI $ lnk
- ]
- [ img_ [src_ $ ms $ Assets.icon <> img]
- , span_ [] [ text label ]
- ]
-
--- TODO: make this a loading gif of some sort... maybe the hero icon filling from white to red
-loading :: View Action
-loading = div_ [ class_ "loading" ] [ text "Loading..." ]
-
-nocomics :: View Action
-nocomics = div_ [ class_ "loading" ] [ text "error: no comics found" ]
-
-shelf :: IsMediaObject o => MisoString -> [o] -> View Action
-shelf title comics = div_ [ class_ "shelf" ]
- [ div_ [ class_ "shelf-head" ] [ text title ]
- , ul_ [ class_ "shelf-body" ] $ thumbnail </ comics
- ]
-
-discoverFooter :: View Action
-discoverFooter = footer_
- [ id_ "app-foot"
- , class_ "is-black"
- ]
- [ div_
- [id_ "app-foot-social", css euro]
- [ div_ [class_ "row is-marginless"]
- [ smallImg "facebook.png" $ Just "https://www.facebook.com/musicmeetscomics"
- , smallImg "twitter.png" $ Just "https://twitter.com/musicmeetscomic"
- , smallImg "instagram.png" $ Just "https://www.instagram.com/musicmeetscomics/"
- , smallImg "spotify.png" $ Just "https://open.spotify.com/user/i4ntfg6ganjgxdsylinigcjlq?si=ymWsSkwsT9iaLw2LeAJNNg"
- , smallImg "youtube.png" $ Just "https://www.youtube.com/channel/UCnNPLiuJ1ueo1KTPgHDE7lA/"
- ]
- , div_ [class_ "row"] [ text "Team | Contact Us | Privacy Policy" ]
- ]
- , div_
- [ id_ "app-foot-quote", css euro ]
- [ p_ [] [text "With great power comes great responsiblity."]
- , p_ [] [text "-Stan Lee"]
- ]
- , div_
- [ css euro, id_ "app-foot-logo", onClick DumpModel ]
- [ a_ [ class_ "social-icon", href_ "#" ] [ img_ [ src_ $ ms $ Assets.icon <> "hero-logo.svg" ]]
- , span_ [] [ text "© Com.MusicMeetsComics Records, Inc. All Rights Reserved" ]
- ]
- ]
- where
- attrs Nothing = [ class_ "social-icon" ]
- attrs (Just lnk) = [ class_ "social-icon", href_ lnk, target_ "_blank" ]
- smallImg x lnk = a_ (attrs lnk)
- [ img_ [src_ $ ms $ Assets.cdnEdge <> "/old-assets/images/icons/" <> x ]]
-
-comicCover :: ComicId -> Model -> View Action
-comicCover comicId_ model = comicPlayer comicId_ 1 model
-
-data ComicReaderView = Spread | Full
- deriving (Show, Eq)
-
-comicPlayer :: ComicId -> Page -> Model -> View Action
-comicPlayer _ _ model = case appComics model of
- NotAsked -> loading
- Loading -> loading
- Failure _ -> nocomics
- Success comics -> case cpState model of
- NotReading -> template "comic-player" [ text "error: not reading" ]
- Cover id -> viewOr404 comics comicSpread id 1 model
- ChooseExperience id pg ->
- viewOr404 comics chooseExperiencePage id pg model
- Reading Spread id pg -> viewOr404 comics comicSpread id pg model
- Reading Full id pg -> viewOr404 comics zoomScreen id pg model
- Watching id -> viewOr404 comics comicVideo id 0 model
-
-viewOr404 :: [Comic]
- -> (Comic -> Page -> Model -> View Action)
- -> ComicId -> Page -> Model -> View Action
-viewOr404 comics f id pg model =
- case findComic id comics of
- Just c -> f c pg model
- Nothing -> the404 model
-
-template :: MisoString -> [View Action] -> View Action
-template id rest = div_ [id_ id, class_ "app is-black"] rest
-
-closeButton :: View Action
-closeButton = a_ [ id_ "close-button", onClick $ ChangeURI discoverLink ]
- [ text "x" ]
-
-zoomScreen :: Comic -> Page -> Model -> View Action
-zoomScreen comic page model = template "comic-player"
- [ topbar
- , main_
- [id_ "app-body"]
- [ img_
- [ src_ comicImg
- , class_ "comic-page-full"
- ]
- ]
- , comicControls comic page model
- ]
- where
- comicImg =
- ms Assets.demo
- <> ms (comicSlug comic)
- <> "-"
- <> padLeft page
- <> ".png"
-
-comicSpread :: Comic -> Page -> Model -> View Action
-comicSpread comic page model = template "comic-player"
- [ topbar
- , main_
- [id_ "app-body"]
- [ div_
- [class_ "comic-player"]
- [ img_ [ src_ comicImgLeft, class_ "comic-page" ]
- , img_ [ src_ comicImgRight, class_ "comic-page" ]
- ]
- , closeButton
- ]
- , appmenu
- , comicControls comic page model
- ]
- where
- comicImgLeft, comicImgRight :: MisoString
- comicImgLeft =
- ms Assets.demo
- <> ms (comicSlug comic)
- <> "-"
- <> padLeft page
- <> ".png"
- comicImgRight =
- ms Assets.demo
- <> ms (comicSlug comic)
- <> "-"
- <> (padLeft $ 1 + page)
- <> ".png"
-
-frameborder_ :: MisoString -> Attribute action
-frameborder_ = textProp "frameborder"
-
-allowfullscreen_ :: Bool -> Attribute action
-allowfullscreen_ = boolProp "allowfullscreen"
-
-comicVideo :: Comic -> Page -> Model -> View Action
-comicVideo _ _ _ = template "comic-player"
- [ topbar
- , main_
- [ id_ "app-body" ]
- [ div_ [class_ "comic-video"]
- [ iframe_
- [ src_ "//player.vimeo.com/video/325757560"
- , frameborder_ "0"
- , allowfullscreen_ True
- ]
- []
- ]
- ]
- ]
-
-padLeft :: Int -> MisoString
-padLeft n | n < 10 = ms $ ("0" <> Legacy.show n)
- | otherwise = ms $ Legacy.show n
-
-comicControls :: Comic -> Page -> Model -> View Action
-comicControls comic page model = footer_
- [ id_ "app-foot", class_ "comic-controls" ]
- [ div_
- [ class_ "comic-nav-audio"
- , css $ flexCenter ]
- [ audio_
- [id_ audioId, loop_ True, crossorigin_ "anonymous"]
- [source_ [src_ $ ms $ Assets.demo <> "stars-instrumental.mp3"]]
- , el $ PlayPause audioId $ cpAudioState model
- , span_
- [ css $ euro <> thicc <> smol <> wide ]
- [ text "Experiencing: Original" ]
- ]
- , div_
- [ class_ "comic-controls-pages", css euro ]
- [ el $ Arrow $ PrevPage
- , span_ [] [ text $ leftPage <> "-" <> rightPage <> " of " <> totalpages ]
- , el $ Arrow $ NextPage
- ]
- , div_ [class_ "comic-controls-share"]
- [ el $ SaveIcon comic $ userLibrary model
- , el $ ZoomIcon (zoomModel model) comic page
- , button_
- [class_ "button icon is-large", onClick ToggleFullscreen]
- [i_ [ class_ "fa fa-expand" ] []]
- ]
- ]
- where
- leftPage = ms . Legacy.show $ page
- rightPage = ms . Legacy.show $ 1 + page
- totalpages = ms . Legacy.show $ comicPages comic
-
-login :: Model -> View Action
-login _ = template "login"
- [ div_ [ id_ "login-inner" ]
- [ img_ [ class_ fadeIn
- , src_ $ ms $ Assets.cdnEdge <> "/old-assets/images/icons/hero-large.png"
- ]
- , hr_ [class_ fadeIn]
- , form_ [class_ fadeIn]
- [ ctrl [class_ "input", type_ "email", placeholder_ "Email"]
- , ctrl [class_ "input", type_ "password", placeholder_ "Password"]
- , div_ [class_ "action", css euro]
- [ div_ [class_ "checkbox remember-me"]
- [ input_ [type_ "checkbox"]
- , label_ [Miso.for_ "checkbox"] [text "Remember Me"]
- ]
- , div_ [class_ "button is-black", onClick $ ChangeURI discoverLink]
- [ text "Login" ]
- ]
- ]
- , hr_ [class_ fadeIn]
- , p_ [ class_ $ "help " <> fadeIn ]
- [ a_ [href_ "#"][text "Forgot your username or password?"]
- , a_ [href_ "#"][text "Don't have an account? Sign Up"]
- ]
- , img_ [ id_ "hero-logo"
- , class_ "blur-out"
- , src_ $ ms $ Assets.cdnEdge <> "/old-assets/images/icons/success-her-image.png"
- ]
- ]
- ]
- where
- fadeIn = "animated fadeIn delay-2s"
- ctrl x = div_ [class_ "control"] [ input_ x ]
-
-chooseExperiencePage :: Comic -> Page -> Model -> View Action
-chooseExperiencePage comic page model = template "choose-experience"
- [ topbar
- , main_ [ id_ "app-body" ]
- [ h2_ [] [ text "Choose Your Musical Experience" ]
- , p_ [] [ text experienceBlurb ]
- , ul_ [] $ li comic </ experiences
- ]
- , appmenu
- , comicControls comic page model
- ]
- where
- li c (name, artist, track) = li_
- [ onClick $ StartReading c ]
- [ div_ []
- [ img_ [ src_ $ ms $ Assets.demo <> name <> ".png" ]
- , span_ [] [ text $ ms name ]
- ]
- , span_ [ css $ thicc ] [ text $ ms artist ]
- , span_ [] [ text $ ms track ]
- ]
- experiences :: [(Text, Text, Text)]
- experiences =
- [ ("comedic", "RxGF", "Soft Reveal")
- , ("dark", "Logan Henderson", "Speak of the Devil")
- , ("original", "Mehcad Brooks", "Stars")
- , ("energetic", "Skela", "What's wrong with me")
- , ("dramatic", "Josh Jacobson", "Sideline")
- ]
-
-
-experienceBlurb :: MisoString
-experienceBlurb = [s|
-As you enter the world of Hero, you will find that music and visual art have a
-symbiotic relationship that can only be experienced, not described. Here, choose
-the tonality of the experience you wish to adventure on, whether it's a comedic,
-dark, energetic or dramatic. Feeling indecisive? Let us navigate your journey
-with the original curated music for this piece of visual art.
-|]
-
-topbar :: View Action
-topbar = header_
- [id_ "app-head", class_ "is-black", css euro]
- [ a_
- [class_ "button is-medium is-black", onClick $ ChangeURI homeLink]
- [img_ [src_ $ ms $ Assets.icon <> "hero-logo.svg"]]
- , div_
- [id_ "app-head-right"]
- [ button_ [class_ "button icon is-medium is-black"]
- [i_ [class_ "fas fa-search" ] []]
- , button_ [ class_ "button is-medium is-black is-size-7"
- , css $ euro <> wide <> thicc
- ]
- [text "News"]
- , span_ [ class_ "icon is-large" ]
- [ i_ [ class_ "fas fa-user" ] []
- ]
- ]
- ]
-
-row :: [View Action] -> View Action
-row = div_ [ css $ Clay.display Clay.flex <> Clay.flexDirection Clay.row ]
-
-column :: [View Action] -> View Action
-column = div_ [ css $ Clay.display Clay.flex <> Clay.flexDirection Clay.column ]
-
--- | Links
-
-comicLink :: ComicId -> URI
-comicLink comicId_ = linkURI $ safeLink routes comicProxy $ comicId_
-
-comicPlayerSpreadLink :: ComicId -> Page -> URI
-comicPlayerSpreadLink id page =
- linkURI $ safeLink routes comicPlayerSpreadProxy id page
-
-comicPlayerFullLink :: ComicId -> Page -> URI
-comicPlayerFullLink id page =
- linkURI $ safeLink routes comicPlayerFullProxy id page
-
-comicVideoLink :: ComicId -> Page -> URI
-comicVideoLink id page =
- linkURI $ safeLink routes comicVideoProxy id page
-
-homeLink :: URI
-homeLink = linkURI $ safeLink routes homeProxy
-
-loginLink :: URI
-loginLink = linkURI $ safeLink routes loginProxy
-
-discoverLink :: URI
-discoverLink = linkURI $ safeLink routes discoverProxy
-
-the404 :: Model -> View Action
-the404 _ = template "404" [p_ [] [text "Not found"]]
-
-chooseExperienceLink :: ComicId -> Page -> URI
-chooseExperienceLink id page =
- linkURI $ safeLink routes chooseExperienceProxy id page