FIX: Dock the timeline if you scroll down too much

This commit is contained in:
Robin Ward 2016-05-19 12:16:19 -04:00
parent 96b2fb791e
commit fa2bffd618
No known key found for this signature in database
GPG Key ID: 0E091E2B4ED1B83D
7 changed files with 109 additions and 49 deletions

View File

@ -30,7 +30,7 @@ export default MountWidget.extend({
_currentPost: -1, _currentPost: -1,
_currentVisible: null, _currentVisible: null,
args: Ember.computed(function() { buildArgs() {
return this.getProperties('posts', return this.getProperties('posts',
'canCreatePost', 'canCreatePost',
'multiSelect', 'multiSelect',
@ -38,7 +38,7 @@ export default MountWidget.extend({
'selectedQuery', 'selectedQuery',
'selectedPostsCount', 'selectedPostsCount',
'searchService'); 'searchService');
}).volatile(), },
beforePatch() { beforePatch() {
const $body = $(document); const $body = $(document);

View File

@ -1,5 +1,6 @@
import MountWidget from 'discourse/components/mount-widget'; import MountWidget from 'discourse/components/mount-widget';
import { observes } from 'ember-addons/ember-computed-decorators'; import { observes } from 'ember-addons/ember-computed-decorators';
import Docking from 'discourse/mixins/docking';
const _flagProperties = []; const _flagProperties = [];
function addFlagProperty(prop) { function addFlagProperty(prop) {
@ -8,45 +9,37 @@ function addFlagProperty(prop) {
const PANEL_BODY_MARGIN = 30; const PANEL_BODY_MARGIN = 30;
const SiteHeaderComponent = MountWidget.extend({ const SiteHeaderComponent = MountWidget.extend(Docking, {
widget: 'header', widget: 'header',
docAt: null, docAt: null,
dockedHeader: null, dockedHeader: null,
_topic: null, _topic: null,
// profileWidget: true,
// classNameBindings: ['editingTopic'],
@observes('currentUser.unread_notifications', 'currentUser.unread_private_messages') @observes('currentUser.unread_notifications', 'currentUser.unread_private_messages')
_notificationsChanged() { _notificationsChanged() {
this.queueRerender(); 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'); const $body = $('body');
const offset = info.offset();
// Check the dock after the current run loop. While rendering, if (offset >= this.docAt) {
// it's much slower to calculate `outlet.offset()` if (!this.dockedHeader) {
Ember.run.next(() => { $body.addClass('docked');
if (this.docAt === null) { this.dockedHeader = true;
const outlet = $('#main-outlet');
if (!(outlet && outlet.length === 1)) return;
this.docAt = outlet.offset().top;
} }
} else {
const offset = window.pageYOffset || $('html').scrollTop(); if (this.dockedHeader) {
if (offset >= this.docAt) { $body.removeClass('docked');
if (!this.dockedHeader) { this.dockedHeader = false;
$body.addClass('docked');
this.dockedHeader = true;
}
} else {
if (this.dockedHeader) {
$body.removeClass('docked');
this.dockedHeader = false;
}
} }
}); }
}, },
setTopic(topic) { setTopic(topic) {
@ -56,8 +49,6 @@ const SiteHeaderComponent = MountWidget.extend({
didInsertElement() { didInsertElement() {
this._super(); this._super();
$(window).bind('scroll.discourse-dock', () => this.examineDockHeader());
$(document).bind('touchmove.discourse-dock', () => this.examineDockHeader());
$(window).on('resize.discourse-menu-panel', () => this.afterRender()); $(window).on('resize.discourse-menu-panel', () => this.afterRender());
this.appEvents.on('header:show-topic', topic => this.setTopic(topic)); this.appEvents.on('header:show-topic', topic => this.setTopic(topic));
@ -72,16 +63,13 @@ const SiteHeaderComponent = MountWidget.extend({
this.eventDispatched('dom:clean', 'header'); this.eventDispatched('dom:clean', 'header');
} }
}); });
this.examineDockHeader();
}, },
willDestroyElement() { willDestroyElement() {
this._super(); this._super();
$(window).unbind('scroll.discourse-dock');
$(document).unbind('touchmove.discourse-dock');
$('body').off('keydown.header'); $('body').off('keydown.header');
this.appEvents.off('notifications:changed'); this.appEvents.off('notifications:changed');
this.appEvents.off('header:keyboard-trigger');
$(window).off('resize.discourse-menu-panel'); $(window).off('resize.discourse-menu-panel');
this.appEvents.off('header:show-topic'); this.appEvents.off('header:show-topic');

View File

@ -1,16 +1,43 @@
import MountWidget from 'discourse/components/mount-widget'; 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({ export default MountWidget.extend(Docking, {
widget: 'topic-timeline', widget: 'topic-timeline-container',
dockAt: null,
@computed('topic') buildArgs() {
args(topic) { return { topic: this.get('topic'),
return { topic, topicTrackingState: this.topicTrackingState }; 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() { didInsertElement() {
this._super(); this._super();
this.dispatch('topic:current-post-changed', 'timeline-scrollarea'); this.dispatch('topic:current-post-changed', 'timeline-scrollarea');
},
willDestroyElement() {
this._super();
this.appEvents.off('topic:current-post-changed');
} }
}); });

View File

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

View File

@ -72,13 +72,11 @@
<div class="posts-wrapper"> <div class="posts-wrapper">
{{#if showTimeline}} {{#if showTimeline}}
<div class='fixed-gutter'> {{topic-timeline topic=model
{{topic-timeline topic=model jumpTop="jumpTop"
jumpTop="jumpTop" jumpToPost="jumpToPost"
jumpToPost="jumpToPost" jumpBottom="jumpBottom"
jumpBottom="jumpBottom" replyToPost="replyToPost"}}
replyToPost="replyToPost"}}
</div>
{{else}} {{else}}
{{topic-progress topic=model {{topic-progress topic=model
jumpTop="jumpTop" jumpTop="jumpTop"

View File

@ -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', { export default createWidget('topic-timeline', {
tagName: 'div.topic-timeline', tagName: 'div.topic-timeline',

View File

@ -2,13 +2,16 @@
width: 900px; width: 900px;
} }
.fixed-gutter { .timeline-container {
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
z-index: 1; z-index: 1;
margin-left: 757px; margin-left: 757px;
position: fixed; position: fixed;
top: 140px;
&.timeline-docked {
position: absolute;
}
.topic-timeline { .topic-timeline {
margin-left: 3em; margin-left: 3em;