From fcaa9757f3fe358beeb7899efc854150a1470425 Mon Sep 17 00:00:00 2001 From: Mark VanLandingham Date: Thu, 22 Jun 2023 09:09:28 -0500 Subject: [PATCH] DEV: Use online/offline window events to track network connectivity (#22243) --- .../app/components/offline-indicator.js | 4 +- .../app/instance-initializers/message-bus.js | 14 ++---- .../discourse/app/services/app-state.js | 28 ----------- .../app/services/message-bus-connectivity.js | 17 ------- .../app/services/network-connectivity.js | 47 +++++++++++++++++++ spec/system/network_disconnected_spec.rb | 26 ++++++++++ 6 files changed, 79 insertions(+), 57 deletions(-) delete mode 100644 app/assets/javascripts/discourse/app/services/app-state.js delete mode 100644 app/assets/javascripts/discourse/app/services/message-bus-connectivity.js create mode 100644 app/assets/javascripts/discourse/app/services/network-connectivity.js create mode 100644 spec/system/network_disconnected_spec.rb diff --git a/app/assets/javascripts/discourse/app/components/offline-indicator.js b/app/assets/javascripts/discourse/app/components/offline-indicator.js index 3a6edebe7a3..347709cfd43 100644 --- a/app/assets/javascripts/discourse/app/components/offline-indicator.js +++ b/app/assets/javascripts/discourse/app/components/offline-indicator.js @@ -3,10 +3,10 @@ import { action } from "@ember/object"; import { inject as service } from "@ember/service"; export default class OfflineIndicator extends Component { - @service messageBusConnectivity; + @service networkConnectivity; get showing() { - return !this.messageBusConnectivity.connected; + return !this.networkConnectivity.connected; } @action diff --git a/app/assets/javascripts/discourse/app/instance-initializers/message-bus.js b/app/assets/javascripts/discourse/app/instance-initializers/message-bus.js index 7bade5df15d..b645c332e07 100644 --- a/app/assets/javascripts/discourse/app/instance-initializers/message-bus.js +++ b/app/assets/javascripts/discourse/app/instance-initializers/message-bus.js @@ -6,16 +6,12 @@ import userPresent, { onPresenceChange } from "discourse/lib/user-presence"; const LONG_POLL_AFTER_UNSEEN_TIME = 1200000; // 20 minutes -function ajax(opts, messageBusConnectivity, appState) { +function ajax(opts) { if (opts.complete) { const oldComplete = opts.complete; opts.complete = function (xhr, stat) { handleLogoff(xhr); oldComplete(xhr, stat); - - messageBusConnectivity.setConnectivity( - xhr.readyState === 4 || stat === "abort" || appState.background - ); }; } else { opts.complete = handleLogoff; @@ -35,9 +31,7 @@ export default { const messageBus = owner.lookup("service:message-bus"), user = owner.lookup("service:current-user"), - siteSettings = owner.lookup("service:site-settings"), - appState = owner.lookup("service:app-state"), - messageBusConnectivity = owner.lookup("service:message-bus-connectivity"); + siteSettings = owner.lookup("service:site-settings"); messageBus.alwaysLongPoll = !isProduction(); messageBus.shouldLongPollCallback = () => @@ -92,7 +86,7 @@ export default { if (userPresent()) { opts.headers["Discourse-Present"] = "true"; } - return ajax(opts, messageBusConnectivity, appState); + return ajax(opts); }; } else { messageBus.ajax = function (opts) { @@ -100,7 +94,7 @@ export default { if (userPresent()) { opts.headers["Discourse-Present"] = "true"; } - return ajax(opts, messageBusConnectivity, appState); + return ajax(opts); }; messageBus.baseUrl = getURL("/"); diff --git a/app/assets/javascripts/discourse/app/services/app-state.js b/app/assets/javascripts/discourse/app/services/app-state.js deleted file mode 100644 index 7d65e9a3493..00000000000 --- a/app/assets/javascripts/discourse/app/services/app-state.js +++ /dev/null @@ -1,28 +0,0 @@ -import Service, { inject as service } from "@ember/service"; - -export default class AppState extends Service { - @service capabilities; - - constructor() { - super(...arguments); - - if (this.capabilities.isAppWebview) { - window.addEventListener("AppStateChange", (event) => { - // Possible states: "active", "inactive", and "background" - this._state = event.detail?.newAppState; - }); - } - } - - get active() { - return this._state === "active"; - } - - get inactive() { - return this._state === "inactive"; - } - - get background() { - return this._state === "background"; - } -} diff --git a/app/assets/javascripts/discourse/app/services/message-bus-connectivity.js b/app/assets/javascripts/discourse/app/services/message-bus-connectivity.js deleted file mode 100644 index a56eb197bb5..00000000000 --- a/app/assets/javascripts/discourse/app/services/message-bus-connectivity.js +++ /dev/null @@ -1,17 +0,0 @@ -import Service from "@ember/service"; -import { tracked } from "@glimmer/tracking"; - -const CONNECTIVITY_ERROR_CLASS = "message-bus-offline"; - -export default class MessageBusConnectivity extends Service { - @tracked connected = true; - - setConnectivity(connected) { - this.connected = connected; - - document.documentElement.classList.toggle( - CONNECTIVITY_ERROR_CLASS, - !connected - ); - } -} diff --git a/app/assets/javascripts/discourse/app/services/network-connectivity.js b/app/assets/javascripts/discourse/app/services/network-connectivity.js new file mode 100644 index 00000000000..6031b5e9585 --- /dev/null +++ b/app/assets/javascripts/discourse/app/services/network-connectivity.js @@ -0,0 +1,47 @@ +import Service from "@ember/service"; +import { ajax } from "discourse/lib/ajax"; +import { tracked } from "@glimmer/tracking"; + +const CONNECTIVITY_ERROR_CLASS = "network-disconnected"; + +export default class NetworkConnectivity extends Service { + @tracked connected = true; + + constructor() { + super(...arguments); + + this.setConnectivity(navigator.onLine); + + window.addEventListener("offline", () => { + this.handleConnectivityChangeEvent(false); + }); + + window.addEventListener("online", () => { + this.handleConnectivityChangeEvent(true); + }); + } + + handleConnectivityChangeEvent(connected) { + if (connected) { + // Make a super cheap request to the server. If we get a response, we are connected! + return ajax("/srv/status", { dataType: "text" }) + .then((response) => { + this.setConnectivity(response === "ok"); + }) + .catch(() => { + this.setConnectivity(false); + }); + } else { + this.setConnectivity(false); + } + } + + setConnectivity(connected) { + this.connected = connected; + + document.documentElement.classList.toggle( + CONNECTIVITY_ERROR_CLASS, + !connected + ); + } +} diff --git a/spec/system/network_disconnected_spec.rb b/spec/system/network_disconnected_spec.rb new file mode 100644 index 00000000000..08a14b5c46c --- /dev/null +++ b/spec/system/network_disconnected_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +RSpec.describe "Network Disconnected", type: :system do + def with_network_disconnected + page.driver.browser.network_conditions = { offline: true } + yield + page.driver.browser.network_conditions = { offline: false } + end + + it "NetworkConnectivity service adds class to DOM and displays offline indicator" do + SiteSetting.enable_offline_indicator = true + + visit("/c") + + expect(page).to have_no_css("html.network-disconnected") + expect(page).to have_no_css(".offline-indicator") + + with_network_disconnected do + # Message bus connectivity services adds the disconnected class to the DOM + expect(page).to have_css("html.network-disconnected") + + # Offline indicator is rendered + expect(page).to have_css(".offline-indicator") + end + end +end