implement server-side alert closing
Rather than using bootstrap's client-side closing. Now closed alerts stay closed.
This commit is contained in:
parent
1f671ee40c
commit
a994130843
5 changed files with 30 additions and 103 deletions
|
@ -37,7 +37,15 @@ data Alert = Alert
|
||||||
}
|
}
|
||||||
|
|
||||||
{- Higher AlertId indicates a more recent alert. -}
|
{- Higher AlertId indicates a more recent alert. -}
|
||||||
type AlertId = Integer
|
newtype AlertId = AlertId Integer
|
||||||
|
deriving (Read, Show, Eq, Ord)
|
||||||
|
|
||||||
|
{- Note: This first alert id is used for yesod's message. -}
|
||||||
|
firstAlertId :: AlertId
|
||||||
|
firstAlertId = AlertId 0
|
||||||
|
|
||||||
|
nextAlertId :: AlertId -> AlertId
|
||||||
|
nextAlertId (AlertId i) = AlertId $ succ i
|
||||||
|
|
||||||
type AlertPair = (AlertId, Alert)
|
type AlertPair = (AlertId, Alert)
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ newDaemonStatus = DaemonStatus
|
||||||
<*> pure Nothing
|
<*> pure Nothing
|
||||||
<*> pure M.empty
|
<*> pure M.empty
|
||||||
<*> pure M.empty
|
<*> pure M.empty
|
||||||
<*> pure 0
|
<*> pure firstAlertId
|
||||||
<*> pure []
|
<*> pure []
|
||||||
<*> newNotificationBroadcaster
|
<*> newNotificationBroadcaster
|
||||||
<*> newNotificationBroadcaster
|
<*> newNotificationBroadcaster
|
||||||
|
@ -217,7 +217,7 @@ addAlert dstatus alert = notifyAlert dstatus `after` modifyDaemonStatus dstatus
|
||||||
where
|
where
|
||||||
go s = (s { alertMax = i, alertMap = m }, i)
|
go s = (s { alertMax = i, alertMap = m }, i)
|
||||||
where
|
where
|
||||||
i = alertMax s + 1
|
i = nextAlertId $ alertMax s
|
||||||
m = M.insertWith' const i alert (alertMap s)
|
m = M.insertWith' const i alert (alertMap s)
|
||||||
|
|
||||||
removeAlert :: DaemonStatusHandle -> AlertId -> IO ()
|
removeAlert :: DaemonStatusHandle -> AlertId -> IO ()
|
||||||
|
|
|
@ -93,6 +93,7 @@ mkYesod "WebApp" [parseRoutes|
|
||||||
/noscriptauto NoScriptAutoR GET
|
/noscriptauto NoScriptAutoR GET
|
||||||
/transfers/#NotificationId TransfersR GET
|
/transfers/#NotificationId TransfersR GET
|
||||||
/sidebar/#NotificationId SideBarR GET
|
/sidebar/#NotificationId SideBarR GET
|
||||||
|
/closealert/#AlertId CloseAlert GET
|
||||||
/config ConfigR GET
|
/config ConfigR GET
|
||||||
/addrepository AddRepositoryR GET
|
/addrepository AddRepositoryR GET
|
||||||
/static StaticR Static getStatic
|
/static StaticR Static getStatic
|
||||||
|
@ -102,6 +103,10 @@ instance PathPiece NotificationId where
|
||||||
toPathPiece = pack . show
|
toPathPiece = pack . show
|
||||||
fromPathPiece = readish . unpack
|
fromPathPiece = readish . unpack
|
||||||
|
|
||||||
|
instance PathPiece AlertId where
|
||||||
|
toPathPiece = pack . show
|
||||||
|
fromPathPiece = readish . unpack
|
||||||
|
|
||||||
instance Yesod WebApp where
|
instance Yesod WebApp where
|
||||||
defaultLayout content = do
|
defaultLayout content = do
|
||||||
webapp <- getYesod
|
webapp <- getYesod
|
||||||
|
@ -110,7 +115,6 @@ instance Yesod WebApp where
|
||||||
addStylesheet $ StaticR css_bootstrap_responsive_css
|
addStylesheet $ StaticR css_bootstrap_responsive_css
|
||||||
addScript $ StaticR jquery_full_js
|
addScript $ StaticR jquery_full_js
|
||||||
addScript $ StaticR js_bootstrap_dropdown_js
|
addScript $ StaticR js_bootstrap_dropdown_js
|
||||||
addScript $ StaticR js_bootstrap_alert_js
|
|
||||||
$(widgetFile "page")
|
$(widgetFile "page")
|
||||||
hamletToRepHtml $(hamletFile $ hamletTemplate "bootstrap")
|
hamletToRepHtml $(hamletFile $ hamletTemplate "bootstrap")
|
||||||
|
|
||||||
|
@ -229,7 +233,7 @@ sideBarDisplay noScript = do
|
||||||
bootstrapclass Message = "alert-info"
|
bootstrapclass Message = "alert-info"
|
||||||
|
|
||||||
renderalert (alertid, alert) = addalert
|
renderalert (alertid, alert) = addalert
|
||||||
(show alertid)
|
alertid
|
||||||
(alertClosable alert)
|
(alertClosable alert)
|
||||||
(alertBlockDisplay alert)
|
(alertBlockDisplay alert)
|
||||||
(bootstrapclass $ alertClass alert)
|
(bootstrapclass $ alertClass alert)
|
||||||
|
@ -238,11 +242,14 @@ sideBarDisplay noScript = do
|
||||||
StringAlert s -> [whamlet|#{s}|]
|
StringAlert s -> [whamlet|#{s}|]
|
||||||
WidgetAlert w -> w alert
|
WidgetAlert w -> w alert
|
||||||
|
|
||||||
rendermessage msg = addalert "yesodmessage" True False
|
rendermessage msg = addalert firstAlertId True False
|
||||||
"alert-info" Nothing [whamlet|#{msg}|]
|
"alert-info" Nothing [whamlet|#{msg}|]
|
||||||
|
|
||||||
addalert :: String -> Bool -> Bool -> Text -> Maybe String -> Widget -> Widget
|
addalert :: AlertId -> Bool -> Bool -> Text -> Maybe String -> Widget -> Widget
|
||||||
addalert alertid closable block divclass heading widget = $(widgetFile "alert")
|
addalert i closable block divclass heading widget = do
|
||||||
|
let alertid = show i
|
||||||
|
let closealert = CloseAlert i
|
||||||
|
$(widgetFile "alert")
|
||||||
|
|
||||||
{- Called by client to get a sidebar display.
|
{- Called by client to get a sidebar display.
|
||||||
-
|
-
|
||||||
|
@ -259,6 +266,12 @@ getSideBarR nid = do
|
||||||
page <- widgetToPageContent $ sideBarDisplay True
|
page <- widgetToPageContent $ sideBarDisplay True
|
||||||
hamletToRepHtml $ [hamlet|^{pageBody page}|]
|
hamletToRepHtml $ [hamlet|^{pageBody page}|]
|
||||||
|
|
||||||
|
{- Called by the client to close an alert. -}
|
||||||
|
getCloseAlert :: AlertId -> Handler ()
|
||||||
|
getCloseAlert i = do
|
||||||
|
webapp <- getYesod
|
||||||
|
void $ liftIO $ removeAlert (daemonStatus webapp) i
|
||||||
|
|
||||||
dashboard :: Bool -> Bool -> Widget
|
dashboard :: Bool -> Bool -> Widget
|
||||||
dashboard noScript warnNoScript = do
|
dashboard noScript warnNoScript = do
|
||||||
sideBarDisplay noScript
|
sideBarDisplay noScript
|
||||||
|
|
94
static/js/bootstrap-alert.js
vendored
94
static/js/bootstrap-alert.js
vendored
|
@ -1,94 +0,0 @@
|
||||||
/* ==========================================================
|
|
||||||
* bootstrap-alert.js v2.0.2
|
|
||||||
* http://twitter.github.com/bootstrap/javascript.html#alerts
|
|
||||||
* ==========================================================
|
|
||||||
* Copyright 2012 Twitter, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* ========================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
!function( $ ){
|
|
||||||
|
|
||||||
"use strict"
|
|
||||||
|
|
||||||
/* ALERT CLASS DEFINITION
|
|
||||||
* ====================== */
|
|
||||||
|
|
||||||
var dismiss = '[data-dismiss="alert"]'
|
|
||||||
, Alert = function ( el ) {
|
|
||||||
$(el).on('click', dismiss, this.close)
|
|
||||||
}
|
|
||||||
|
|
||||||
Alert.prototype = {
|
|
||||||
|
|
||||||
constructor: Alert
|
|
||||||
|
|
||||||
, close: function ( e ) {
|
|
||||||
var $this = $(this)
|
|
||||||
, selector = $this.attr('data-target')
|
|
||||||
, $parent
|
|
||||||
|
|
||||||
if (!selector) {
|
|
||||||
selector = $this.attr('href')
|
|
||||||
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
|
|
||||||
}
|
|
||||||
|
|
||||||
$parent = $(selector)
|
|
||||||
$parent.trigger('close')
|
|
||||||
|
|
||||||
e && e.preventDefault()
|
|
||||||
|
|
||||||
$parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
|
|
||||||
|
|
||||||
$parent
|
|
||||||
.trigger('close')
|
|
||||||
.removeClass('in')
|
|
||||||
|
|
||||||
function removeElement() {
|
|
||||||
$parent
|
|
||||||
.trigger('closed')
|
|
||||||
.remove()
|
|
||||||
}
|
|
||||||
|
|
||||||
$.support.transition && $parent.hasClass('fade') ?
|
|
||||||
$parent.on($.support.transition.end, removeElement) :
|
|
||||||
removeElement()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ALERT PLUGIN DEFINITION
|
|
||||||
* ======================= */
|
|
||||||
|
|
||||||
$.fn.alert = function ( option ) {
|
|
||||||
return this.each(function () {
|
|
||||||
var $this = $(this)
|
|
||||||
, data = $this.data('alert')
|
|
||||||
if (!data) $this.data('alert', (data = new Alert(this)))
|
|
||||||
if (typeof option == 'string') data[option].call($this)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$.fn.alert.Constructor = Alert
|
|
||||||
|
|
||||||
|
|
||||||
/* ALERT DATA-API
|
|
||||||
* ============== */
|
|
||||||
|
|
||||||
$(function () {
|
|
||||||
$('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
|
|
||||||
})
|
|
||||||
|
|
||||||
}( window.jQuery );
|
|
|
@ -1,4 +1,4 @@
|
||||||
<div .alert .fade .in .#{divclass} :block:.alert-block ##{alertid}>
|
<div .alert .fade .in .#{divclass} :block:.alert-block ##{alertid} :closable:onclick="(function( $ ) { $.get('@{closealert}') })( jQuery );">
|
||||||
$if closable
|
$if closable
|
||||||
<a .close data-dismiss="alert" href="#">×</a>
|
<a .close data-dismiss="alert" href="#">×</a>
|
||||||
$maybe h <- heading
|
$maybe h <- heading
|
||||||
|
|
Loading…
Reference in a new issue