From fa2bffd618f1b5336cb9ba0f4e0dd74780f8edc5 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Thu, 19 May 2016 12:16:19 -0400 Subject: [PATCH] FIX: Dock the timeline if you scroll down too much --- .../components/scrolling-post-stream.js.es6 | 4 +- .../discourse/components/site-header.js.es6 | 52 +++++++------------ .../components/topic-timeline.js.es6 | 39 +++++++++++--- .../discourse/mixins/docking.js.es6 | 25 +++++++++ .../javascripts/discourse/templates/topic.hbs | 12 ++--- .../discourse/widgets/topic-timeline.js.es6 | 19 +++++++ .../stylesheets/desktop/topic-timeline.scss | 7 ++- 7 files changed, 109 insertions(+), 49 deletions(-) create mode 100644 app/assets/javascripts/discourse/mixins/docking.js.es6 diff --git a/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 b/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 index 89f77192bf0..cfbcb5ffae1 100644 --- a/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 +++ b/app/assets/javascripts/discourse/components/scrolling-post-stream.js.es6 @@ -30,7 +30,7 @@ export default MountWidget.extend({ _currentPost: -1, _currentVisible: null, - args: Ember.computed(function() { + buildArgs() { return this.getProperties('posts', 'canCreatePost', 'multiSelect', @@ -38,7 +38,7 @@ export default MountWidget.extend({ 'selectedQuery', 'selectedPostsCount', 'searchService'); - }).volatile(), + }, beforePatch() { const $body = $(document); diff --git a/app/assets/javascripts/discourse/components/site-header.js.es6 b/app/assets/javascripts/discourse/components/site-header.js.es6 index d8e87bb7d80..0cac49b191b 100644 --- a/app/assets/javascripts/discourse/components/site-header.js.es6 +++ b/app/assets/javascripts/discourse/components/site-header.js.es6 @@ -1,5 +1,6 @@ import MountWidget from 'discourse/components/mount-widget'; import { observes } from 'ember-addons/ember-computed-decorators'; +import Docking from 'discourse/mixins/docking'; const _flagProperties = []; function addFlagProperty(prop) { @@ -8,45 +9,37 @@ function addFlagProperty(prop) { const PANEL_BODY_MARGIN = 30; -const SiteHeaderComponent = MountWidget.extend({ +const SiteHeaderComponent = MountWidget.extend(Docking, { widget: 'header', docAt: null, dockedHeader: null, _topic: null, - // profileWidget: true, - // classNameBindings: ['editingTopic'], - @observes('currentUser.unread_notifications', 'currentUser.unread_private_messages') _notificationsChanged() { this.queueRerender(); }, - examineDockHeader() { + dockCheck(info) { + if (this.docAt === null) { + const outlet = $('#main-outlet'); + if (!(outlet && outlet.length === 1)) return; + this.docAt = outlet.offset().top; + } + const $body = $('body'); - - // Check the dock after the current run loop. While rendering, - // it's much slower to calculate `outlet.offset()` - Ember.run.next(() => { - if (this.docAt === null) { - const outlet = $('#main-outlet'); - if (!(outlet && outlet.length === 1)) return; - this.docAt = outlet.offset().top; + const offset = info.offset(); + if (offset >= this.docAt) { + if (!this.dockedHeader) { + $body.addClass('docked'); + this.dockedHeader = true; } - - const offset = window.pageYOffset || $('html').scrollTop(); - if (offset >= this.docAt) { - if (!this.dockedHeader) { - $body.addClass('docked'); - this.dockedHeader = true; - } - } else { - if (this.dockedHeader) { - $body.removeClass('docked'); - this.dockedHeader = false; - } + } else { + if (this.dockedHeader) { + $body.removeClass('docked'); + this.dockedHeader = false; } - }); + } }, setTopic(topic) { @@ -56,8 +49,6 @@ const SiteHeaderComponent = MountWidget.extend({ didInsertElement() { this._super(); - $(window).bind('scroll.discourse-dock', () => this.examineDockHeader()); - $(document).bind('touchmove.discourse-dock', () => this.examineDockHeader()); $(window).on('resize.discourse-menu-panel', () => this.afterRender()); this.appEvents.on('header:show-topic', topic => this.setTopic(topic)); @@ -72,16 +63,13 @@ const SiteHeaderComponent = MountWidget.extend({ this.eventDispatched('dom:clean', 'header'); } }); - - this.examineDockHeader(); }, willDestroyElement() { this._super(); - $(window).unbind('scroll.discourse-dock'); - $(document).unbind('touchmove.discourse-dock'); $('body').off('keydown.header'); this.appEvents.off('notifications:changed'); + this.appEvents.off('header:keyboard-trigger'); $(window).off('resize.discourse-menu-panel'); this.appEvents.off('header:show-topic'); diff --git a/app/assets/javascripts/discourse/components/topic-timeline.js.es6 b/app/assets/javascripts/discourse/components/topic-timeline.js.es6 index bb96c4c6329..1a5dcbbdd9e 100644 --- a/app/assets/javascripts/discourse/components/topic-timeline.js.es6 +++ b/app/assets/javascripts/discourse/components/topic-timeline.js.es6 @@ -1,16 +1,43 @@ import MountWidget from 'discourse/components/mount-widget'; -import computed from 'ember-addons/ember-computed-decorators'; +import Docking from 'discourse/mixins/docking'; -export default MountWidget.extend({ - widget: 'topic-timeline', +export default MountWidget.extend(Docking, { + widget: 'topic-timeline-container', + dockAt: null, - @computed('topic') - args(topic) { - return { topic, topicTrackingState: this.topicTrackingState }; + buildArgs() { + return { topic: this.get('topic'), + topicTrackingState: this.topicTrackingState, + dockAt: this.dockAt }; + }, + + dockCheck(info) { + const topicBottom = $('#topic-bottom').offset().top; + const $timeline = this.$('.timeline-container'); + const timelineHeight = $timeline.height(); + + const tTop = 140; + + const prev = this.dockAt; + const pos = tTop + info.offset() + timelineHeight; + if (pos > topicBottom) { + this.dockAt = topicBottom - timelineHeight - $timeline.offsetParent().offset().top; + } else { + this.dockAt = null; + } + + if (this.dockAt !== prev) { + this.queueRerender(); + } }, didInsertElement() { this._super(); this.dispatch('topic:current-post-changed', 'timeline-scrollarea'); + }, + + willDestroyElement() { + this._super(); + this.appEvents.off('topic:current-post-changed'); } }); diff --git a/app/assets/javascripts/discourse/mixins/docking.js.es6 b/app/assets/javascripts/discourse/mixins/docking.js.es6 new file mode 100644 index 00000000000..a70bcdee5f8 --- /dev/null +++ b/app/assets/javascripts/discourse/mixins/docking.js.es6 @@ -0,0 +1,25 @@ +const helper = { + offset: () => window.pageYOffset || $('html').scrollTop() +}; + +export default Ember.Mixin.create({ + _dockHandler: null, + + didInsertElement() { + this._super(); + + // Check the dock after the current run loop since reading sizes is slow + this._dockHandler = () => Ember.run.next(() => this.dockCheck(helper)); + + $(window).bind('scroll.discourse-dock', this._dockHandler); + $(document).bind('touchmove.discourse-dock', this._dockHandler); + + this._dockHandler(); + }, + + willDestroyElement() { + this._super(); + $(window).unbind('scroll.discourse-dock', this._dockHandler); + $(document).unbind('touchmove.discourse-dock', this._dockHandler); + } +}); diff --git a/app/assets/javascripts/discourse/templates/topic.hbs b/app/assets/javascripts/discourse/templates/topic.hbs index f1e69848af4..e7457fb8ae6 100644 --- a/app/assets/javascripts/discourse/templates/topic.hbs +++ b/app/assets/javascripts/discourse/templates/topic.hbs @@ -72,13 +72,11 @@
{{#if showTimeline}} -
- {{topic-timeline topic=model - jumpTop="jumpTop" - jumpToPost="jumpToPost" - jumpBottom="jumpBottom" - replyToPost="replyToPost"}} -
+ {{topic-timeline topic=model + jumpTop="jumpTop" + jumpToPost="jumpToPost" + jumpBottom="jumpBottom" + replyToPost="replyToPost"}} {{else}} {{topic-progress topic=model jumpTop="jumpTop" diff --git a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 index b1df89fc264..275deb53959 100644 --- a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 @@ -169,6 +169,25 @@ createWidget('timeline-scrollarea', { } }); +createWidget('topic-timeline-container', { + tagName: 'div.timeline-container', + buildClasses(attrs) { + if (attrs.dockAt) { return 'timeline-docked'; } + }, + + buildAttributes(attrs) { + if (attrs.dockAt) { + return { style: `top: ${attrs.dockAt}px` }; + }; + + return { style: 'top: 140px' }; + }, + + html(attrs) { + return this.attach('topic-timeline', attrs); + } +}); + export default createWidget('topic-timeline', { tagName: 'div.topic-timeline', diff --git a/app/assets/stylesheets/desktop/topic-timeline.scss b/app/assets/stylesheets/desktop/topic-timeline.scss index 5cd77f5d893..a90dc9127e3 100644 --- a/app/assets/stylesheets/desktop/topic-timeline.scss +++ b/app/assets/stylesheets/desktop/topic-timeline.scss @@ -2,13 +2,16 @@ width: 900px; } -.fixed-gutter { +.timeline-container { width: 100%; box-sizing: border-box; z-index: 1; margin-left: 757px; position: fixed; - top: 140px; + + &.timeline-docked { + position: absolute; + } .topic-timeline { margin-left: 3em;