From 43d86d7d5d3f3b34b0500f6d5d3afe7bd86b0060 Mon Sep 17 00:00:00 2001 From: Haocen Xu Date: Fri, 6 Jul 2018 12:48:46 -0400 Subject: Hotfix for mobile device --- client/components/boards/boardBody.js | 7 ++++- client/components/cards/checklists.js | 7 ++++- client/components/lists/list.js | 7 ++++- client/components/main/header.styl | 2 +- client/components/mixins/perfectScrollbar.js | 18 ++++++----- client/components/swimlanes/swimlanes.js | 7 ++++- client/lib/utils.js | 46 ++++++++++++++++++++++++++++ 7 files changed, 82 insertions(+), 12 deletions(-) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 68ac8b27..6ff40ca4 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -1,5 +1,5 @@ const subManager = new SubsManager(); -const { calculateIndex } = Utils; +const { calculateIndex, enableClickOnTouch } = Utils; BlazeComponent.extendComponent({ onCreated() { @@ -74,6 +74,11 @@ BlazeComponent.extendComponent({ }, }); + // ugly touch event hotfix + $('.js-swimlane:not(.placeholder)').each(function() { + enableClickOnTouch(this); + }); + function userIsMember() { return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); } diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js index 519af629..7fc54f9e 100644 --- a/client/components/cards/checklists.js +++ b/client/components/cards/checklists.js @@ -1,4 +1,4 @@ -const { calculateIndexData } = Utils; +const { calculateIndexData, enableClickOnTouch } = Utils; function initSorting(items) { items.sortable({ @@ -36,6 +36,11 @@ function initSorting(items) { checklistItem.move(checklistId, sortIndex.base); }, }); + + // ugly touch event hotfix + $('.js-checklist-item:not(.placeholder)').each(function() { + enableClickOnTouch(this); + }); } BlazeComponent.extendComponent({ diff --git a/client/components/lists/list.js b/client/components/lists/list.js index 38a87674..3f7c6dea 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -1,4 +1,4 @@ -const { calculateIndex } = Utils; +const { calculateIndex, enableClickOnTouch } = Utils; BlazeComponent.extendComponent({ // Proxy @@ -83,6 +83,11 @@ BlazeComponent.extendComponent({ }, }); + // ugly touch event hotfix + $(itemsSelector).each(function() { + enableClickOnTouch(this); + }); + // Disable drag-dropping if the current user is not a board member or is comment only this.autorun(() => { $cards.sortable('option', 'disabled', !userIsMember()); diff --git a/client/components/main/header.styl b/client/components/main/header.styl index f9455f8e..495716e1 100644 --- a/client/components/main/header.styl +++ b/client/components/main/header.styl @@ -218,7 +218,7 @@ position: absolute right: 0px padding: 10px - margin: -10px + margin: -10px 0 -10px -10px .announcement, .offline-warning diff --git a/client/components/mixins/perfectScrollbar.js b/client/components/mixins/perfectScrollbar.js index f652f043..12f8a892 100644 --- a/client/components/mixins/perfectScrollbar.js +++ b/client/components/mixins/perfectScrollbar.js @@ -1,12 +1,16 @@ +const { isTouchDevice } = Utils; + Mixins.PerfectScrollbar = BlazeComponent.extendComponent({ onRendered() { - const component = this.mixinParent(); - const domElement = component.find('.js-perfect-scrollbar'); - Ps.initialize(domElement); + if (!isTouchDevice()) { + const component = this.mixinParent(); + const domElement = component.find('.js-perfect-scrollbar'); + Ps.initialize(domElement); - // XXX We should create an event map to be consistent with other components - // but since BlazeComponent doesn't merge Mixins events transparently I - // prefered to use a jQuery event (which is what an event map ends up doing) - component.$(domElement).on('mouseenter', () => Ps.update(domElement)); + // XXX We should create an event map to be consistent with other components + // but since BlazeComponent doesn't merge Mixins events transparently I + // prefered to use a jQuery event (which is what an event map ends up doing) + component.$(domElement).on('mouseenter', () => Ps.update(domElement)); + } }, }); diff --git a/client/components/swimlanes/swimlanes.js b/client/components/swimlanes/swimlanes.js index c67fe6af..2acf4a82 100644 --- a/client/components/swimlanes/swimlanes.js +++ b/client/components/swimlanes/swimlanes.js @@ -1,4 +1,4 @@ -const { calculateIndex } = Utils; +const { calculateIndex, enableClickOnTouch } = Utils; function currentCardIsInThisList(listId, swimlaneId) { const currentCard = Cards.findOne(Session.get('currentCard')); @@ -66,6 +66,11 @@ function initSortable(boardComponent, $listsDom) { }, }); + // ugly touch event hotfix + $('.js-list:not(.js-list-composer)').each(function() { + enableClickOnTouch(this); + }); + function userIsMember() { return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); } diff --git a/client/lib/utils.js b/client/lib/utils.js index 1f44c60d..7e2651d2 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -95,6 +95,52 @@ Utils = { increment, }; }, + + // Detect touch device + isTouchDevice() { + const isTouchable = (() => { + const prefixes = ' -webkit- -moz- -o- -ms- '.split(' '); + const mq = function(query) { + return window.matchMedia(query).matches; + }; + + if (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) { + return true; + } + + // include the 'heartz' as a way to have a non matching MQ to help terminate the join + // https://git.io/vznFH + const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''); + return mq(query); + })(); + return isTouchable; + }, + + calculateTouchDistance(touchA, touchB) { + return Math.sqrt( + Math.pow(touchA.screenX - touchB.screenX, 2) + + Math.pow(touchA.screenY - touchB.screenY, 2) + ); + }, + + enableClickOnTouch(element) { + let touchStart = null; + let lastTouch = null; + element.addEventListener('touchstart', function(e) { + touchStart = e.touches[0]; + }, false); + element.addEventListener('touchmove', function(e) { + const touches = e.touches; + lastTouch = touches[touches.length - 1]; + }, true); + element.addEventListener('touchend', function() { + if (touchStart && lastTouch && Utils.calculateTouchDistance(touchStart, lastTouch) <= 20) { + const clickEvent = document.createEvent('MouseEvents'); + clickEvent.initEvent('click', true, true); + this.dispatchEvent(clickEvent); + } + }, false); + }, }; // A simple tracker dependency that we invalidate every time the window is -- cgit v1.2.3-1-g7c22 From 616dade81c25b10fc409aee1bcc9a93ddbfee81b Mon Sep 17 00:00:00 2001 From: Haocen Xu Date: Fri, 6 Jul 2018 14:42:36 -0400 Subject: Hotfix more sortable elements --- client/components/boards/boardBody.js | 4 +--- client/components/cards/cardDetails.js | 6 ++++++ client/components/cards/checklists.js | 4 +--- client/components/lists/list.js | 4 +--- client/components/swimlanes/swimlanes.js | 4 +--- client/lib/utils.js | 21 +++++++++++---------- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/client/components/boards/boardBody.js b/client/components/boards/boardBody.js index 6ff40ca4..b68c9b12 100644 --- a/client/components/boards/boardBody.js +++ b/client/components/boards/boardBody.js @@ -75,9 +75,7 @@ BlazeComponent.extendComponent({ }); // ugly touch event hotfix - $('.js-swimlane:not(.placeholder)').each(function() { - enableClickOnTouch(this); - }); + enableClickOnTouch('.js-swimlane:not(.placeholder)'); function userIsMember() { return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 5fee1680..1583a51f 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -132,6 +132,9 @@ BlazeComponent.extendComponent({ }, }); + // ugly touch event hotfix + enableClickOnTouch('.card-checklist-items .js-checklist'); + const $subtasksDom = this.$('.card-subtasks-items'); $subtasksDom.sortable({ @@ -167,6 +170,9 @@ BlazeComponent.extendComponent({ }, }); + // ugly touch event hotfix + enableClickOnTouch('.card-subtasks-items .js-subtasks'); + function userIsMember() { return Meteor.user() && Meteor.user().isBoardMember(); } diff --git a/client/components/cards/checklists.js b/client/components/cards/checklists.js index 7fc54f9e..e014abba 100644 --- a/client/components/cards/checklists.js +++ b/client/components/cards/checklists.js @@ -38,9 +38,7 @@ function initSorting(items) { }); // ugly touch event hotfix - $('.js-checklist-item:not(.placeholder)').each(function() { - enableClickOnTouch(this); - }); + enableClickOnTouch('.js-checklist-item:not(.placeholder)'); } BlazeComponent.extendComponent({ diff --git a/client/components/lists/list.js b/client/components/lists/list.js index 3f7c6dea..267af31c 100644 --- a/client/components/lists/list.js +++ b/client/components/lists/list.js @@ -84,9 +84,7 @@ BlazeComponent.extendComponent({ }); // ugly touch event hotfix - $(itemsSelector).each(function() { - enableClickOnTouch(this); - }); + enableClickOnTouch(itemsSelector); // Disable drag-dropping if the current user is not a board member or is comment only this.autorun(() => { diff --git a/client/components/swimlanes/swimlanes.js b/client/components/swimlanes/swimlanes.js index 2acf4a82..865895a9 100644 --- a/client/components/swimlanes/swimlanes.js +++ b/client/components/swimlanes/swimlanes.js @@ -67,9 +67,7 @@ function initSortable(boardComponent, $listsDom) { }); // ugly touch event hotfix - $('.js-list:not(.js-list-composer)').each(function() { - enableClickOnTouch(this); - }); + enableClickOnTouch('.js-list:not(.js-list-composer)'); function userIsMember() { return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly(); diff --git a/client/lib/utils.js b/client/lib/utils.js index 7e2651d2..b70faec6 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -123,23 +123,24 @@ Utils = { ); }, - enableClickOnTouch(element) { + enableClickOnTouch(selector) { let touchStart = null; let lastTouch = null; - element.addEventListener('touchstart', function(e) { - touchStart = e.touches[0]; - }, false); - element.addEventListener('touchmove', function(e) { - const touches = e.touches; + + $(document).on('touchstart', selector, function(e) { + touchStart = e.originalEvent.touches[0]; + }); + $(document).on('touchmove', selector, function(e) { + const touches = e.originalEvent.touches; lastTouch = touches[touches.length - 1]; - }, true); - element.addEventListener('touchend', function() { + }); + $(document).on('touchend', selector, function(e) { if (touchStart && lastTouch && Utils.calculateTouchDistance(touchStart, lastTouch) <= 20) { const clickEvent = document.createEvent('MouseEvents'); clickEvent.initEvent('click', true, true); - this.dispatchEvent(clickEvent); + e.target.dispatchEvent(clickEvent); } - }, false); + }); }, }; -- cgit v1.2.3-1-g7c22 From 9c204d9bbe4845bc3e352e839615dfb782a753f4 Mon Sep 17 00:00:00 2001 From: Haocen Xu Date: Fri, 6 Jul 2018 15:25:26 -0400 Subject: Avoid default behavior --- client/lib/utils.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/lib/utils.js b/client/lib/utils.js index b70faec6..71e2f1f1 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -136,6 +136,7 @@ Utils = { }); $(document).on('touchend', selector, function(e) { if (touchStart && lastTouch && Utils.calculateTouchDistance(touchStart, lastTouch) <= 20) { + e.preventDefault(); const clickEvent = document.createEvent('MouseEvents'); clickEvent.initEvent('click', true, true); e.target.dispatchEvent(clickEvent); -- cgit v1.2.3-1-g7c22 From 5c774070617357c25c7bb35b43f4b122eb4b3e34 Mon Sep 17 00:00:00 2001 From: Haocen Xu Date: Fri, 6 Jul 2018 20:15:39 -0400 Subject: Fix missing utility function. --- client/components/cards/cardDetails.js | 2 +- client/lib/utils.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/client/components/cards/cardDetails.js b/client/components/cards/cardDetails.js index 1583a51f..b41bfc17 100644 --- a/client/components/cards/cardDetails.js +++ b/client/components/cards/cardDetails.js @@ -1,5 +1,5 @@ const subManager = new SubsManager(); -const { calculateIndexData } = Utils; +const { calculateIndexData, enableClickOnTouch } = Utils; BlazeComponent.extendComponent({ mixins() { diff --git a/client/lib/utils.js b/client/lib/utils.js index 71e2f1f1..6b8e3524 100644 --- a/client/lib/utils.js +++ b/client/lib/utils.js @@ -113,6 +113,7 @@ Utils = { const query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''); return mq(query); })(); + Utils.isTouchDevice = () => isTouchable; return isTouchable; }, -- cgit v1.2.3-1-g7c22