26 Fine tune {shinyMobile}
26.1 Enhance the disconnect screen
As depicted on Figure 26.1, having the classic Shiny disconnect screen in a mobile device is not that beautiful, especially knowing about all the Framework7 capabilities.
Let’s do better! Upon disconnection, we want to display a toast with two buttons:
- A reload button that reloads the window and re-initialize the app. This button
calls
location.reload()
upon click. - A reconnect button, that tries to reconnect with the server websocket,
so that we don’t lose any input, output elements. This button calls
Shiny.shinyapp.reconnect()
upon click.
How do we know when shiny is disconnected? As described in Chapter 11,
whenever the client socket connection is closed, for any reason, the shiny:disconnected
event is raised:
.onclose = function() {
socket// These things are needed only if we've successfully
// opened the websocket.
if (hasOpened) {
$(document).trigger({
type: 'shiny:disconnected',
socket: socket
;
})
.$notifyDisconnected();
self
}
.onDisconnected(); // run before self.$removeSocket()
self.$removeSocket();
self }
This allows us to listen to that event on the JS side:
$(document).on('shiny:disconnected', function(event) {
// Do things
; })
In the next step, we to remove the default shiny reconnect elements. They are inserted by the onDisconnected
method, that adds a disconnect overlay (gray-out screen) and optionally a reconnect notification:
// From within Shiny.shinyapp...
this.onDisconnected = function() {
// Add gray-out overlay, if not already present
var $overlay = $('#shiny-disconnected-overlay');
if ($overlay.length === 0) {
$(document.body)
.append('<div id="shiny-disconnected-overlay"></div>');
}
// To try a reconnect, both the app (this.$allowReconnect)
// and the server (this.$socket.allowReconnect) must allow
// reconnections, or session$allowReconnect("force") was
// called. The "force" option should only be used for
// testing.
if (
this.$allowReconnect === true &&
(this.$socket.allowReconnect === true) ||
this.$allowReconnect === 'force')
{var delay = reconnectDelay.next();
.showReconnectDialog(delay);
exportsthis.$scheduleReconnect(delay);
} }
To remove default shiny reconnect elements, there are multiple alternatives. The easiest way is to wait
for the client to be connected, that is listening to shiny:connected
, and set the Shiny.shinyapp.onDisconnected
method to only add the gray overlay.
Before modifying any vanilla shiny elements, make sure to check all the possible side effects.
// remove shiny reconnect stuff;
$(document).on('shiny:connected', function(event) {
.shinyapp.onDisconnected = function() {
Shiny// Add gray-out overlay, if not already present
let $overlay = $('#shiny-disconnected-overlay');
if ($overlay.length === 0) {
$(document.body)
.append('<div id="shiny-disconnected-overlay"></div>');
};
}; })
We edit the previous disconnected event listener to add a custom Framework7 toast, which closes upon click:
$(document).on('shiny:disconnected', function(event) {
let reconnectToast = app.toast
.create({
position: 'center',
text:
`Oups... disconnected </br> </br>
<div class="row">
<button
onclick="Shiny.shinyapp.reconnect();"
class="toast-button button color-green col">
Reconnect
</button>
<button
onclick="location.reload();"
class="toast-button button color-red col">
Reload
</button>
</div>`
}).open();
// close toast whenever a choice is made ...
$('.toast-button').on('click', function() {
.close();
reconnectToast;
}); })
Result is shown Figure 26.2.
The above JS code ignores the user reconnect setup and proposes
to reconnect regardless of the session$allowReconnect
configuration. If you want to keep the original behavior, you may include the following condition before showing the toast:
if (
.shinyapp.$allowReconnect === true &&
(Shiny.shinyapp.$socket.allowReconnect === true) ||
Shiny.shinyapp.$allowReconnect === 'force') {
Shiny// Toast logic
}