cleaned up refreshing code into a widget

Very happy to have a reusable autoUpdate widget that can make any Yesod
widget automatically refresh!

Also added support for non-javascript browsers, falling back to meta
refresh.

Also, the home page is now rendered with the webapp status on it, before
any refreshing is done.
This commit is contained in:
Joey Hess 2012-07-26 21:03:46 -04:00
parent 9b2eec2e7a
commit f5ef46d01e
4 changed files with 62 additions and 16 deletions

View file

@ -41,7 +41,7 @@ staticFiles "static"
mkYesod "WebApp" [parseRoutes|
/ HomeR GET
/poll PollR GET
/status StatusR GET
/config ConfigR GET
/static StaticR Static getStatic
|]
@ -66,23 +66,63 @@ instance Yesod WebApp where
makeSessionBackend = webAppSessionBackend
jsLoader _ = BottomOfHeadBlocking
{- Add to any widget to make it auto-update.
-
- The widget should have a html element with id=poll, which will be
- replaced when it's updated.
-
- Updating is done by getting html from the gethtml route.
- Or, the home route is used if the whole page has to be refreshed to
- update.
-
- ms_delay is how long to delay between updates.
- ms_startdelay is how long to delay before updating the widget at the
- state.
-}
autoUpdate :: Text -> Route WebApp -> Route WebApp -> Int -> Int -> Widget
autoUpdate poll gethtml home ms_delay ms_startdelay = do
{- Fallback refreshing is provided for non-javascript browsers. -}
let delayseconds = show $ ms_to_seconds ms_delay
toWidgetHead $(hamletFile $ hamletTemplate "metarefresh")
{- Use long polling to update the status display. -}
let delay = show ms_delay
let startdelay = show ms_startdelay
addScriptRemote "http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"
toWidgetHead $(juliusFile $ juliusTemplate "longpolling")
where
ms_to_seconds :: Int -> Int
ms_to_seconds ms = ceiling ((fromIntegral ms :: Double) / 1000)
{- Continually updating status display. -}
statusDisplay :: Widget
statusDisplay = do
webapp <- lift getYesod
time <- show <$> liftIO getCurrentTime
poll <- lift newIdent
$(whamletFile $ hamletTemplate "status")
autoUpdate poll StatusR HomeR (3000 :: Int) (40 :: Int)
getHomeR :: Handler RepHtml
getHomeR = defaultLayout $ do
[whamlet|<div id="poll">Starting ...|]
addScriptRemote "http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"
toWidgetBody $(juliusFile $ juliusTemplate "longpolling")
statusDisplay
[whamlet|<p><a href="@{ConfigR}">config|]
{- Called by client to poll for a new webapp status display.
-
- Should block until the status has changed, and then return a div
- containing the new status, which will be inserted into the calling page.
-
- Note that the head of the widget is not included, only its
- body is. To get the widget head content, the widget is also
- inserted onto the getHomeR page.
-}
getPollR :: Handler RepHtml
getPollR = do
webapp <- getYesod
time <- show <$> liftIO getCurrentTime
hamletToRepHtml $(hamletFile $ hamletTemplate "poll")
getStatusR :: Handler RepHtml
getStatusR = do
page <- widgetToPageContent statusDisplay
hamletToRepHtml $ [hamlet|^{pageBody page}|]
getConfigR :: Handler RepHtml
getConfigR = defaultLayout $ do

View file

@ -1,5 +1,9 @@
// Uses long-polling to update a div with id=poll.
// The PollR route should return a new div, also with id=poll.
// Uses long-polling to update a div with id=#{poll}
// The gethtml route should return a new div, with the same id.
//
// Maximum update frequency is controlled by #{startdelay}
// and #{delay}, both in milliseconds.
(function( $ ) {
@ -7,11 +11,11 @@ $.LongPoll = (function() {
return {
send : function() {
$.ajax({
'url': '@{PollR}',
'url': '@{gethtml}',
'dataType': 'html',
'success': function(data, status, jqxhr) {
$('#poll').replaceWith(data);
setTimeout($.LongPoll.send, 3000);
$('##{poll}').replaceWith(data);
setTimeout($.LongPoll.send, #{delay});
},
});
}
@ -19,7 +23,7 @@ $.LongPoll = (function() {
}());
$(document).bind('ready.app', function() {
setTimeout($.LongPoll.send, 40);
setTimeout($.LongPoll.send, #{startdelay});
});
})( jQuery );

View file

@ -0,0 +1,2 @@
<noscript>
<meta http-equiv="refresh" content="#{delayseconds}; URL=@{home}">

View file

@ -1,2 +1,2 @@
<div id="poll">
<div id="#{poll}">
polled at #{time}