50 min mp3s ◦ ...and seek forward 5/10/30 seconds ◦ ...and seek backward 5/10/30 seconds • Haven’t found anything I like • Don’t want to make native app • Want it to be easy to update
for audio to be played back on browser • What I need: ◦ Way to use local files for <audio> sources ◦ Use built-in play/pause, seek ◦ Need to be able to get and set current time
internet of shit) • Reuse and adapt existing libraries ◦ E.g. purescript-echarts - do you really want to rewrite this? ▪ Purescript-echarts • 9 people 115 commits from Jan 2015 ▪ Baidu EFE echarts • 37 people 3780 commits from May 2013 Why JS and easy FFI?
Based on common ideas, with user-defined: ◦ State types ◦ Query type ◦ Input types (when a component used as child) ◦ Output message types • Render of State -> HTML Query ◦ Parameterized HTML since before even Elm got on board • Query eval eval :: forall m. Query ~> H.ComponentDSL State Query Message m
a newtype for ObjectURL The only sensible state to keep is our file ObjectURL Then we only need to handle two query types: setting a file and skipping newtype ObjectURL = ObjectURL String type State = { file :: Maybe ObjectURL } data Query a = FileSet a | Skip SkipDir SkipSize a data SkipDir = Bck | Fwd data SkipSize = Sm | Md | Lg
the core row of effects in our application In our app, we’ll be writing to the console and using DOM methods a lot Of course, some qualified imports to keep in mind type AppEffects eff = ( console :: CONSOLE , dom :: DOM | eff ) import Halogen as H import Halogen.Aff as HA import Halogen.HTML as HH import Halogen.HTML.Events as HE import Halogen.HTML.Properties as HP import Halogen.VDom.Driver as D
for this project, the params are simple: • HTML • Our Query type • Input type (no inputs) • Output type (no outputs) • Our type for non-component effects, using Aff for asynchronous effects ui :: forall eff. H.Component HH.HTML Query Unit Void (Aff (AppEffects eff))
state is defined simply as such, and the rest we will define as we go along Receiver used for inputs, which we won’t be using ui = H.component { initialState: const initialState , render , eval , receiver: const Nothing } where initialState = { file: Nothing }
Query into the Component DSL using our State, Query, no outgoing messages, and non-component Aff of AppEffects eff. eval :: Query ~> H.ComponentDSL State Query Void (Aff (AppEffects eff))
way possible Uses the ref we defined earlier to get the generic HTML element Then uses purescript-dom-classy to read the input as an input element On success, we then continue handling our input eval (FileSet next) = do input <- H.getHTMLElementRef $ wrap "input" case fromHTMLElement =<< input of Nothing -> log' "No input ref found" Just el -> handleInput el pure next
viewing pleasure window.URL is used create an object url string from it and set it to our state This is then put into the component state using modify handleInput el = -- ... handleFile file = do url <- H.liftEff $ url =<< window blob <- H.liftEff $ createObjectURL file url -- ... H.modify \s -> s {file = pure $ wrap blob}
need to find this element and set the time eval (Skip dir size next) = do audio <- H.getHTMLElementRef $ wrap "audio" case htmlAudioElementToHTMLMediaElement <$> (fromHTMLElement =<< audio) of Just el -> do current <- H.liftEff $ currentTime el H.liftEff $ setCurrentTime (current + delta) el _ -> log' "No audio ref found" pure next
We grab the document.body and run our component on it That’s it! main = HA.runHalogenAff do body <- HA.awaitBody io <- D.runUI ui unit body log "Running"