FEATURE: Move site updated modal into a less obtrusive prompt (#12577)

This moves the "This site was just updated" modal asking the user if they want to refresh into a subtle prompt that slides down from the header.

Also in this PR I've added a helper to publish message bus messages in JS tests. So instead of this:

```javascript
// Mimic a messagebus message
MessageBus.callbacks
  .filterBy("channel", "/global/asset-version")
  .map((c) => c.func("somenewversion"));
```

We can have:

```javascript
publishToMessageBus("/global/asset-version", "somenewversion");
```
This commit is contained in:
Martin Brennan 2021-04-07 08:56:48 +10:00 committed by GitHub
parent b0ff853f9f
commit 432b839997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 183 additions and 54 deletions

View File

@ -0,0 +1,56 @@
import getURL from "discourse-common/lib/get-url";
import { later } from "@ember/runloop";
import discourseComputed, { on } from "discourse-common/utils/decorators";
import Component from "@ember/component";
export default Component.extend({
showPrompt: false,
classNameBindings: ["getClassNames"],
attributeBindings: ["isHidden:aria-hidden"],
@discourseComputed
rootUrl() {
return getURL("/");
},
@discourseComputed("showPrompt")
getClassNames(showPrompt) {
let classes = ["software-update-prompt"];
if (showPrompt) {
classes.push("require-software-refresh");
}
return classes.join(" ");
},
@discourseComputed("showPrompt")
isHidden(showPrompt) {
return !showPrompt;
},
@on("init")
initSubscribtions() {
let timeout;
this.messageBus.subscribe("/refresh_client", () => {
this.session.requiresRefresh = true;
});
let updatePrompt = this;
this.messageBus.subscribe("/global/asset-version", (version) => {
if (this.session.assetVersion !== version) {
this.session.requiresRefresh = true;
}
if (!timeout && this.session.requiresRefresh) {
// Since we can do this transparently for people browsing the forum
// hold back the message 24 hours.
timeout = later(() => {
updatePrompt.set("showPrompt", true);
}, 1000 * 60 * 24 * 60);
}
});
},
});

View File

@ -1,40 +0,0 @@
import I18n from "I18n";
import bootbox from "bootbox";
import { later } from "@ember/runloop";
// Subscribe to "asset-version" change events via the Message Bus
export default {
name: "asset-version",
after: "message-bus",
initialize(container) {
let timeout;
const messageBus = container.lookup("message-bus:main");
if (!messageBus) {
return;
}
let session = container.lookup("session:main");
messageBus.subscribe("/refresh_client", () => {
session.requiresRefresh = true;
});
messageBus.subscribe("/global/asset-version", function (version) {
if (session.assetVersion !== version) {
session.requiresRefresh = true;
}
if (!timeout && session.requiresRefresh) {
// Since we can do this transparently for people browsing the forum
// hold back the message 24 hours.
timeout = later(() => {
bootbox.confirm(I18n.t("assets_changed_confirm"), function (result) {
if (result) {
document.location.reload();
}
});
}, 1000 * 60 * 24 * 60);
}
});
},
};

View File

@ -7,6 +7,7 @@
toggleMobileView=(route-action "toggleMobileView")
toggleAnonymous=(route-action "toggleAnonymous")
logout=(route-action "logout")}}
{{software-update-prompt id="software-update-prompt"}}
{{plugin-outlet name="below-site-header" tagName="" args=(hash currentPath=router._router.currentPath)}}

View File

@ -0,0 +1,5 @@
<div class="wrap">
<a href={{rootUrl}} aria-live="polite">
{{d-icon "redo"}} {{html-safe (i18n "software_update_prompt")}}
</a>
</div>

View File

@ -1,10 +1,10 @@
import {
acceptance,
exists,
publishToMessageBus,
queryAll,
} from "discourse/tests/helpers/qunit-helpers";
import DiscourseURL from "discourse/lib/url";
import MessageBus from "message-bus-client";
import selectKit from "discourse/tests/helpers/select-kit-helper";
import sinon from "sinon";
import { test } from "qunit";
@ -94,19 +94,16 @@ acceptance("Topic Discovery", function (needs) {
"shows the topic unread"
);
// Mimic a messagebus message
MessageBus.callbacks.filterBy("channel", "/latest").map((c) =>
c.func({
message_type: "read",
publishToMessageBus("/latest", {
message_type: "read",
topic_id: 11995,
payload: {
highest_post_number: 1,
last_read_post_number: 2,
notification_level: 1,
topic_id: 11995,
payload: {
highest_post_number: 1,
last_read_post_number: 2,
notification_level: 1,
topic_id: 11995,
},
})
);
},
});
await visit("/"); // We're already there, but use this to wait for re-render

View File

@ -1,4 +1,5 @@
import { TestModuleForComponent, render } from "@ember/test-helpers";
import MessageBus from "message-bus-client";
import EmberObject from "@ember/object";
import { setupRenderingTest as EmberSetupRenderingTest } from "ember-qunit";
import Session from "discourse/models/session";
@ -67,6 +68,9 @@ export default function (name, opts) {
instantiate: false,
});
this.registry.register("capabilities:main", EmberObject);
this.registry.register("message-bus:main", MessageBus, {
instantiate: false,
});
this.registry.register("site:main", this.site, { instantiate: false });
this.registry.register("session:main", this.session, {
instantiate: false,
@ -80,6 +84,7 @@ export default function (name, opts) {
this.registry.injection("component", "capabilities", "capabilities:main");
this.registry.injection("component", "site", "site:main");
this.registry.injection("component", "session", "session:main");
this.registry.injection("component", "messageBus", "message-bus:main");
this.siteSettings = currentSettings();
store = createStore();

View File

@ -1,4 +1,5 @@
import QUnit, { module } from "qunit";
import MessageBus from "message-bus-client";
import {
clearCache as clearOutletCache,
resetExtraClasses,
@ -437,3 +438,9 @@ export function count(selector) {
export function exists(selector) {
return count(selector) > 0;
}
export function publishToMessageBus(channelPath, ...args) {
MessageBus.callbacks
.filterBy("channel", channelPath)
.map((c) => c.func(...args));
}

View File

@ -0,0 +1,60 @@
import componentTest, {
setupRenderingTest,
} from "discourse/tests/helpers/component-test";
import sinon from "sinon";
import {
discourseModule,
fakeTime,
publishToMessageBus,
queryAll,
} from "discourse/tests/helpers/qunit-helpers";
import hbs from "htmlbars-inline-precompile";
let clock = null;
discourseModule(
"Integration | Component | software-update-prompt",
function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
clock = fakeTime("2019-12-10T08:00:00", "Australia/Brisbane", true);
});
hooks.afterEach(function () {
clock.restore();
sinon.restore();
});
componentTest(
"software-update-prompt gets correct CSS class after messageBus message",
{
template: hbs`{{software-update-prompt}}`,
test(assert) {
assert.ok(
queryAll("div.software-update-prompt.require-software-refresh")
.length === 0,
"it does not have the class to show the prompt"
);
assert.equal(
queryAll("div.software-update-prompt")[0].getAttribute(
"aria-hidden"
),
"",
"it does have the aria-hidden attribute"
);
publishToMessageBus("/global/asset-version", "somenewversion");
clock.tick(1000 * 60 * 24 * 60 + 10);
assert.ok(
queryAll("div.software-update-prompt.require-software-refresh")
.length === 1,
"it does have the class to show the prompt"
);
},
}
);
}
);

View File

@ -10,4 +10,5 @@
@import "common/printer-friendly";
@import "common/base/_index";
@import "common/d-editor";
@import "common/software-update-prompt";
@import "common/topic-timeline";

View File

@ -0,0 +1,35 @@
.software-update-prompt {
position: fixed;
flex: 1;
right: 0;
left: 0;
background-color: var(--tertiary-low);
color: var(--tertiary);
max-height: 0;
visibility: hidden;
transition: max-height 1s;
box-shadow: shadow("header");
z-index: z("header") + 10;
a {
display: block;
padding: 0.5em 0;
}
span {
text-decoration: underline;
}
.d-icon {
margin-right: 0.33em;
font-size: 0.9em;
}
&.require-software-refresh {
visibility: visible;
overflow: hidden;
max-height: 300px;
margin-left: auto;
margin-right: auto;
}
}

View File

@ -178,6 +178,8 @@ en:
wizard_required: "Welcome to your new Discourse! Lets get started with <a href='%{url}' data-auto-route='true'>the setup wizard</a> ✨"
emails_are_disabled: "All outgoing email has been globally disabled by an administrator. No email notifications of any kind will be sent."
software_update_prompt: "We've updated this site, <span>please refresh</span>, or you may experience unexpected behaviour."
bootstrap_mode_enabled:
one: "To make launching your new site easier, you are in bootstrap mode. All new users will be granted trust level 1 and have daily email summary emails enabled. This will be automatically turned off when %{count} user has joined."
other: "To make launching your new site easier, you are in bootstrap mode. All new users will be granted trust level 1 and have daily email summary emails enabled. This will be automatically turned off when %{count} users have joined."
@ -1025,7 +1027,7 @@ en:
no_messages_title: "You dont have any messages"
no_messages_body: >
Need to have a direct personal conversation with someone, outside the normal conversational flow?<br><br>
Message them by selecting their avatar and then look for the MESSAGE button.
Message them by selecting their avatar and then look for the MESSAGE button.
first_notification: "Your first notification! Select it to begin."
dynamic_favicon: "Show counts on browser icon"
skip_new_user_tips: