mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 03:13:39 +08:00
REFACTOR: Move Page title / focus / counts logic to service
We had a handful of methods attached to the root `Discourse` object related to focus and notification counts. This patch pulls them out into a service called `document-title` for updating the title, and a component called `d-document` to attach and listen for browser events related to focus. It also removes some computed properties and observers in favor of plain old Javascript objects.
This commit is contained in:
parent
14eec43889
commit
92b26ecbfb
|
@ -2,8 +2,7 @@
|
||||||
import Application from "@ember/application";
|
import Application from "@ember/application";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
import { buildResolver } from "discourse-common/resolver";
|
import { buildResolver } from "discourse-common/resolver";
|
||||||
import { bind } from "@ember/runloop";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import discourseComputed, { observes } from "discourse-common/utils/decorators";
|
|
||||||
import { default as getURL, getURLWithCDN } from "discourse-common/lib/get-url";
|
import { default as getURL, getURLWithCDN } from "discourse-common/lib/get-url";
|
||||||
import deprecated from "discourse-common/lib/deprecated";
|
import deprecated from "discourse-common/lib/deprecated";
|
||||||
|
|
||||||
|
@ -11,10 +10,7 @@ const _pluginCallbacks = [];
|
||||||
|
|
||||||
const Discourse = Application.extend({
|
const Discourse = Application.extend({
|
||||||
rootElement: "#main",
|
rootElement: "#main",
|
||||||
_docTitle: document.title,
|
|
||||||
__widget_helpers: {},
|
__widget_helpers: {},
|
||||||
hasFocus: null,
|
|
||||||
_boundFocusChange: null,
|
|
||||||
|
|
||||||
customEvents: {
|
customEvents: {
|
||||||
paste: "paste"
|
paste: "paste"
|
||||||
|
@ -23,24 +19,6 @@ const Discourse = Application.extend({
|
||||||
reset() {
|
reset() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
Mousetrap.reset();
|
Mousetrap.reset();
|
||||||
|
|
||||||
document.removeEventListener("visibilitychange", this._boundFocusChange);
|
|
||||||
document.removeEventListener("resume", this._boundFocusChange);
|
|
||||||
document.removeEventListener("freeze", this._boundFocusChange);
|
|
||||||
|
|
||||||
this._boundFocusChange = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
ready() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this._boundFocusChange = bind(this, this._focusChanged);
|
|
||||||
|
|
||||||
// Default to true
|
|
||||||
this.set("hasFocus", true);
|
|
||||||
|
|
||||||
document.addEventListener("visibilitychange", this._boundFocusChange);
|
|
||||||
document.addEventListener("resume", this._boundFocusChange);
|
|
||||||
document.addEventListener("freeze", this._boundFocusChange);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getURL(url) {
|
getURL(url) {
|
||||||
|
@ -61,72 +39,6 @@ const Discourse = Application.extend({
|
||||||
|
|
||||||
Resolver: buildResolver("discourse"),
|
Resolver: buildResolver("discourse"),
|
||||||
|
|
||||||
@observes("_docTitle", "hasFocus", "contextCount", "notificationCount")
|
|
||||||
_titleChanged() {
|
|
||||||
let title = this._docTitle || this.SiteSettings.title;
|
|
||||||
|
|
||||||
let displayCount = this.displayCount;
|
|
||||||
let dynamicFavicon = this.currentUser && this.currentUser.dynamic_favicon;
|
|
||||||
if (displayCount > 0 && !dynamicFavicon) {
|
|
||||||
title = `(${displayCount}) ${title}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = title;
|
|
||||||
},
|
|
||||||
|
|
||||||
@discourseComputed("contextCount", "notificationCount")
|
|
||||||
displayCount() {
|
|
||||||
return this.currentUser &&
|
|
||||||
this.currentUser.get("title_count_mode") === "notifications"
|
|
||||||
? this.notificationCount
|
|
||||||
: this.contextCount;
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("contextCount", "notificationCount")
|
|
||||||
faviconChanged() {
|
|
||||||
if (this.currentUser && this.currentUser.get("dynamic_favicon")) {
|
|
||||||
let url = this.SiteSettings.site_favicon_url;
|
|
||||||
|
|
||||||
// Since the favicon is cached on the browser for a really long time, we
|
|
||||||
// append the favicon_url as query params to the path so that the cache
|
|
||||||
// is not used when the favicon changes.
|
|
||||||
if (/^http/.test(url)) {
|
|
||||||
url = getURL("/favicon/proxied?" + encodeURIComponent(url));
|
|
||||||
}
|
|
||||||
|
|
||||||
new window.Favcount(url).set(this.displayCount);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateContextCount(count) {
|
|
||||||
this.set("contextCount", count);
|
|
||||||
},
|
|
||||||
|
|
||||||
updateNotificationCount(count) {
|
|
||||||
if (!this.hasFocus) {
|
|
||||||
this.set("notificationCount", count);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
incrementBackgroundContextCount() {
|
|
||||||
if (!this.hasFocus) {
|
|
||||||
this.set("backgroundNotify", true);
|
|
||||||
this.set("contextCount", (this.contextCount || 0) + 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
@observes("hasFocus")
|
|
||||||
resetCounts() {
|
|
||||||
if (this.hasFocus && this.backgroundNotify) {
|
|
||||||
this.set("contextCount", 0);
|
|
||||||
}
|
|
||||||
this.set("backgroundNotify", false);
|
|
||||||
|
|
||||||
if (this.hasFocus) {
|
|
||||||
this.set("notificationCount", 0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
authenticationComplete(options) {
|
authenticationComplete(options) {
|
||||||
// TODO, how to dispatch this to the controller without the container?
|
// TODO, how to dispatch this to the controller without the container?
|
||||||
const loginController = this.__container__.lookup("controller:login");
|
const loginController = this.__container__.lookup("controller:login");
|
||||||
|
@ -192,19 +104,7 @@ const Discourse = Application.extend({
|
||||||
}
|
}
|
||||||
return this.currentAssetVersion;
|
return this.currentAssetVersion;
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
|
||||||
_focusChanged() {
|
|
||||||
if (document.visibilityState === "hidden") {
|
|
||||||
if (this.hasFocus) {
|
|
||||||
this.set("hasFocus", false);
|
|
||||||
this.appEvents.trigger("discourse:focus-changed", false);
|
|
||||||
}
|
|
||||||
} else if (!this.hasFocus) {
|
|
||||||
this.set("hasFocus", true);
|
|
||||||
this.appEvents.trigger("discourse:focus-changed", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Discourse;
|
export default Discourse;
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import Component from "@ember/component";
|
||||||
|
import { bind } from "@ember/runloop";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
_boundFocusChange: null,
|
||||||
|
tagName: "",
|
||||||
|
documentTitle: service(),
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
this.documentTitle.setTitle(document.title);
|
||||||
|
this._boundFocusChange = bind(this, this._focusChanged);
|
||||||
|
document.addEventListener("visibilitychange", this._boundFocusChange);
|
||||||
|
document.addEventListener("resume", this._boundFocusChange);
|
||||||
|
document.addEventListener("freeze", this._boundFocusChange);
|
||||||
|
this.session.hasFocus = true;
|
||||||
|
|
||||||
|
this.appEvents.on("notifications:changed", this, this._updateNotifications);
|
||||||
|
},
|
||||||
|
|
||||||
|
willDestroyElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
document.removeEventListener("visibilitychange", this._boundFocusChange);
|
||||||
|
document.removeEventListener("resume", this._boundFocusChange);
|
||||||
|
document.removeEventListener("freeze", this._boundFocusChange);
|
||||||
|
this._boundFocusChange = null;
|
||||||
|
|
||||||
|
this.appEvents.off(
|
||||||
|
"notifications:changed",
|
||||||
|
this,
|
||||||
|
this._updateNotifications
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_updateNotifications() {
|
||||||
|
if (!this.currentUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.documentTitle.updateNotificationCount(
|
||||||
|
this.currentUser.unread_notifications +
|
||||||
|
this.currentUser.unread_high_priority_notifications
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
_focusChanged() {
|
||||||
|
if (document.visibilityState === "hidden") {
|
||||||
|
if (this.session.hasFocus) {
|
||||||
|
this.documentTitle.setFocus(false);
|
||||||
|
}
|
||||||
|
} else if (!this.hasFocus) {
|
||||||
|
this.documentTitle.setFocus(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -3,10 +3,12 @@ import Component from "@ember/component";
|
||||||
import { on, observes } from "discourse-common/utils/decorators";
|
import { on, observes } from "discourse-common/utils/decorators";
|
||||||
import LoadMore from "discourse/mixins/load-more";
|
import LoadMore from "discourse/mixins/load-more";
|
||||||
import UrlRefresh from "discourse/mixins/url-refresh";
|
import UrlRefresh from "discourse/mixins/url-refresh";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
const DiscoveryTopicsListComponent = Component.extend(UrlRefresh, LoadMore, {
|
const DiscoveryTopicsListComponent = Component.extend(UrlRefresh, LoadMore, {
|
||||||
classNames: ["contents"],
|
classNames: ["contents"],
|
||||||
eyelineSelector: ".topic-list-item",
|
eyelineSelector: ".topic-list-item",
|
||||||
|
documentTitle: service(),
|
||||||
|
|
||||||
@on("didInsertElement")
|
@on("didInsertElement")
|
||||||
@observes("model")
|
@observes("model")
|
||||||
|
@ -26,7 +28,7 @@ const DiscoveryTopicsListComponent = Component.extend(UrlRefresh, LoadMore, {
|
||||||
|
|
||||||
@observes("incomingCount")
|
@observes("incomingCount")
|
||||||
_updateTitle() {
|
_updateTitle() {
|
||||||
Discourse.updateContextCount(this.incomingCount);
|
this.documentTitle.updateContextCount(this.incomingCount);
|
||||||
},
|
},
|
||||||
|
|
||||||
saveScrollPosition() {
|
saveScrollPosition() {
|
||||||
|
@ -35,7 +37,7 @@ const DiscoveryTopicsListComponent = Component.extend(UrlRefresh, LoadMore, {
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
loadMore() {
|
loadMore() {
|
||||||
Discourse.updateContextCount(0);
|
this.documentTitle.updateContextCount(0);
|
||||||
this.model.loadMore().then(hasMoreResults => {
|
this.model.loadMore().then(hasMoreResults => {
|
||||||
schedule("afterRender", () => this.saveScrollPosition());
|
schedule("afterRender", () => this.saveScrollPosition());
|
||||||
if (hasMoreResults && $(window).height() >= $(document).height()) {
|
if (hasMoreResults && $(window).height() >= $(document).height()) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import TopicTimer from "discourse/models/topic-timer";
|
||||||
import { Promise } from "rsvp";
|
import { Promise } from "rsvp";
|
||||||
import { escapeExpression } from "discourse/lib/utilities";
|
import { escapeExpression } from "discourse/lib/utilities";
|
||||||
import { AUTO_DELETE_PREFERENCES } from "discourse/models/bookmark";
|
import { AUTO_DELETE_PREFERENCES } from "discourse/models/bookmark";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
let customPostMessageCallbacks = {};
|
let customPostMessageCallbacks = {};
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||||
username_filters: null,
|
username_filters: null,
|
||||||
filter: null,
|
filter: null,
|
||||||
quoteState: null,
|
quoteState: null,
|
||||||
|
documentTitle: service(),
|
||||||
|
|
||||||
canRemoveTopicFeaturedLink: and(
|
canRemoveTopicFeaturedLink: and(
|
||||||
"canEditTopicFeaturedLink",
|
"canEditTopicFeaturedLink",
|
||||||
|
@ -1350,7 +1352,7 @@ export default Controller.extend(bufferedProperty("model"), {
|
||||||
case "created": {
|
case "created": {
|
||||||
postStream.triggerNewPostInStream(data.id).then(() => refresh());
|
postStream.triggerNewPostInStream(data.id).then(() => refresh());
|
||||||
if (this.get("currentUser.id") !== data.user_id) {
|
if (this.get("currentUser.id") !== data.user_id) {
|
||||||
Discourse.incrementBackgroundContextCount();
|
this.documentTitle.incrementBackgroundContextCount();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,9 @@ export default {
|
||||||
router.on("routeDidChange", cleanDOM);
|
router.on("routeDidChange", cleanDOM);
|
||||||
|
|
||||||
let appEvents = container.lookup("service:app-events");
|
let appEvents = container.lookup("service:app-events");
|
||||||
|
let documentTitle = container.lookup("service:document-title");
|
||||||
|
|
||||||
startPageTracking(router, appEvents);
|
startPageTracking(router, appEvents, documentTitle);
|
||||||
|
|
||||||
// Out of the box, Discourse tries to track google analytics
|
// Out of the box, Discourse tries to track google analytics
|
||||||
// if it is present
|
// if it is present
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
export default {
|
|
||||||
name: "title-notifications",
|
|
||||||
after: "message-bus",
|
|
||||||
|
|
||||||
initialize(container) {
|
|
||||||
const user = container.lookup("current-user:main");
|
|
||||||
if (!user) return; // must be logged in
|
|
||||||
|
|
||||||
this.container = container;
|
|
||||||
|
|
||||||
container
|
|
||||||
.lookup("service:app-events")
|
|
||||||
.on("notifications:changed", this, "_updateTitle");
|
|
||||||
},
|
|
||||||
|
|
||||||
teardown(container) {
|
|
||||||
container
|
|
||||||
.lookup("service:app-events")
|
|
||||||
.off("notifications:changed", this, "_updateTitle");
|
|
||||||
|
|
||||||
this.container = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
_updateTitle() {
|
|
||||||
const user = this.container.lookup("current-user:main");
|
|
||||||
if (!user) return; // must be logged in
|
|
||||||
|
|
||||||
const notifications =
|
|
||||||
user.unread_notifications + user.unread_high_priority_notifications;
|
|
||||||
|
|
||||||
Discourse.updateNotificationCount(notifications);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -18,7 +18,7 @@ export function resetPageTracking() {
|
||||||
cache = {};
|
cache = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startPageTracking(router, appEvents) {
|
export function startPageTracking(router, appEvents, documentTitle) {
|
||||||
if (_started) {
|
if (_started) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,9 @@ export function startPageTracking(router, appEvents) {
|
||||||
// Refreshing the title is debounced, so we need to trigger this in the
|
// Refreshing the title is debounced, so we need to trigger this in the
|
||||||
// next runloop to have the correct title.
|
// next runloop to have the correct title.
|
||||||
next(() => {
|
next(() => {
|
||||||
let title = Discourse.get("_docTitle");
|
|
||||||
|
|
||||||
appEvents.trigger("page:changed", {
|
appEvents.trigger("page:changed", {
|
||||||
url,
|
url,
|
||||||
title,
|
title: documentTitle.getTitle(),
|
||||||
currentRouteName: router.currentRouteName,
|
currentRouteName: router.currentRouteName,
|
||||||
replacedOnlyQueryParams
|
replacedOnlyQueryParams
|
||||||
});
|
});
|
||||||
|
|
|
@ -240,7 +240,7 @@ export default class {
|
||||||
this.flush();
|
this.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Discourse.get("hasFocus")) {
|
if (this.session.hasFocus) {
|
||||||
this._topicTime += diff;
|
this._topicTime += diff;
|
||||||
|
|
||||||
this._onscreen.forEach(
|
this._onscreen.forEach(
|
||||||
|
|
|
@ -4,7 +4,9 @@ import Singleton from "discourse/mixins/singleton";
|
||||||
// A data model representing current session data. You can put transient
|
// A data model representing current session data. You can put transient
|
||||||
// data here you might want later. It is not stored or serialized anywhere.
|
// data here you might want later. It is not stored or serialized anywhere.
|
||||||
const Session = RestModel.extend({
|
const Session = RestModel.extend({
|
||||||
init: function() {
|
hasFocus: null,
|
||||||
|
|
||||||
|
init() {
|
||||||
this.set("highestSeenByTopic", {});
|
this.set("highestSeenByTopic", {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -69,6 +69,7 @@ export default {
|
||||||
const session = Session.current();
|
const session = Session.current();
|
||||||
app.register("session:main", session, { instantiate: false });
|
app.register("session:main", session, { instantiate: false });
|
||||||
ALL_TARGETS.forEach(t => app.inject(t, "session", "session:main"));
|
ALL_TARGETS.forEach(t => app.inject(t, "session", "session:main"));
|
||||||
|
app.inject("service", "session", "session:main");
|
||||||
|
|
||||||
const screenTrack = new ScreenTrack(
|
const screenTrack = new ScreenTrack(
|
||||||
topicTrackingState,
|
topicTrackingState,
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { findAll } from "discourse/models/login-method";
|
||||||
import { getOwner } from "discourse-common/lib/get-owner";
|
import { getOwner } from "discourse-common/lib/get-owner";
|
||||||
import { userPath } from "discourse/lib/url";
|
import { userPath } from "discourse/lib/url";
|
||||||
import Composer from "discourse/models/composer";
|
import Composer from "discourse/models/composer";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
|
||||||
function unlessReadOnly(method, message) {
|
function unlessReadOnly(method, message) {
|
||||||
return function() {
|
return function() {
|
||||||
|
@ -27,6 +28,7 @@ function unlessReadOnly(method, message) {
|
||||||
const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
|
const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
|
||||||
siteTitle: setting("title"),
|
siteTitle: setting("title"),
|
||||||
shortSiteDescription: setting("short_site_description"),
|
shortSiteDescription: setting("short_site_description"),
|
||||||
|
documentTitle: service(),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
toggleAnonymous() {
|
toggleAnonymous() {
|
||||||
|
@ -53,7 +55,7 @@ const ApplicationRoute = DiscourseRoute.extend(OpenComposer, {
|
||||||
) {
|
) {
|
||||||
tokens.push(this.shortSiteDescription);
|
tokens.push(this.shortSiteDescription);
|
||||||
}
|
}
|
||||||
Discourse.set("_docTitle", tokens.join(" - "));
|
this.documentTitle.setTitle(tokens.join(" - "));
|
||||||
},
|
},
|
||||||
|
|
||||||
// Ember doesn't provider a router `willTransition` event so let's make one
|
// Ember doesn't provider a router `willTransition` event so let's make one
|
||||||
|
|
105
app/assets/javascripts/discourse/app/services/document-title.js
Normal file
105
app/assets/javascripts/discourse/app/services/document-title.js
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
import Service from "@ember/service";
|
||||||
|
import { inject as service } from "@ember/service";
|
||||||
|
import getURL from "discourse-common/lib/get-url";
|
||||||
|
|
||||||
|
export default Service.extend({
|
||||||
|
appEvents: service(),
|
||||||
|
contextCount: null,
|
||||||
|
notificationCount: null,
|
||||||
|
_title: null,
|
||||||
|
_backgroundNotify: null,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
this.reset();
|
||||||
|
},
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.contextCount = 0;
|
||||||
|
this.notificationCount = 0;
|
||||||
|
this._title = null;
|
||||||
|
this._backgroundNotify = null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTitle() {
|
||||||
|
return this._title;
|
||||||
|
},
|
||||||
|
|
||||||
|
setTitle(title) {
|
||||||
|
this._title = title;
|
||||||
|
this._renderTitle();
|
||||||
|
},
|
||||||
|
|
||||||
|
setFocus(focus) {
|
||||||
|
let { session } = this;
|
||||||
|
|
||||||
|
session.hasFocus = focus;
|
||||||
|
|
||||||
|
if (session.hasFocus && this._backgroundNotify) {
|
||||||
|
this.updateContextCount(0);
|
||||||
|
}
|
||||||
|
this._backgroundNotify = false;
|
||||||
|
|
||||||
|
if (session.hasFocus) {
|
||||||
|
this.notificationCount = 0;
|
||||||
|
}
|
||||||
|
this.appEvents.trigger("discourse:focus-changed", session.hasFocus);
|
||||||
|
this._renderTitle();
|
||||||
|
},
|
||||||
|
|
||||||
|
updateContextCount(count) {
|
||||||
|
this.contextCount = count;
|
||||||
|
this._renderTitle();
|
||||||
|
},
|
||||||
|
|
||||||
|
updateNotificationCount(count) {
|
||||||
|
if (!this.session.hasFocus) {
|
||||||
|
this.notificationCount = count;
|
||||||
|
this._renderFavicon();
|
||||||
|
this._renderTitle();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
incrementBackgroundContextCount() {
|
||||||
|
if (!this.session.hasFocus) {
|
||||||
|
this._backgroundNotify = true;
|
||||||
|
this.contextCount += 1;
|
||||||
|
this._renderFavicon();
|
||||||
|
this._renderTitle();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_displayCount() {
|
||||||
|
return this.currentUser &&
|
||||||
|
this.currentUser.title_count_mode === "notifications"
|
||||||
|
? this.notificationCount
|
||||||
|
: this.contextCount;
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderTitle() {
|
||||||
|
let title = this._title || this.siteSettings.title;
|
||||||
|
|
||||||
|
let displayCount = this._displayCount();
|
||||||
|
let dynamicFavicon = this.currentUser && this.currentUser.dynamic_favicon;
|
||||||
|
if (displayCount > 0 && !dynamicFavicon) {
|
||||||
|
title = `(${displayCount}) ${title}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = title;
|
||||||
|
},
|
||||||
|
|
||||||
|
_renderFavicon() {
|
||||||
|
if (this.currentUser && this.currentUser.dynamic_favicon) {
|
||||||
|
let url = this.siteSettings.site_favicon_url;
|
||||||
|
|
||||||
|
// Since the favicon is cached on the browser for a really long time, we
|
||||||
|
// append the favicon_url as query params to the path so that the cache
|
||||||
|
// is not used when the favicon changes.
|
||||||
|
if (/^http/.test(url)) {
|
||||||
|
url = getURL("/favicon/proxied?" + encodeURIComponent(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
new window.Favcount(url).set(this._displayCount());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
{{d-document}}
|
||||||
{{plugin-outlet name="above-site-header"}}
|
{{plugin-outlet name="above-site-header"}}
|
||||||
{{site-header canSignUp=canSignUp
|
{{site-header canSignUp=canSignUp
|
||||||
showCreateAccount=(route-action "showCreateAccount")
|
showCreateAccount=(route-action "showCreateAccount")
|
||||||
|
|
|
@ -10,7 +10,8 @@ moduleFor("controller:topic", "controller:topic", {
|
||||||
needs: [
|
needs: [
|
||||||
"controller:composer",
|
"controller:composer",
|
||||||
"controller:application",
|
"controller:application",
|
||||||
"service:app-events"
|
"service:app-events",
|
||||||
|
"service:document-title"
|
||||||
],
|
],
|
||||||
beforeEach() {
|
beforeEach() {
|
||||||
this.registry.injection("controller", "appEvents", "service:app-events");
|
this.registry.injection("controller", "appEvents", "service:app-events");
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { logIn, updateCurrentUser } from "helpers/qunit-helpers";
|
|
||||||
|
|
||||||
QUnit.module("lib:discourse");
|
|
||||||
|
|
||||||
QUnit.test("title counts are updated correctly", assert => {
|
|
||||||
Discourse.set("hasFocus", true);
|
|
||||||
Discourse.set("contextCount", 0);
|
|
||||||
Discourse.set("notificationCount", 0);
|
|
||||||
|
|
||||||
Discourse.set("_docTitle", "Test Title");
|
|
||||||
|
|
||||||
assert.equal(document.title, "Test Title", "title is correct");
|
|
||||||
|
|
||||||
Discourse.updateNotificationCount(5);
|
|
||||||
assert.equal(document.title, "Test Title", "title doesn't change with focus");
|
|
||||||
|
|
||||||
Discourse.incrementBackgroundContextCount();
|
|
||||||
assert.equal(document.title, "Test Title", "title doesn't change with focus");
|
|
||||||
|
|
||||||
Discourse.set("hasFocus", false);
|
|
||||||
|
|
||||||
Discourse.updateNotificationCount(5);
|
|
||||||
assert.equal(
|
|
||||||
document.title,
|
|
||||||
"Test Title",
|
|
||||||
"notification count ignored for anon"
|
|
||||||
);
|
|
||||||
|
|
||||||
Discourse.incrementBackgroundContextCount();
|
|
||||||
assert.equal(
|
|
||||||
document.title,
|
|
||||||
"(1) Test Title",
|
|
||||||
"title changes when incremented for anon"
|
|
||||||
);
|
|
||||||
|
|
||||||
logIn();
|
|
||||||
updateCurrentUser({ dynamic_favicon: false });
|
|
||||||
|
|
||||||
Discourse.set("hasFocus", true);
|
|
||||||
Discourse.set("hasFocus", false);
|
|
||||||
|
|
||||||
Discourse.incrementBackgroundContextCount();
|
|
||||||
assert.equal(
|
|
||||||
document.title,
|
|
||||||
"Test Title",
|
|
||||||
"title doesn't change when incremented for logged in"
|
|
||||||
);
|
|
||||||
|
|
||||||
Discourse.updateNotificationCount(3);
|
|
||||||
assert.equal(
|
|
||||||
document.title,
|
|
||||||
"(3) Test Title",
|
|
||||||
"title includes notification count for logged in user"
|
|
||||||
);
|
|
||||||
|
|
||||||
Discourse.set("hasFocus", false);
|
|
||||||
Discourse.set("hasFocus", true);
|
|
||||||
|
|
||||||
assert.equal(
|
|
||||||
document.title,
|
|
||||||
"Test Title",
|
|
||||||
"counter dissappears after focus, and doesn't reappear until another notification arrives"
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -1,76 +0,0 @@
|
||||||
import TopicTrackingState from "discourse/models/topic-tracking-state";
|
|
||||||
import Session from "discourse/models/session";
|
|
||||||
import ScreenTrack from "discourse/lib/screen-track";
|
|
||||||
import pretender from "helpers/create-pretender";
|
|
||||||
|
|
||||||
let clock;
|
|
||||||
|
|
||||||
QUnit.module("lib:screen-track", {
|
|
||||||
beforeEach() {
|
|
||||||
clock = sinon.useFakeTimers(new Date(2012, 11, 31, 12, 0).getTime());
|
|
||||||
},
|
|
||||||
|
|
||||||
afterEach() {
|
|
||||||
clock.restore();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// skip for now test leaks state
|
|
||||||
QUnit.skip("Correctly flushes posts as needed", assert => {
|
|
||||||
const timings = [];
|
|
||||||
|
|
||||||
pretender.post("/topics/timings", t => {
|
|
||||||
timings.push(t);
|
|
||||||
return [200, {}, ""];
|
|
||||||
});
|
|
||||||
|
|
||||||
const trackingState = TopicTrackingState.create();
|
|
||||||
const siteSettings = {
|
|
||||||
flush_timings_secs: 60
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentUser = { id: 1, username: "sam" };
|
|
||||||
|
|
||||||
const tracker = new ScreenTrack(
|
|
||||||
trackingState,
|
|
||||||
siteSettings,
|
|
||||||
Session.current(),
|
|
||||||
currentUser
|
|
||||||
);
|
|
||||||
|
|
||||||
const topicController = null;
|
|
||||||
|
|
||||||
Discourse.set("hasFocus", true);
|
|
||||||
|
|
||||||
tracker.reset();
|
|
||||||
tracker.start(1, topicController);
|
|
||||||
|
|
||||||
tracker.setOnscreen([1, 2, 3], [1, 2, 3]);
|
|
||||||
|
|
||||||
clock.tick(1050);
|
|
||||||
clock.tick(1050);
|
|
||||||
|
|
||||||
// no ajax yet
|
|
||||||
assert.equal(0, timings.length);
|
|
||||||
|
|
||||||
tracker.setOnscreen([1, 2, 3, 4], [1, 2, 3]);
|
|
||||||
|
|
||||||
clock.tick(1050);
|
|
||||||
clock.tick(1050);
|
|
||||||
|
|
||||||
// we should be rushed now cause there is a new thing on the screen
|
|
||||||
assert.equal(1, timings.length);
|
|
||||||
|
|
||||||
const req =
|
|
||||||
"timings%5B1%5D=3000&timings%5B2%5D=3000&timings%5B3%5D=3000&timings%5B4%5D=1000&topic_time=3000&topic_id=1";
|
|
||||||
assert.equal(timings[0].requestBody, req);
|
|
||||||
|
|
||||||
tracker.stop();
|
|
||||||
|
|
||||||
assert.equal(2, timings.length);
|
|
||||||
|
|
||||||
const req2 =
|
|
||||||
"timings%5B1%5D=1200&timings%5B2%5D=1200&timings%5B3%5D=1200&timings%5B4%5D=1200&topic_time=1200&topic_id=1";
|
|
||||||
|
|
||||||
assert.equal(timings[1].requestBody, req2);
|
|
||||||
});
|
|
66
test/javascripts/services/document-title-test.js
Normal file
66
test/javascripts/services/document-title-test.js
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
import { discourseModule } from "helpers/qunit-helpers";
|
||||||
|
import { currentUser } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
discourseModule("service:document-title", {
|
||||||
|
beforeEach() {
|
||||||
|
this.documentTitle = this.container.lookup("service:document-title");
|
||||||
|
this.documentTitle.currentUser = null;
|
||||||
|
this.container.lookup("session:main").hasFocus = true;
|
||||||
|
},
|
||||||
|
afterEach() {
|
||||||
|
this.documentTitle.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("it updates the document title", function(assert) {
|
||||||
|
this.documentTitle.setTitle("Test Title");
|
||||||
|
assert.equal(document.title, "Test Title", "title is correct");
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test(
|
||||||
|
"it doesn't display notification counts for anonymous users",
|
||||||
|
function(assert) {
|
||||||
|
this.documentTitle.setTitle("test notifications");
|
||||||
|
this.documentTitle.updateNotificationCount(5);
|
||||||
|
assert.equal(document.title, "test notifications");
|
||||||
|
this.documentTitle.setFocus(false);
|
||||||
|
this.documentTitle.updateNotificationCount(6);
|
||||||
|
assert.equal(document.title, "test notifications");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
QUnit.test("it displays notification counts for logged in users", function(
|
||||||
|
assert
|
||||||
|
) {
|
||||||
|
this.documentTitle.currentUser = currentUser();
|
||||||
|
this.documentTitle.currentUser.dynamic_favicon = false;
|
||||||
|
this.documentTitle.setTitle("test notifications");
|
||||||
|
this.documentTitle.updateNotificationCount(5);
|
||||||
|
assert.equal(document.title, "test notifications");
|
||||||
|
this.documentTitle.setFocus(false);
|
||||||
|
this.documentTitle.updateNotificationCount(6);
|
||||||
|
assert.equal(document.title, "(6) test notifications");
|
||||||
|
this.documentTitle.setFocus(true);
|
||||||
|
assert.equal(document.title, "test notifications");
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test(
|
||||||
|
"it doesn't increment background context counts when focused",
|
||||||
|
function(assert) {
|
||||||
|
this.documentTitle.setTitle("background context");
|
||||||
|
this.documentTitle.setFocus(true);
|
||||||
|
this.documentTitle.incrementBackgroundContextCount();
|
||||||
|
assert.equal(document.title, "background context");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
QUnit.test("it increments background context counts when not focused", function(
|
||||||
|
assert
|
||||||
|
) {
|
||||||
|
this.documentTitle.setTitle("background context");
|
||||||
|
this.documentTitle.setFocus(false);
|
||||||
|
this.documentTitle.incrementBackgroundContextCount();
|
||||||
|
assert.equal(document.title, "(1) background context");
|
||||||
|
this.documentTitle.setFocus(true);
|
||||||
|
assert.equal(document.title, "background context");
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user