mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 18:03:43 +08:00
DEV: Replace add-archetype-class mixin with body-class helper (#25813)
This commit is contained in:
parent
13eabff739
commit
502eceb570
|
@ -7,7 +7,6 @@ import $ from "jquery";
|
|||
import ClickTrack from "discourse/lib/click-track";
|
||||
import DiscourseURL from "discourse/lib/url";
|
||||
import { highlightPost } from "discourse/lib/utilities";
|
||||
import AddArchetypeClass from "discourse/mixins/add-archetype-class";
|
||||
import MobileScrollDirection from "discourse/mixins/mobile-scroll-direction";
|
||||
import Scrolling from "discourse/mixins/scrolling";
|
||||
import discourseLater from "discourse-common/lib/later";
|
||||
|
@ -15,206 +14,193 @@ import { bind, observes } from "discourse-common/utils/decorators";
|
|||
|
||||
const MOBILE_SCROLL_DIRECTION_CHECK_THROTTLE = 300;
|
||||
|
||||
export default Component.extend(
|
||||
AddArchetypeClass,
|
||||
Scrolling,
|
||||
MobileScrollDirection,
|
||||
{
|
||||
userFilters: alias("topic.userFilters"),
|
||||
classNameBindings: [
|
||||
"multiSelect",
|
||||
"topic.archetype",
|
||||
"topic.is_warning",
|
||||
"topic.category.read_restricted:read_restricted",
|
||||
"topic.deleted:deleted-topic",
|
||||
],
|
||||
menuVisible: true,
|
||||
SHORT_POST: 1200,
|
||||
export default Component.extend(Scrolling, MobileScrollDirection, {
|
||||
userFilters: alias("topic.userFilters"),
|
||||
classNameBindings: [
|
||||
"multiSelect",
|
||||
"topic.archetype",
|
||||
"topic.is_warning",
|
||||
"topic.category.read_restricted:read_restricted",
|
||||
"topic.deleted:deleted-topic",
|
||||
],
|
||||
menuVisible: true,
|
||||
SHORT_POST: 1200,
|
||||
|
||||
postStream: alias("topic.postStream"),
|
||||
archetype: alias("topic.archetype"),
|
||||
dockAt: 0,
|
||||
postStream: alias("topic.postStream"),
|
||||
dockAt: 0,
|
||||
|
||||
_lastShowTopic: null,
|
||||
_lastShowTopic: null,
|
||||
|
||||
mobileScrollDirection: null,
|
||||
pauseHeaderTopicUpdate: false,
|
||||
mobileScrollDirection: null,
|
||||
pauseHeaderTopicUpdate: false,
|
||||
|
||||
@observes("enteredAt")
|
||||
_enteredTopic() {
|
||||
// Ember is supposed to only call observers when values change but something
|
||||
// in our view set up is firing this observer with the same value. This check
|
||||
// prevents scrolled from being called twice
|
||||
if (this.enteredAt && this.lastEnteredAt !== this.enteredAt) {
|
||||
this._lastShowTopic = null;
|
||||
schedule("afterRender", this.scrolled);
|
||||
this.set("lastEnteredAt", this.enteredAt);
|
||||
}
|
||||
},
|
||||
@observes("enteredAt")
|
||||
_enteredTopic() {
|
||||
// Ember is supposed to only call observers when values change but something
|
||||
// in our view set up is firing this observer with the same value. This check
|
||||
// prevents scrolled from being called twice
|
||||
if (this.enteredAt && this.lastEnteredAt !== this.enteredAt) {
|
||||
this._lastShowTopic = null;
|
||||
schedule("afterRender", this.scrolled);
|
||||
this.set("lastEnteredAt", this.enteredAt);
|
||||
}
|
||||
},
|
||||
|
||||
_highlightPost(postNumber, options = {}) {
|
||||
if (isBlank(options.jump) || options.jump !== false) {
|
||||
scheduleOnce("afterRender", null, highlightPost, postNumber);
|
||||
}
|
||||
},
|
||||
_highlightPost(postNumber, options = {}) {
|
||||
if (isBlank(options.jump) || options.jump !== false) {
|
||||
scheduleOnce("afterRender", null, highlightPost, postNumber);
|
||||
}
|
||||
},
|
||||
|
||||
_hideTopicInHeader() {
|
||||
this.appEvents.trigger("header:hide-topic");
|
||||
this._lastShowTopic = false;
|
||||
},
|
||||
_hideTopicInHeader() {
|
||||
this.appEvents.trigger("header:hide-topic");
|
||||
this._lastShowTopic = false;
|
||||
},
|
||||
|
||||
_showTopicInHeader(topic) {
|
||||
if (this.pauseHeaderTopicUpdate) {
|
||||
return;
|
||||
}
|
||||
this.appEvents.trigger("header:show-topic", topic);
|
||||
this._lastShowTopic = true;
|
||||
},
|
||||
_showTopicInHeader(topic) {
|
||||
if (this.pauseHeaderTopicUpdate) {
|
||||
return;
|
||||
}
|
||||
this.appEvents.trigger("header:show-topic", topic);
|
||||
this._lastShowTopic = true;
|
||||
},
|
||||
|
||||
_updateTopic(topic, debounceDuration) {
|
||||
if (topic === null) {
|
||||
this._hideTopicInHeader();
|
||||
|
||||
if (debounceDuration && !this.pauseHeaderTopicUpdate) {
|
||||
this.pauseHeaderTopicUpdate = true;
|
||||
this._lastShowTopic = true;
|
||||
|
||||
discourseLater(() => {
|
||||
this._lastShowTopic = false;
|
||||
this.pauseHeaderTopicUpdate = false;
|
||||
}, debounceDuration);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const offset = window.pageYOffset || document.documentElement.scrollTop;
|
||||
this._lastShowTopic = this.shouldShowTopicInHeader(topic, offset);
|
||||
|
||||
if (this._lastShowTopic) {
|
||||
this._showTopicInHeader(topic);
|
||||
} else {
|
||||
this._hideTopicInHeader();
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.bindScrolling();
|
||||
window.addEventListener("resize", this.scrolled);
|
||||
$(this.element).on(
|
||||
"click.discourse-redirect",
|
||||
".cooked a, a.track-link",
|
||||
(e) => ClickTrack.trackClick(e, getOwner(this))
|
||||
);
|
||||
this.appEvents.on("discourse:focus-changed", this, "gotFocus");
|
||||
this.appEvents.on("post:highlight", this, "_highlightPost");
|
||||
this.appEvents.on("header:update-topic", this, "_updateTopic");
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.unbindScrolling();
|
||||
window.removeEventListener("resize", this.scrolled);
|
||||
|
||||
// Unbind link tracking
|
||||
$(this.element).off(
|
||||
"click.discourse-redirect",
|
||||
".cooked a, a.track-link"
|
||||
);
|
||||
|
||||
this.resetExamineDockCache();
|
||||
|
||||
// this happens after route exit, stuff could have trickled in
|
||||
_updateTopic(topic, debounceDuration) {
|
||||
if (topic === null) {
|
||||
this._hideTopicInHeader();
|
||||
this.appEvents.off("discourse:focus-changed", this, "gotFocus");
|
||||
this.appEvents.off("post:highlight", this, "_highlightPost");
|
||||
this.appEvents.off("header:update-topic", this, "_updateTopic");
|
||||
},
|
||||
|
||||
gotFocus(hasFocus) {
|
||||
if (hasFocus) {
|
||||
this.scrolled();
|
||||
}
|
||||
},
|
||||
if (debounceDuration && !this.pauseHeaderTopicUpdate) {
|
||||
this.pauseHeaderTopicUpdate = true;
|
||||
this._lastShowTopic = true;
|
||||
|
||||
resetExamineDockCache() {
|
||||
this.set("dockAt", 0);
|
||||
},
|
||||
|
||||
shouldShowTopicInHeader(topic, offset) {
|
||||
// On mobile, we show the header topic if the user has scrolled past the topic
|
||||
// title and the current scroll direction is down
|
||||
// On desktop the user only needs to scroll past the topic title.
|
||||
return (
|
||||
offset > this.dockAt &&
|
||||
(!this.site.mobileView || this.mobileScrollDirection === "down")
|
||||
);
|
||||
},
|
||||
|
||||
// The user has scrolled the window, or it is finished rendering and ready for processing.
|
||||
@bind
|
||||
scrolled() {
|
||||
if (this.isDestroyed || this.isDestroying || this._state !== "inDOM") {
|
||||
return;
|
||||
discourseLater(() => {
|
||||
this._lastShowTopic = false;
|
||||
this.pauseHeaderTopicUpdate = false;
|
||||
}, debounceDuration);
|
||||
}
|
||||
|
||||
const offset = window.pageYOffset || document.documentElement.scrollTop;
|
||||
if (this.dockAt === 0) {
|
||||
const title = document.querySelector("#topic-title");
|
||||
if (title) {
|
||||
this.set(
|
||||
"dockAt",
|
||||
title.getBoundingClientRect().top + window.scrollY
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const offset = window.pageYOffset || document.documentElement.scrollTop;
|
||||
this._lastShowTopic = this.shouldShowTopicInHeader(topic, offset);
|
||||
|
||||
if (this._lastShowTopic) {
|
||||
this._showTopicInHeader(topic);
|
||||
} else {
|
||||
this._hideTopicInHeader();
|
||||
}
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.bindScrolling();
|
||||
window.addEventListener("resize", this.scrolled);
|
||||
$(this.element).on(
|
||||
"click.discourse-redirect",
|
||||
".cooked a, a.track-link",
|
||||
(e) => ClickTrack.trackClick(e, getOwner(this))
|
||||
);
|
||||
this.appEvents.on("discourse:focus-changed", this, "gotFocus");
|
||||
this.appEvents.on("post:highlight", this, "_highlightPost");
|
||||
this.appEvents.on("header:update-topic", this, "_updateTopic");
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.unbindScrolling();
|
||||
window.removeEventListener("resize", this.scrolled);
|
||||
|
||||
// Unbind link tracking
|
||||
$(this.element).off("click.discourse-redirect", ".cooked a, a.track-link");
|
||||
|
||||
this.resetExamineDockCache();
|
||||
|
||||
// this happens after route exit, stuff could have trickled in
|
||||
this._hideTopicInHeader();
|
||||
this.appEvents.off("discourse:focus-changed", this, "gotFocus");
|
||||
this.appEvents.off("post:highlight", this, "_highlightPost");
|
||||
this.appEvents.off("header:update-topic", this, "_updateTopic");
|
||||
},
|
||||
|
||||
gotFocus(hasFocus) {
|
||||
if (hasFocus) {
|
||||
this.scrolled();
|
||||
}
|
||||
},
|
||||
|
||||
resetExamineDockCache() {
|
||||
this.set("dockAt", 0);
|
||||
},
|
||||
|
||||
shouldShowTopicInHeader(topic, offset) {
|
||||
// On mobile, we show the header topic if the user has scrolled past the topic
|
||||
// title and the current scroll direction is down
|
||||
// On desktop the user only needs to scroll past the topic title.
|
||||
return (
|
||||
offset > this.dockAt &&
|
||||
(!this.site.mobileView || this.mobileScrollDirection === "down")
|
||||
);
|
||||
},
|
||||
|
||||
// The user has scrolled the window, or it is finished rendering and ready for processing.
|
||||
@bind
|
||||
scrolled() {
|
||||
if (this.isDestroyed || this.isDestroying || this._state !== "inDOM") {
|
||||
return;
|
||||
}
|
||||
|
||||
const offset = window.pageYOffset || document.documentElement.scrollTop;
|
||||
if (this.dockAt === 0) {
|
||||
const title = document.querySelector("#topic-title");
|
||||
if (title) {
|
||||
this.set("dockAt", title.getBoundingClientRect().top + window.scrollY);
|
||||
}
|
||||
}
|
||||
|
||||
this.set("hasScrolled", offset > 0);
|
||||
this.set("hasScrolled", offset > 0);
|
||||
|
||||
const showTopic = this.shouldShowTopicInHeader(this.topic, offset);
|
||||
const showTopic = this.shouldShowTopicInHeader(this.topic, offset);
|
||||
|
||||
if (showTopic !== this._lastShowTopic) {
|
||||
if (showTopic) {
|
||||
this._showTopicInHeader(this.topic);
|
||||
} else {
|
||||
if (!DiscourseURL.isJumpScheduled()) {
|
||||
const loadingNear =
|
||||
this.topic.get("postStream.loadingNearPost") || 1;
|
||||
if (loadingNear === 1) {
|
||||
this._hideTopicInHeader();
|
||||
}
|
||||
if (showTopic !== this._lastShowTopic) {
|
||||
if (showTopic) {
|
||||
this._showTopicInHeader(this.topic);
|
||||
} else {
|
||||
if (!DiscourseURL.isJumpScheduled()) {
|
||||
const loadingNear = this.topic.get("postStream.loadingNearPost") || 1;
|
||||
if (loadingNear === 1) {
|
||||
this._hideTopicInHeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since the user has scrolled, we need to check the scroll direction on mobile.
|
||||
// We use throttle instead of debounce because we want the switch to occur
|
||||
// at the start of the scroll. This feels a lot more snappy compared to waiting
|
||||
// for the scroll to end if we debounce.
|
||||
if (this.site.mobileView && this.hasScrolled) {
|
||||
throttle(
|
||||
this,
|
||||
this.calculateDirection,
|
||||
offset,
|
||||
MOBILE_SCROLL_DIRECTION_CHECK_THROTTLE
|
||||
);
|
||||
}
|
||||
|
||||
// Trigger a scrolled event
|
||||
this.appEvents.trigger("topic:scrolled", offset);
|
||||
},
|
||||
|
||||
// We observe the scroll direction on mobile and if it's down, we show the topic
|
||||
// in the header, otherwise, we hide it.
|
||||
@observes("mobileScrollDirection")
|
||||
toggleMobileHeaderTopic() {
|
||||
return this.appEvents.trigger(
|
||||
"header:update-topic",
|
||||
this.mobileScrollDirection === "down" ? this.topic : null
|
||||
// Since the user has scrolled, we need to check the scroll direction on mobile.
|
||||
// We use throttle instead of debounce because we want the switch to occur
|
||||
// at the start of the scroll. This feels a lot more snappy compared to waiting
|
||||
// for the scroll to end if we debounce.
|
||||
if (this.site.mobileView && this.hasScrolled) {
|
||||
throttle(
|
||||
this,
|
||||
this.calculateDirection,
|
||||
offset,
|
||||
MOBILE_SCROLL_DIRECTION_CHECK_THROTTLE
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Trigger a scrolled event
|
||||
this.appEvents.trigger("topic:scrolled", offset);
|
||||
},
|
||||
|
||||
// We observe the scroll direction on mobile and if it's down, we show the topic
|
||||
// in the header, otherwise, we hide it.
|
||||
@observes("mobileScrollDirection")
|
||||
toggleMobileHeaderTopic() {
|
||||
return this.appEvents.trigger(
|
||||
"header:update-topic",
|
||||
this.mobileScrollDirection === "down" ? this.topic : null
|
||||
);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
import { observes, on } from "discourse-common/utils/decorators";
|
||||
|
||||
// Mix this in to a view that has a `archetype` property to automatically
|
||||
// add it to the body as the view is entered / left / model is changed.
|
||||
// This is used for keeping the `body` style in sync for the background image.
|
||||
export default {
|
||||
_cleanUp() {
|
||||
document.body.classList.forEach((name) => {
|
||||
if (/\barchetype-\S+/g.test(name)) {
|
||||
document.body.classList.remove(name);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@observes("archetype")
|
||||
@on("init")
|
||||
_archetypeChanged() {
|
||||
this._cleanUp();
|
||||
|
||||
if (this.archetype) {
|
||||
document.body.classList.add(`archetype-${this.archetype}`);
|
||||
}
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this._cleanUp();
|
||||
},
|
||||
};
|
|
@ -17,6 +17,7 @@
|
|||
@tags={{this.model.tags}}
|
||||
/>
|
||||
<AddTopicStatusClasses @topic={{this.model}} />
|
||||
{{body-class (concat "archetype-" this.model.archetype)}}
|
||||
<div class="container">
|
||||
<DiscourseBanner
|
||||
@overlay={{this.hasScrolled}}
|
||||
|
|
Loading…
Reference in New Issue
Block a user