Show a message if device is offline or on a slow network

Let users know that their device is offline or has a slow network connection.

This is useful as certain features might not work if the app is offline or has a slow connection.

Paste the following code in the global Javscript in your app. Tweak the error messages as required.

Once added to your app, ensure you set your device to be offline to test that the messages show as expected.

JavaScript

//Slow network code
(function monitorSlowRequests() {
 
  var SLOW_REQUEST_THRESHOLD = 3000;
  var MESSAGES = {
    slow: 'Slow connection detected, some features may not work.',
    offline: 'Unable to connect to server, some features may not work.'
  };

  var NETWORK_STATUS = 'network_status';
  var NETWORK_WARNING_CLASS = 'fl-network-warning';
  var request = Fliplet.API.request;
  
  function showWarning(status) {
    if (!MESSAGES[status]) {
      // Message not defined
      hideWarning();
      return;
    }
    
    var $warning = $('.' + NETWORK_WARNING_CLASS);
    
    if ($warning.length) {
      $warning.html(MESSAGES[status]);
      $warning.addClass('active');
      return;
    }
    
    $warning = $('<div></div>');
    $warning
      .html(MESSAGES[status])
      .addClass(NETWORK_WARNING_CLASS)
      .appendTo('body')
      .addClass('active');
  }

  function hideWarning() {
    $('.' + NETWORK_WARNING_CLASS).removeClass('active');
  }
  
  function logSlowRequest(options) {
    console.debug('Slow request', (options.method || 'GET') + ' ' + options.url);
  }
  
  function getNetworkStatus() {
    return Fliplet.Storage.get(NETWORK_STATUS, {
      defaults: {
        status: Fliplet.Navigator.isOnline() ? 'online' : 'offline'
      }
    });
  }
  
  function setNetworkStatus(status) {
    if (['online', 'offline', 'slow'].indexOf(status) < 0) {
      return Promise.reject('Status "' + status + '" is invalid. It must be one of the following: online, offline, slow.');
    }
    
    return getNetworkStatus().then(function (data) {
      if (data.status !== status) {
        // Emit a status update if it has changed
        Fliplet.Hooks.run('networkStatusChanged', {
          status: status
        });
      }
      
      return Fliplet.Storage.set(NETWORK_STATUS, {
        status: status
      });
    })
  }
  
  function processNetworkStatus(status) {
    switch (status) {
      case 'slow':
      case 'offline':
        showWarning(status);
        break;
      case 'online':
      default:
        hideWarning();
        break;
    }    
  }
  
  Fliplet.Hooks.on('networkStatusChanged', function (data) {
   
    processNetworkStatus(data.status);
  });      
  
  // Detect online and offline changes
  Fliplet.Navigator.onOnline(function(){
    setNetworkStatus('online');
  });
  Fliplet.Navigator.onOffline(function(){
    setNetworkStatus('offline');
  });  
  
  // Override Fliplet.API.request to start monitoring network status
  Fliplet.API.request = function (options) {
    var startTime;
    var requestTimeout;

    if (typeof options === 'string') {
      options = { url: options };
    }

    options.beforeSend = function (xhr) {
      startTime = new Date().getTime();

      requestTimeout = setTimeout(function () {
        setNetworkStatus(!Fliplet.Navigator.isOnline() ? 'offline' : 'slow');
        logSlowRequest(options);
      }, SLOW_REQUEST_THRESHOLD);
    };
    options.success = function (data) {
      
      clearTimeout(requestTimeout);

      if (new Date().getTime() - startTime < SLOW_REQUEST_THRESHOLD) {
        setNetworkStatus('online');
      }
    };
    options.error = function (jqXHR, textStatus) {
      clearTimeout(requestTimeout);

      if (textStatus === 'timeout' || !Fliplet.Navigator.isOnline()) {
        setNetworkStatus('offline');
      } else if (typeof error === 'object'  
          && error.readyState === 0
          && error.status === 0
          && !options.required) {
        setNetworkStatus('offline');
      }
    };

    return request(options);
  };
  
  // Check network status on load
  getNetworkStatus().then(function (data) {
    processNetworkStatus(data.status);
  });
})();

The following CSS will also need to be added to global code.

CSS

.fl-network-warning {
  background-color: #333;
  color: #fff;
  font-size: 10px;
  display: inline-block;
  position: fixed;
  /* Change positioning depending on menu/header/footer design */
  top: 5px;
  top: calc(5px + env(safe-area-inset-top));
  left: 50%;
  line-height: 1;
  padding: 5px 10px;
  z-index: 1050;
  border-radius: 10px;
  transform: translate3d(-50%, -50px, 0);
  transition: all 0.2s ease-out;
  pointer-events: none;
  text-align: center;
  max-width: 80vw;
  overflow: hidden;
  text-overflow: ellipsis;
  box-shadow: 0px 0px 10px rgba(255, 255, 255, 0.3);
}

.fl-network-warning.active {
  transform: translate3d(-50%, 0px, 0);
}

@media screen and (min-width: 640px) {
  .fl-network-warning {
    max-width: 50vw;
  }
}

@media screen and (min-width: 1024px) {
  .fl-network-warning {
    max-width: 600px;
  }
}