FEATURE: Offline indicator ()

This commit is contained in:
Mark VanLandingham 2023-06-05 11:08:04 -05:00 committed by GitHub
parent 704a792f18
commit 3bfc6805ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 146 additions and 15 deletions

View File

@ -0,0 +1,10 @@
{{#if this.showing}}
<div class="offline-indicator">
<span>{{i18n "offline_indicator.no_internet"}}</span>
<DButton
@label="offline_indicator.refresh_page"
@display="link"
@action={{this.refresh}}
/>
</div>
{{/if}}

View File

@ -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);
}
}

View File

@ -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("/");

View File

@ -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";
}
}

View File

@ -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
);
}
}

View File

@ -24,6 +24,7 @@
{{/if}}
<SoftwareUpdatePrompt />
<OfflineIndicator />
<PluginOutlet
@name="below-site-header"

View File

@ -22,6 +22,7 @@
@import "ignored-user-list";
@import "keyboard_shortcuts";
@import "navs";
@import "offline-indicator";
@import "pick-files-button";
@import "relative-time-picker";
@import "share-and-invite-modal";

View File

@ -0,0 +1,10 @@
.offline-indicator {
position: fixed;
top: var(--header-offset);
left: 50%;
transform: translate(-50%, 0);
padding: 0.5em 1em;
background-color: var(--primary-very-low);
border-radius: var(--d-nav-pill-border-radius);
z-index: z("header") - 10;
}

View File

@ -2070,6 +2070,10 @@ en:
intro: "Hello! Looks like youre enjoying the discussion, but you havent signed up for an account yet."
value_prop: "Tired of scrolling through the same posts? When you create an account youll always come back to where you left off. With an account you can also be notified of new replies, save bookmarks, and use likes to thank others. We can all work together to make this community great. :heart:"
offline_indicator:
no_internet: "No internet connection."
refresh_page: "Refresh page"
summary:
enabled_description: "You're viewing a summary of this topic: the most interesting posts as determined by the community."
description:

View File

@ -2343,6 +2343,7 @@ en:
default_text_size: "Text size which is selected by default"
default_title_count_mode: "Default mode for the page title counter"
enable_offline_indicator: "Display a message to users when it is detected that they have no network connection"
retain_web_hook_events_period_days: "Number of days to retain web hook event records."
retry_web_hook_events: "Automatically retry failed web hook events for 4 times. Time gaps between the retries are 1, 5, 25 and 125 minutes."

View File

@ -341,7 +341,7 @@ basic:
list_type: compact
default: "14"
allow_any: false
refresh: true
refresh: true
enable_bookmarks_with_reminders:
client: true
default: true
@ -2701,6 +2701,9 @@ user_preferences:
choices:
- notifications
- contextual
enable_offline_indicator:
default: false
client: true
api:
retain_web_hook_events_period_days:
default: 30

View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
RSpec.describe "Network Disconnected", type: :system, js: true do
fab!(:current_user) { Fabricate(:user) }
def with_network_disconnected
page.driver.browser.network_conditions = { offline: true }
yield
page.driver.browser.network_conditions = { offline: false }
end
it "Message bus connectivity service adds class to DOM and displays offline indicator" do
SiteSetting.enable_offline_indicator = true
visit("/c")
expect(page).to have_no_css("html.message-bus-offline")
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.message-bus-offline")
# Offline indicator is rendered
expect(page).to have_css(".offline-indicator")
end
end
it "Doesn't show the offline indicator when the site setting isn't present" do
SiteSetting.enable_offline_indicator = false
visit("/c")
with_network_disconnected do
expect(page).to have_css("html.message-bus-offline")
expect(page).not_to have_css(".offline-indicator")
end
end
end