diff --git a/app/assets/javascripts/discourse/app/components/offline-indicator.hbs b/app/assets/javascripts/discourse/app/components/offline-indicator.hbs
new file mode 100644
index 00000000000..ee53efd7aeb
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/offline-indicator.hbs
@@ -0,0 +1,10 @@
+{{#if this.showing}}
+
+ {{i18n "offline_indicator.no_internet"}}
+
+
+{{/if}}
\ No newline at end of file
diff --git a/app/assets/javascripts/discourse/app/components/offline-indicator.js b/app/assets/javascripts/discourse/app/components/offline-indicator.js
new file mode 100644
index 00000000000..6f63f004646
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/components/offline-indicator.js
@@ -0,0 +1,20 @@
+import Component from "@glimmer/component";
+import { action } from "@ember/object";
+import { inject as service } from "@ember/service";
+
+export default class OfflineIndicator extends Component {
+ @service messageBusConnectivity;
+ @service siteSettings;
+
+ get showing() {
+ return (
+ this.siteSettings.enable_offline_indicator &&
+ !this.messageBusConnectivity.connected
+ );
+ }
+
+ @action
+ refresh() {
+ window.location.reload(true);
+ }
+}
diff --git a/app/assets/javascripts/discourse/app/initializers/message-bus.js b/app/assets/javascripts/discourse/app/initializers/message-bus.js
index 8eb790749df..c53c28471b6 100644
--- a/app/assets/javascripts/discourse/app/initializers/message-bus.js
+++ b/app/assets/javascripts/discourse/app/initializers/message-bus.js
@@ -5,23 +5,17 @@ import { handleLogoff } from "discourse/lib/ajax";
import userPresent, { onPresenceChange } from "discourse/lib/user-presence";
const LONG_POLL_AFTER_UNSEEN_TIME = 1200000; // 20 minutes
-const CONNECTIVITY_ERROR_CLASS = "message-bus-offline";
-function updateConnectivityIndicator(stat) {
- if (stat === "error") {
- document.documentElement.classList.add(CONNECTIVITY_ERROR_CLASS);
- } else {
- document.documentElement.classList.remove(CONNECTIVITY_ERROR_CLASS);
- }
-}
-
-function ajax(opts) {
+function ajax(opts, messageBusConnectivity, appState) {
if (opts.complete) {
const oldComplete = opts.complete;
opts.complete = function (xhr, stat) {
handleLogoff(xhr);
oldComplete(xhr, stat);
- updateConnectivityIndicator(stat);
+
+ messageBusConnectivity.setConnectivity(
+ xhr.readyState === 4 || stat === "abort" || appState.background
+ );
};
} else {
opts.complete = handleLogoff;
@@ -42,7 +36,11 @@ export default {
const messageBus = container.lookup("service:message-bus"),
user = container.lookup("service:current-user"),
- siteSettings = container.lookup("service:site-settings");
+ siteSettings = container.lookup("service:site-settings"),
+ appState = container.lookup("service:app-state"),
+ messageBusConnectivity = container.lookup(
+ "service:message-bus-connectivity"
+ );
messageBus.alwaysLongPoll = !isProduction();
messageBus.shouldLongPollCallback = () =>
@@ -97,7 +95,7 @@ export default {
if (userPresent()) {
opts.headers["Discourse-Present"] = "true";
}
- return ajax(opts);
+ return ajax(opts, messageBusConnectivity, appState);
};
} else {
messageBus.ajax = function (opts) {
@@ -105,7 +103,7 @@ export default {
if (userPresent()) {
opts.headers["Discourse-Present"] = "true";
}
- return ajax(opts);
+ return ajax(opts, messageBusConnectivity, appState);
};
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
new file mode 100644
index 00000000000..7d65e9a3493
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/services/app-state.js
@@ -0,0 +1,28 @@
+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
new file mode 100644
index 00000000000..876ecb34e99
--- /dev/null
+++ b/app/assets/javascripts/discourse/app/services/message-bus-connectivity.js
@@ -0,0 +1,16 @@
+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/templates/application.hbs b/app/assets/javascripts/discourse/app/templates/application.hbs
index 2c6d11c0e4a..623f253f224 100644
--- a/app/assets/javascripts/discourse/app/templates/application.hbs
+++ b/app/assets/javascripts/discourse/app/templates/application.hbs
@@ -24,6 +24,7 @@
{{/if}}
+