From ecb59e9c3154dd0b980dba8f576d487da2f33dcc Mon Sep 17 00:00:00 2001 From: Winston Chang Date: Mon, 21 Mar 2016 16:44:04 -0500 Subject: [PATCH] Add R notification functions --- DESCRIPTION | 1 + NAMESPACE | 2 + R/notifications.R | 87 +++++++++++++++++++++++++++++++++++++++++ R/shiny.R | 5 +++ man/showNotification.Rd | 78 ++++++++++++++++++++++++++++++++++++ srcjs/shinyapp.js | 9 +++++ 6 files changed, 182 insertions(+) create mode 100644 R/notifications.R create mode 100644 man/showNotification.Rd diff --git a/DESCRIPTION b/DESCRIPTION index a9cdec505..0934ff66a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -121,6 +121,7 @@ Collate: 'middleware-shiny.R' 'middleware.R' 'modules.R' + 'notifications.R' 'priorityqueue.R' 'progress.R' 'react.R' diff --git a/NAMESPACE b/NAMESPACE index 7e84bbe79..1bc65b0d3 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -155,6 +155,7 @@ export(reactiveValues) export(reactiveValuesToList) export(registerInputHandler) export(removeInputHandler) +export(removeNotification) export(renderDataTable) export(renderImage) export(renderPlot) @@ -179,6 +180,7 @@ export(shinyAppDir) export(shinyAppFile) export(shinyServer) export(shinyUI) +export(showNotification) export(showReactLog) export(sidebarLayout) export(sidebarPanel) diff --git a/R/notifications.R b/R/notifications.R new file mode 100644 index 000000000..42e0858f0 --- /dev/null +++ b/R/notifications.R @@ -0,0 +1,87 @@ +#' Show or remove a notification +#' +#' These functions show and remove notifications in a Shiny application. +#' +#' @param html HTML content of message. +#' @param duration Number of seconds to display the message before it +#' disappears. Use \code{NULL} to make the message not automatically +#' disappear. +#' @param closeButton If \code{TRUE}, display a button which will make the +#' notification disappear when clicked. If \code{FALSE} do not display. +#' @param id An ID string. This can be used to change the contents of an +#' existing message with \code{showNotification}, or to remove it with +#' \code{removeNotification}. If not provided, one will be generated +#' automatically. +#' @param session Session object to send notification to. +#' +#' @return An ID for the notification. +#' +#' @examples +#' if (interactive()) { +#' # Show a message when button is clicked +#' shinyApp( +#' ui = fluidPage( +#' actionButton("show", "Show") +#' ), +#' server = function(input, output) { +#' observeEvent(input$show, { +#' showNotification("Message text") +#' }) +#' } +#' ) +#' +#' # App with show and remove buttons +#' shinyApp( +#' ui = fluidPage( +#' actionButton("show", "Show"), +#' actionButton("remove", "Remove") +#' ), +#' server = function(input, output) { +#' # A queue of notification IDs +#' ids <- character(0) +#' # A counter +#' n <- 0 +#' +#' observeEvent(input$show, { +#' # Save the ID for removal later +#' id <- showNotification(paste("Message", n), duration = NULL) +#' ids <<- c(ids, id) +#' n <<- n + 1 +#' }) +#' +#' observeEvent(input$remove, { +#' if (length(ids) > 0) +#' removeNotification(ids[1]) +#' ids <<- ids[-1] +#' }) +#' } +#' ) +#' } +#' @export +showNotification <- function(html, duration = 5, closeButton = TRUE, + id = NULL, session = getDefaultReactiveDomain()) { + + if (is.null(id)) + id <- randomID() + + session$sendNotification("show", + list( + html = as.character(html), + duration = if (!is.null(duration)) duration * 1000, + closeButton = closeButton, + id = id + ) + ) + + id +} + +#' @rdname showNotification +#' @export +removeNotification <- function(id = NULL, session = getDefaultReactiveDomain()) { + if (is.null(id)) { + stop("id is required.") + } + session$sendNotification("remove", id) + id +} diff --git a/R/shiny.R b/R/shiny.R index 076313e0f..51d2f9544 100644 --- a/R/shiny.R +++ b/R/shiny.R @@ -685,6 +685,11 @@ ShinySession <- R6Class( progress = list(type = type, message = message) ) }, + sendNotification = function(type, message) { + private$sendMessage( + notification = list(type = type, message = message) + ) + }, dispatch = function(msg) { method <- paste('@', msg$method, sep='') func <- try(self[[method]], silent = TRUE) diff --git a/man/showNotification.Rd b/man/showNotification.Rd new file mode 100644 index 000000000..f38df20a1 --- /dev/null +++ b/man/showNotification.Rd @@ -0,0 +1,78 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/notifications.R +\name{showNotification} +\alias{removeNotification} +\alias{showNotification} +\title{Show or remove a notification} +\usage{ +showNotification(html, duration = 5, closeButton = TRUE, id = NULL, + session = getDefaultReactiveDomain()) + +removeNotification(id = NULL, session = getDefaultReactiveDomain()) +} +\arguments{ +\item{html}{HTML content of message.} + +\item{duration}{Number of seconds to display the message before it +disappears. Use \code{NULL} to make the message not automatically +disappear.} + +\item{closeButton}{If \code{TRUE}, display a button which will make the +notification disappear when clicked. If \code{FALSE} do not display.} + +\item{id}{An ID string. This can be used to change the contents of an +existing message with \code{showNotification}, or to remove it with +\code{removeNotification}. If not provided, one will be generated +automatically.} + +\item{session}{Session object to send notification to.} +} +\value{ +An ID for the notification. +} +\description{ +These functions show and remove notifications in a Shiny application. +} +\examples{ +if (interactive()) { +# Show a message when button is clicked +shinyApp( + ui = fluidPage( + actionButton("show", "Show") + ), + server = function(input, output) { + observeEvent(input$show, { + showNotification("Message text") + }) + } +) + +# App with show and remove buttons +shinyApp( + ui = fluidPage( + actionButton("show", "Show"), + actionButton("remove", "Remove") + ), + server = function(input, output) { + # A queue of notification IDs + ids <- character(0) + # A counter + n <- 0 + + observeEvent(input$show, { + # Save the ID for removal later + id <- showNotification(paste("Message", n), duration = NULL) + ids <<- c(ids, id) + n <<- n + 1 + }) + + observeEvent(input$remove, { + if (length(ids) > 0) + removeNotification(ids[1]) + ids <<- ids[-1] + }) + } +) +} +} + diff --git a/srcjs/shinyapp.js b/srcjs/shinyapp.js index c7a69a69a..1d35ac388 100644 --- a/srcjs/shinyapp.js +++ b/srcjs/shinyapp.js @@ -468,6 +468,15 @@ var ShinyApp = function() { } }); + addMessageHandler('notification', function(message) { + if (message.type === 'show') + exports.notifications.show(message.message); + else if (message.type === 'remove') + exports.notifications.remove(message.message); + else + throw('Unkown notification type: ' + message.type); + }); + addMessageHandler('response', function(message) { var requestId = message.tag; var request = this.$activeRequests[requestId];