FIX: scrolling was off sometimes
Also changed "Jump To Post" to go to the post index in the stream rather than the post number
This commit is contained in:
parent
a26d3fc2de
commit
d5412cff4e
|
@ -18,13 +18,15 @@ RawHandlebars.helpers['get'] = function(context, options) {
|
||||||
var firstContext = options.contexts[0];
|
var firstContext = options.contexts[0];
|
||||||
var val = firstContext[context];
|
var val = firstContext[context];
|
||||||
|
|
||||||
if (context.indexOf('controller') === 0) {
|
if (context.indexOf('controller.') === 0) {
|
||||||
context = context.replace(/^controller\./, '');
|
context = context.slice(context.indexOf('.') + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val && val.isDescriptor) { return Em.get(firstContext, context); }
|
if (val && val.isDescriptor) {
|
||||||
val = val === undefined ? Em.get(firstContext, context): val;
|
return Em.get(firstContext, context);
|
||||||
return val;
|
}
|
||||||
|
|
||||||
|
return val === undefined ? Em.get(firstContext, context) : val;
|
||||||
};
|
};
|
||||||
|
|
||||||
// adds compatability so this works with stringParams
|
// adds compatability so this works with stringParams
|
||||||
|
|
|
@ -54,8 +54,7 @@ export default MountWidget.extend({
|
||||||
const scrollTop = $body.scrollTop();
|
const scrollTop = $body.scrollTop();
|
||||||
|
|
||||||
// This hack is for when swapping out many cloaked views at once
|
// This hack is for when swapping out many cloaked views at once
|
||||||
// when using keyboard navigation. It could suddenly move the
|
// when using keyboard navigation. It could suddenly move the scroll
|
||||||
// scroll
|
|
||||||
if (this.prevHeight === height && scrollTop !== this.prevScrollTop) {
|
if (this.prevHeight === height && scrollTop !== this.prevScrollTop) {
|
||||||
$body.scrollTop(this.prevScrollTop);
|
$body.scrollTop(this.prevScrollTop);
|
||||||
}
|
}
|
||||||
|
@ -80,7 +79,7 @@ export default MountWidget.extend({
|
||||||
const postsWrapperTop = $('.posts-wrapper').offset().top;
|
const postsWrapperTop = $('.posts-wrapper').offset().top;
|
||||||
const $posts = this.$('.onscreen-post, .cloaked-post');
|
const $posts = this.$('.onscreen-post, .cloaked-post');
|
||||||
const viewportTop = windowTop - slack;
|
const viewportTop = windowTop - slack;
|
||||||
const topView = findTopView($posts, viewportTop, postsWrapperTop, 0, $posts.length-1);
|
const topView = findTopView($posts, viewportTop, postsWrapperTop, 0, $posts.length - 1);
|
||||||
|
|
||||||
let windowBottom = windowTop + windowHeight;
|
let windowBottom = windowTop + windowHeight;
|
||||||
let viewportBottom = windowBottom + slack;
|
let viewportBottom = windowBottom + slack;
|
||||||
|
@ -93,7 +92,7 @@ export default MountWidget.extend({
|
||||||
let percent = null;
|
let percent = null;
|
||||||
|
|
||||||
const offset = offsetCalculator();
|
const offset = offsetCalculator();
|
||||||
const topCheck = Math.ceil(windowTop + offset);
|
const topCheck = Math.ceil(windowTop + offset + 5);
|
||||||
|
|
||||||
// uncomment to debug the eyeline
|
// uncomment to debug the eyeline
|
||||||
/*
|
/*
|
||||||
|
@ -102,7 +101,7 @@ export default MountWidget.extend({
|
||||||
$('body').prepend('<div class="debug-eyeline"></div>');
|
$('body').prepend('<div class="debug-eyeline"></div>');
|
||||||
$eyeline = $('.debug-eyeline');
|
$eyeline = $('.debug-eyeline');
|
||||||
}
|
}
|
||||||
$eyeline.css({ height: '1px', width: '100%', backgroundColor: 'blue', position: 'absolute', top: `${topCheck}px` });
|
$eyeline.css({ height: '5px', width: '100%', backgroundColor: 'blue', position: 'absolute', top: `${topCheck}px`, zIndex: 999999 });
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let allAbove = true;
|
let allAbove = true;
|
||||||
|
@ -115,7 +114,7 @@ export default MountWidget.extend({
|
||||||
if (!$post) { break; }
|
if (!$post) { break; }
|
||||||
|
|
||||||
const viewTop = $post.offset().top;
|
const viewTop = $post.offset().top;
|
||||||
const postHeight = $post.height();
|
const postHeight = $post.outerHeight(true);
|
||||||
const viewBottom = Math.ceil(viewTop + postHeight);
|
const viewBottom = Math.ceil(viewTop + postHeight);
|
||||||
|
|
||||||
allAbove = allAbove && (viewTop < topCheck);
|
allAbove = allAbove && (viewTop < topCheck);
|
||||||
|
@ -170,7 +169,7 @@ export default MountWidget.extend({
|
||||||
this.sendAction('topVisibleChanged', { post: first, refresh: topRefresh });
|
this.sendAction('topVisibleChanged', { post: first, refresh: topRefresh });
|
||||||
}
|
}
|
||||||
|
|
||||||
const last = posts.objectAt(onscreen[onscreen.length-1]);
|
const last = posts.objectAt(onscreen[onscreen.length - 1]);
|
||||||
if (this._bottomVisible !== last) {
|
if (this._bottomVisible !== last) {
|
||||||
this._bottomVisible = last;
|
this._bottomVisible = last;
|
||||||
this.sendAction('bottomVisibleChanged', { post: last, refresh });
|
this.sendAction('bottomVisibleChanged', { post: last, refresh });
|
||||||
|
@ -184,7 +183,7 @@ export default MountWidget.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (percent !== null) {
|
if (percent !== null) {
|
||||||
if (percent > 1.0) { percent = 1.0; }
|
percent = Math.max(0.0, Math.min(1.0, percent));
|
||||||
|
|
||||||
if (changedPost || (this._currentPercent !== percent)) {
|
if (changedPost || (this._currentPercent !== percent)) {
|
||||||
this._currentPercent = percent;
|
this._currentPercent = percent;
|
||||||
|
|
|
@ -6,10 +6,8 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
jump() {
|
jump() {
|
||||||
let where = parseInt(this.get('postNumber'));
|
const max = this.get("topic.postStream.filteredPostsCount");
|
||||||
if (where < 1) { where = 1; }
|
const where = Math.min(max, Math.max(1, parseInt(this.get("postNumber"))));
|
||||||
const max = this.get('topic.postStream.filteredPostsCount');
|
|
||||||
if (where > max) { where = max; }
|
|
||||||
|
|
||||||
this.jumpToIndex(where);
|
this.jumpToIndex(where);
|
||||||
this.send('closeModal');
|
this.send('closeModal');
|
||||||
|
|
|
@ -452,17 +452,17 @@ export default Ember.Controller.extend(BufferedContent, {
|
||||||
},
|
},
|
||||||
|
|
||||||
jumpToIndex(index) {
|
jumpToIndex(index) {
|
||||||
this._jumpToPostId(this.get('model.postStream.stream')[index - 1]);
|
this._jumpToIndex(index);
|
||||||
},
|
},
|
||||||
|
|
||||||
jumpToPostPrompt() {
|
jumpToPostPrompt() {
|
||||||
const postText = prompt(I18n.t('topic.progress.jump_prompt_long'));
|
const postText = prompt(I18n.t('topic.progress.jump_prompt_long'));
|
||||||
if (postText === null) { return; }
|
if (postText === null) { return; }
|
||||||
|
|
||||||
const postNumber = parseInt(postText, 10);
|
const postIndex = parseInt(postText, 10);
|
||||||
if (postNumber === 0) { return; }
|
if (postIndex === 0) { return; }
|
||||||
|
|
||||||
this._jumpToPostId(this.get('model.postStream').findPostIdForPostNumber(postNumber));
|
this._jumpToIndex(postIndex);
|
||||||
},
|
},
|
||||||
|
|
||||||
jumpToPost(postNumber) {
|
jumpToPost(postNumber) {
|
||||||
|
@ -755,6 +755,12 @@ export default Ember.Controller.extend(BufferedContent, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_jumpToIndex(index) {
|
||||||
|
const stream = this.get("model.postStream.stream");
|
||||||
|
index = Math.max(1, Math.min(stream.length, index));
|
||||||
|
this._jumpToPostId(stream[index - 1]);
|
||||||
|
},
|
||||||
|
|
||||||
_jumpToPostId(postId) {
|
_jumpToPostId(postId) {
|
||||||
if (!postId) {
|
if (!postId) {
|
||||||
Ember.Logger.warn("jump-post code broken - requested an index outside the stream array");
|
Ember.Logger.warn("jump-post code broken - requested an index outside the stream array");
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import DiscourseURL from 'discourse/lib/url';
|
import DiscourseURL from 'discourse/lib/url';
|
||||||
import Composer from 'discourse/models/composer';
|
import Composer from 'discourse/models/composer';
|
||||||
import { scrollTopFor } from 'discourse/lib/offset-calculator';
|
import { minimumOffset } from 'discourse/lib/offset-calculator';
|
||||||
|
|
||||||
const bindings = {
|
const bindings = {
|
||||||
'!': {postAction: 'showFlags'},
|
'!': {postAction: 'showFlags'},
|
||||||
|
@ -318,10 +318,10 @@ export default {
|
||||||
const $selected = ($articles.filter('.selected').length !== 0)
|
const $selected = ($articles.filter('.selected').length !== 0)
|
||||||
? $articles.filter('.selected')
|
? $articles.filter('.selected')
|
||||||
: $articles.filter('[data-islastviewedtopic=true]');
|
: $articles.filter('[data-islastviewedtopic=true]');
|
||||||
|
|
||||||
let index = $articles.index($selected);
|
let index = $articles.index($selected);
|
||||||
|
|
||||||
if ($selected.length !== 0) { //boundries check
|
if ($selected.length !== 0) {
|
||||||
// loop is not allowed
|
|
||||||
if (direction === -1 && index === 0) { return; }
|
if (direction === -1 && index === 0) { return; }
|
||||||
if (direction === 1 && index === ($articles.length - 1) ) { return; }
|
if (direction === 1 && index === ($articles.length - 1) ) { return; }
|
||||||
}
|
}
|
||||||
|
@ -349,14 +349,12 @@ export default {
|
||||||
const $article = $articles.eq(index + direction);
|
const $article = $articles.eq(index + direction);
|
||||||
|
|
||||||
if ($article.length > 0) {
|
if ($article.length > 0) {
|
||||||
|
|
||||||
$articles.removeClass('selected');
|
$articles.removeClass('selected');
|
||||||
$article.addClass('selected');
|
$article.addClass('selected');
|
||||||
|
|
||||||
if ($article.is('.topic-post')) {
|
if ($article.is('.topic-post')) {
|
||||||
$('a.tabLoc', $article).focus();
|
$('a.tabLoc', $article).focus();
|
||||||
this._scrollToPost($article);
|
this._scrollToPost($article);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this._scrollList($article, direction);
|
this._scrollList($article, direction);
|
||||||
}
|
}
|
||||||
|
@ -364,8 +362,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
_scrollToPost($article) {
|
_scrollToPost($article) {
|
||||||
const pos = $article.offset();
|
$(window).scrollTop($article.offset().top - minimumOffset());
|
||||||
$(window).scrollTop(scrollTopFor(pos.top));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_scrollList($article) {
|
_scrollList($article) {
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { scrollTopFor } from 'discourse/lib/offset-calculator';
|
import { minimumOffset } from "discourse/lib/offset-calculator";
|
||||||
|
|
||||||
// Dear traveller, you are entering a zone where we are at war with the browser
|
// Dear traveller, you are entering a zone where we are at war with the browser.
|
||||||
// the browser is insisting on positioning scrollTop per the location it was in
|
// The browser is insisting on positioning scrollTop per the location it was in
|
||||||
// the past, we are insisting on it being where we want it to be
|
// the past, we are insisting on it being where we want it to be.
|
||||||
// The hack is just to keep trying over and over to position the scrollbar (up to 1 minute)
|
// The hack is just to keep trying over and over to position the scrollbar (up to 1 second).
|
||||||
//
|
//
|
||||||
// The root cause is that a "refresh" on a topic page will almost never be at the
|
// The root cause is that a "refresh" on a topic page will almost never be at the
|
||||||
// same position it was in the past, the URL points to the post at the top of the
|
// same position it was in the past, the URL points to the post at the top of the
|
||||||
// page, so a refresh will try to bring that post into view causing drift
|
// page, so a refresh will try to bring that post into view causing drift.
|
||||||
//
|
//
|
||||||
// Additionally if you loaded multiple batches of posts, on refresh they will not
|
// Additionally if you loaded multiple batches of posts, on refresh they will not
|
||||||
// be loaded.
|
// be loaded.
|
||||||
|
@ -18,29 +18,29 @@ import { scrollTopFor } from 'discourse/lib/offset-calculator';
|
||||||
// 1. onbeforeunload ensure we are scrolled to the right spot
|
// 1. onbeforeunload ensure we are scrolled to the right spot
|
||||||
// 2. give up on the scrollbar and implement it ourselves (something that will happen)
|
// 2. give up on the scrollbar and implement it ourselves (something that will happen)
|
||||||
|
|
||||||
|
const LOCK_DURATION_MS = 1000;
|
||||||
const SCROLL_EVENTS = "scroll.lock-on touchmove.lock-on mousedown.lock-on wheel.lock-on DOMMouseScroll.lock-on mousewheel.lock-on keyup.lock-on";
|
const SCROLL_EVENTS = "scroll.lock-on touchmove.lock-on mousedown.lock-on wheel.lock-on DOMMouseScroll.lock-on mousewheel.lock-on keyup.lock-on";
|
||||||
|
const SCROLL_TYPES = ["mousedown", "mousewheel", "touchmove", "wheel"];
|
||||||
|
|
||||||
function within(threshold, x, y) {
|
function within(threshold, x, y) {
|
||||||
return Math.abs(x-y) < threshold;
|
return Math.abs(x - y) < threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class LockOn {
|
export default class LockOn {
|
||||||
constructor(selector, options) {
|
constructor(selector, options) {
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.options = options || {};
|
this.options = options || {};
|
||||||
this.offsetTop = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elementTop() {
|
elementTop() {
|
||||||
const selected = $(this.selector);
|
const $selected = $(this.selector);
|
||||||
if (selected && selected.offset && selected.offset()) {
|
if ($selected && $selected.offset && $selected.offset()) {
|
||||||
const result = selected.offset().top;
|
return $selected.offset().top - minimumOffset();
|
||||||
return scrollTopFor(result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearLock(interval) {
|
clearLock(interval) {
|
||||||
$('body,html').off(SCROLL_EVENTS);
|
$("body, html").off(SCROLL_EVENTS);
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
if (this.options.finished) {
|
if (this.options.finished) {
|
||||||
this.options.finished();
|
this.options.finished();
|
||||||
|
@ -54,9 +54,7 @@ export default class LockOn {
|
||||||
$(window).scrollTop(previousTop);
|
$(window).scrollTop(previousTop);
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
let top = this.elementTop();
|
const top = Math.max(0, this.elementTop());
|
||||||
if (top < 0) { top = 0; }
|
|
||||||
|
|
||||||
const scrollTop = $(window).scrollTop();
|
const scrollTop = $(window).scrollTop();
|
||||||
|
|
||||||
if (typeof(top) === "undefined" || isNaN(top)) {
|
if (typeof(top) === "undefined" || isNaN(top)) {
|
||||||
|
@ -68,20 +66,14 @@ export default class LockOn {
|
||||||
previousTop = top;
|
previousTop = top;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We commit suicide after 3s just to clean up
|
// Commit suicide after a little while
|
||||||
const nowTime = new Date().getTime();
|
if (new Date().getTime() - startedAt > LOCK_DURATION_MS) {
|
||||||
if (nowTime - startedAt > 1000) {
|
|
||||||
return this.clearLock(interval);
|
return this.clearLock(interval);
|
||||||
}
|
}
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
$('body,html').off(SCROLL_EVENTS).on(SCROLL_EVENTS, e => {
|
$("body, html").off(SCROLL_EVENTS).on(SCROLL_EVENTS, e => {
|
||||||
if ( e.which > 0 ||
|
if (e.which > 0 || SCROLL_TYPES.includes(e.type)) {
|
||||||
e.type === "mousedown" ||
|
|
||||||
e.type === "mousewheel" ||
|
|
||||||
e.type === "touchmove" ||
|
|
||||||
e.type === "wheel"
|
|
||||||
) {
|
|
||||||
this.clearLock(interval);
|
this.clearLock(interval);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,50 +1,36 @@
|
||||||
export function scrollTopFor(y) {
|
export function scrollTopFor(y) {
|
||||||
return y - offsetCalculator(y);
|
return y - offsetCalculator();
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function offsetCalculator(y) {
|
export function minimumOffset() {
|
||||||
const $header = $('header');
|
const $header = $("header.d-header");
|
||||||
const $container = $('.posts-wrapper');
|
const headerHeight = $header.outerHeight(true) || 0;
|
||||||
const containerOffset = $container.offset();
|
const headerPositionTop = $header.position().top;
|
||||||
|
return headerHeight + headerPositionTop;
|
||||||
let titleHeight = 0;
|
}
|
||||||
const scrollTop = y || $(window).scrollTop();
|
|
||||||
|
export default function offsetCalculator() {
|
||||||
if (!containerOffset || scrollTop < containerOffset.top) {
|
const min = minimumOffset();
|
||||||
titleHeight = $('#topic-title').height() || 0;
|
|
||||||
}
|
// on mobile, just use the header
|
||||||
|
if ($("html").hasClass("mobile-view")) return min;
|
||||||
const rawWinHeight = $(window).height();
|
|
||||||
const windowHeight = rawWinHeight - titleHeight;
|
const $window = $(window);
|
||||||
|
const windowHeight = $window.height();
|
||||||
const eyeTarget = (windowHeight / 10);
|
const documentHeight = $(document).height();
|
||||||
const headerHeight = $header.outerHeight(true);
|
const topicBottomOffsetTop = $("#topic-bottom").offset().top;
|
||||||
const expectedOffset = titleHeight - ($header.find('.contents').height() || 0) + (eyeTarget * 2);
|
|
||||||
const ideal = headerHeight + ((expectedOffset < 0) ? 0 : expectedOffset);
|
// the footer is bigger than the window, we can scroll down past the last post
|
||||||
|
if (documentHeight - windowHeight > topicBottomOffsetTop) return min;
|
||||||
if ($container.length === 0) { return expectedOffset; }
|
|
||||||
|
const scrollTop = $window.scrollTop();
|
||||||
const topPos = $container.offset().top;
|
const visibleBottomHeight = scrollTop + windowHeight - topicBottomOffsetTop;
|
||||||
|
|
||||||
const docHeight = $(document).height();
|
if (visibleBottomHeight > 0) {
|
||||||
let scrollPercent = Math.min((scrollTop / (docHeight-rawWinHeight)), 1.0);
|
const bottomHeight = documentHeight - topicBottomOffsetTop;
|
||||||
|
const offset = (windowHeight - bottomHeight) * visibleBottomHeight / bottomHeight;
|
||||||
let inter = topPos - scrollTop + ($container.height() * scrollPercent);
|
return Math.max(min, offset);
|
||||||
if (inter < headerHeight + eyeTarget) {
|
}
|
||||||
inter = headerHeight + eyeTarget;
|
|
||||||
}
|
return min;
|
||||||
|
|
||||||
if (inter > ideal) {
|
|
||||||
const bottom = $('#topic-bottom').offset().top;
|
|
||||||
const switchPos = bottom - rawWinHeight - ideal;
|
|
||||||
|
|
||||||
if (scrollTop > switchPos) {
|
|
||||||
const p = Math.max(Math.min((scrollTop + inter - switchPos) / rawWinHeight, 1.0), 0.0);
|
|
||||||
return ((1 - p) * ideal) + (p * inter);
|
|
||||||
} else {
|
|
||||||
return ideal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return inter;
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue