[actual working timer Ganesh Sittampalam **20200713065909 Ignore-this: a0d8f1abef9aa3da81687d7c48d88f28845c05fd4694bbe55c33352c9cce904af06b58f691a56b8a ] hunk ./household.cabal 25 + ,servant-js hunk ./household.cabal 29 - ,wai-cors hunk ./household.cabal 31 + ,text hunk ./src/Core.hs 4 +import Control.Concurrent.MVar hunk ./src/Core.hs 9 +import Data.Text hunk ./src/Core.hs 16 +import Servant.JS hunk ./src/Core.hs 19 -import Network.Wai.Middleware.Cors hunk ./src/Core.hs 20 -data TimerState = - TimerState +data TimeLeft = + TimeLeft hunk ./src/Core.hs 25 - deriving (Generic, ToJSON, FromJSON) - -type Api = - "timer" :> Get '[JSON] TimerState - -theServer :: Server Api -theServer = do - theTime <- liftIO getCurrentTime - return (TimerState 1 (fromIntegral (diffTimeToPicoseconds (utctDayTime theTime) `div` 1000000000000 `mod` 60))) - -application :: Application -application = simpleCors $ serve (Proxy @Api) theServer + deriving (Generic, ToJSON, FromJSON, Show) + +data TimerState = Stopped NominalDiffTime | RunningTill UTCTime + +type Api = TimerApi :<|> JSApi :<|> Raw + +type TimerApi = + "timer" :> + ( + "current" :> (Get '[JSON] TimeLeft :<|> ReqBody '[JSON] TimeLeft :> PutNoContent) + :<|> "stop" :> PostNoContent + :<|> "start" :> PostNoContent + ) + +type JSApi = "js" :> ("api" :> ("vanilla" :> Get '[PlainText] Text :<|> "jquery" :> Get '[PlainText] Text)) + +getCurrent :: MVar TimerState -> Handler TimeLeft +getCurrent state = do + timerState <- liftIO $ readMVar state + timeLeft <- + case timerState of + Stopped timeLeft -> return timeLeft + RunningTill endTime -> do + theTime <- liftIO getCurrentTime + return $ max 0 (endTime `diffUTCTime` theTime) + return $ uncurry TimeLeft $ floor timeLeft `divMod` 60 + +setCurrent :: MVar TimerState -> TimeLeft -> Handler NoContent +setCurrent state newTime = do + liftIO $ do + putStrLn $ "setCurrent: " ++ show newTime + modifyMVar_ state $ \_ -> + return $ Stopped $ fromIntegral (60 * timeLeftMins newTime + timeLeftSecs newTime) + return NoContent + +startTimer :: MVar TimerState -> Handler NoContent +startTimer state = do + liftIO $ do + putStrLn $ "startTimer" + modifyMVar_ state $ \timerState -> + case timerState of + RunningTill {} -> return timerState + Stopped timeLeft -> do + theTime <- liftIO getCurrentTime + return $ RunningTill $ timeLeft `addUTCTime` theTime + return NoContent + +stopTimer :: MVar TimerState -> Handler NoContent +stopTimer state = do + liftIO $ do + putStrLn $ "stopTimer" + modifyMVar_ state $ \timerState -> + case timerState of + Stopped {} -> return timerState + RunningTill endTime -> do + theTime <- liftIO getCurrentTime + return $ Stopped $ max 0 $ endTime `diffUTCTime` theTime + return NoContent + +jsCode :: JavaScriptGenerator -> Handler Text +jsCode thing = Prelude.return (jsForAPI (Proxy @TimerApi) thing) + +js :: Server JSApi +js = jsCode vanillaJS :<|> jsCode jquery + +theServer :: MVar TimerState -> Server Api +theServer state = + ((getCurrent state :<|> setCurrent state) :<|> stopTimer state :<|> startTimer state) + :<|> js + :<|> serveDirectoryFileServer "static/" + +application :: MVar TimerState -> Application +application state = serve (Proxy @Api) (theServer state) hunk ./src/Core.hs 102 - race_ (run port application) $ do + state <- newMVar (Stopped 0) + race_ (run port (application state)) $ do hunk ./src/Core.hs 108 - hunk ./static/timer.html 12 - + hunk ./static/timer.html 17 + + + hunk ./static/timer.html 21 + function set(mins, secs) { + putTimerCurrent({timeLeftMins : mins, timeLeftSecs : secs}, () => {}, () => {}); + } + function start() { + postTimerStart(() => {}, () => {}); + } + function stop() { + postTimerStop(() => {}, () => {}); + } hunk ./static/timer.html 48 - fetch("http://localhost:8001/timer") + fetch("/timer/current") hunk ./static/timer.html 66 - return
{payload.timeLeftMins}:{payload.timeLeftSecs}
; + var formattedSeconds = ("0" + payload.timeLeftSecs).slice(-2); + return
{payload.timeLeftMins}:{formattedSeconds}
;