From 36449aa2f25025d6786667e205dadcdbaa971d3b Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Mon, 19 Dec 2016 13:33:55 -0500 Subject: [PATCH] UX: Docking back button on topic timeline --- .../discourse/widgets/hooks.js.es6 | 8 +-- .../discourse/widgets/topic-timeline.js.es6 | 64 +++++++++++++------ .../stylesheets/common/topic-timeline.scss | 12 ++-- 3 files changed, 56 insertions(+), 28 deletions(-) diff --git a/app/assets/javascripts/discourse/widgets/hooks.js.es6 b/app/assets/javascripts/discourse/widgets/hooks.js.es6 index d863d46e81a..45d8f8bf4bf 100644 --- a/app/assets/javascripts/discourse/widgets/hooks.js.es6 +++ b/app/assets/javascripts/discourse/widgets/hooks.js.es6 @@ -56,12 +56,12 @@ let _dragging; const DRAG_NAME = "mousemove.discourse-widget-drag"; const DRAG_NAME_TOUCH = "touchmove.discourse-widget-drag"; -function cancelDrag() { +function cancelDrag(e) { $('body').removeClass('widget-dragging'); $(document).off(DRAG_NAME).off(DRAG_NAME_TOUCH); if (_dragging) { - if (_dragging.dragEnd) { _dragging.dragEnd(); } + if (_dragging.dragEnd) { _dragging.dragEnd(e); } _dragging = null; } } @@ -70,7 +70,7 @@ WidgetClickHook.setupDocumentCallback = function() { if (_watchingDocument) { return; } $(document).on('mousedown.discource-widget-drag, touchstart.discourse-widget-drag', e => { - cancelDrag(); + cancelDrag(e); const widget = findWidget(e.target, DRAG_ATTRIBUTE_NAME); if (widget) { e.preventDefault(); @@ -87,7 +87,7 @@ WidgetClickHook.setupDocumentCallback = function() { } }); - $(document).on('mouseup.discourse-widget-drag, touchend.discourse-widget-drag', () => cancelDrag()); + $(document).on('mouseup.discourse-widget-drag, touchend.discourse-widget-drag', e => cancelDrag(e)); $(document).on('click.discourse-widget', e => { nodeCallback(e.target, CLICK_ATTRIBUTE_NAME, w => w.click(e)); diff --git a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 index eaa6aac250d..9c49bc7b190 100644 --- a/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 +++ b/app/assets/javascripts/discourse/widgets/topic-timeline.js.es6 @@ -12,6 +12,15 @@ function clamp(p, min=0.0, max=1.0) { return Math.max(Math.min(p, max), min); } +function attachBackButton(widget) { + return widget.attach('button', { + className: 'btn btn-primary btn-small back-button', + label: 'topic.timeline.back', + title: 'topic.timeline.back_description', + action: 'goBack' + }); +} + createWidget('timeline-last-read', { tagName: 'div.timeline-last-read', @@ -22,20 +31,12 @@ createWidget('timeline-last-read', { html(attrs) { const result = [ iconNode('minus', { class: 'progress' }) ]; if (attrs.showButton) { - result.push(this.attach('button', { - className: 'btn btn-primary btn-small', - label: 'topic.timeline.back', - title: 'topic.timeline.back_description', - action: 'goBack' - })); + result.push(attachBackButton(this)); } return result; }, - goBack() { - this.sendWidgetAction('jumpToPost', this.attrs.lastRead); - } }); function timelineDate(date) { @@ -61,6 +62,9 @@ createWidget('timeline-scroller', { contents.push(h('div.timeline-ago', timelineDate(date))); } + if (attrs.showDockedButton) { + contents.push(attachBackButton(this)); + } let result = [ h('div.timeline-handle'), h('div.timeline-scroller-content', contents) ]; if (attrs.fullScreen) { @@ -74,8 +78,10 @@ createWidget('timeline-scroller', { this.sendWidgetAction('updatePercentage', e.pageY); }, - dragEnd() { - this.sendWidgetAction('commit'); + dragEnd(e) { + if (!$(e.target).is('button')) { + this.sendWidgetAction('commit'); + } } }); @@ -153,21 +159,35 @@ createWidget('timeline-scrollarea', { const before = SCROLLAREA_REMAINING * percentage; const after = (SCROLLAREA_HEIGHT - before) - SCROLLER_HEIGHT; + let showButton = false; + const hasBackPosition = + position.lastRead > 3 && + Math.abs(position.lastRead - position.current) > 3 && + (position.lastRead && position.lastRead !== position.total); + + if (hasBackPosition) { + const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT); + showButton = ((lastReadTop < before - (SCROLLER_HEIGHT * 0.5)) || + (lastReadTop > (before + SCROLLER_HEIGHT))) && + (lastReadTop < (SCROLLAREA_HEIGHT - SCROLLER_HEIGHT)); + } + const result = [ this.attach('timeline-padding', { height: before }), - this.attach('timeline-scroller', _.merge(position, {fullScreen: attrs.fullScreen})), + this.attach('timeline-scroller', _.merge(position, { + showDockedButton: hasBackPosition && !showButton, + fullScreen: attrs.fullScreen + })), this.attach('timeline-padding', { height: after }) ]; - if (position.lastRead && position.lastRead !== position.total) { + if (hasBackPosition) { const lastReadTop = Math.round(position.lastReadPercentage * SCROLLAREA_HEIGHT); - if (lastReadTop > (before + SCROLLER_HEIGHT * 0.5)) { - result.push(this.attach('timeline-last-read', { - top: lastReadTop, - lastRead: position.lastRead, - showButton: (lastReadTop > (before + SCROLLER_HEIGHT)) && (lastReadTop < (SCROLLAREA_HEIGHT - SCROLLER_HEIGHT)) - })); - } + result.push(this.attach('timeline-last-read', { + top: lastReadTop, + lastRead: position.lastRead, + showButton + })); } return result; @@ -196,6 +216,10 @@ createWidget('timeline-scrollarea', { _percentFor(topic, postIndex) { const total = topic.get('postStream.filteredPostsCount'); return clamp(parseFloat(postIndex - 1.0) / total); + }, + + goBack() { + this.sendWidgetAction('jumpToPost', this.position().lastRead); } }); diff --git a/app/assets/stylesheets/common/topic-timeline.scss b/app/assets/stylesheets/common/topic-timeline.scss index 47e9653d68f..ac1ed7431ef 100644 --- a/app/assets/stylesheets/common/topic-timeline.scss +++ b/app/assets/stylesheets/common/topic-timeline.scss @@ -253,6 +253,10 @@ cursor: ns-resize; display: flex; align-items: center; + + .back-button { + margin-top: 1em; + } } .timeline-replies { @@ -263,10 +267,6 @@ position: absolute; margin-left: -0.35em; - .btn-small { - padding: 2px 5px; - } - i.progress { font-size: 0.8em; color: $tertiary; @@ -274,6 +274,10 @@ } } + .back-button { + padding: 2px 5px; + } + .now-date { @include unselectable; display: inline-block;