Nobody likes it when an app stops responding. Traditionally, Shiny
apps will be “grayed out”. Since this doesn’t provide a nice user
experience, {shinyMobile}
adds a custom disconnected
screen. This screen (a toast) is displayed when the app is disconnected
from the server.
library(shiny)
library(shinyMobile)
shinyApp(
ui = f7Page(
options = list(dark = FALSE),
title = "My app",
f7SingleLayout(
navbar = f7Navbar(
title = "Custom disconnected screen",
hairline = FALSE
),
f7List(
inset = TRUE,
dividers = FALSE,
strong = TRUE,
f7Text(
inputId = "text",
label = "Text input",
value = "Some text",
placeholder = "Your text here"
),
f7Select(
inputId = "select",
label = "Make a choice",
choices = 1:3,
selected = 1
)
),
f7Block(
f7Button(inputId = "disconnect",
label = "Disconnect me")
),
f7Block(
verbatimTextOutput("mytext"),
verbatimTextOutput("myselect")
)
)
),
server = function(input, output) {
# set ignoreInit to avoid the observer being triggered after reconnect
# this happens because of restoring the app's state
observeEvent(input$disconnect, ignoreInit = TRUE, {
print(input$disconnect)
stop("Oops, I disconnected you!")
})
output$mytext <- renderText({
input$text
})
output$myselect <- renderText({
input$select
})
}
)
The user is given two options: either to reconnect or to reload the app. There’s a subtle difference between the two, which is explained below.
This button will attempt to reconnect to the server. If the server is not available, or encounters an error again, the custom disconnected screen will be displayed again.
When reconnecting, Shiny attempts to restore the app to its previous
state. This means that the app will be in the same state as before the
disconnection. In the above code, this means
input$disconnect
, input$text
and
input$select
will be restored to their previous values. The
user actually doesn’t see that they are “restored”: it looks like the
user never left. But it is important to realize that behind the scenes,
Shiny is starting from the initial state of your app and sets back the
values of the inputs to their previous values. This means observers and
reactive expressions will be triggered again. In the code above, we’re
using ignoreInit
to avoid the observer being triggered
after reconnect. If we would omit this, we would end up being
disconnected again. After all, the value of
input$disconnect
changes from 0
to
1
when clicked.
Note that the JS code behind the reconnect button ignores the user reconnect
setup and proposes reconnecting regardless of the
session$allowReconnect
configuration. A solution is
provided in the Outstanding
User Interfaces with Shiny book.
The custom disconnected screen also includes a reload button. This button will reload the app (like hitting refresh in your browser). This is useful when the app is stuck in a loop and the reconnect button doesn’t work. Compared to reconnecting, reloading will start from a clean slate: there’s no restoring of values.
Note that when setting your app up to be a PWA, there’s an
offline.html
asset generated when using
charpente::set_pwa()
. This page will be displayed when the
user is offline. This page contains a reload button that will reload the
app. It is not reconnecting.