From 393d4fe591a0b8825db7ef8e0425fcd2775f7efd Mon Sep 17 00:00:00 2001 From: itsjohncs Date: Wed, 13 Oct 2021 00:55:28 -0700 Subject: [PATCH] Enable Android's context menus in network list. After #4326 Android users could no longer long-touch to bring up the context menu for channels in the network list. Now they can again. --- client/components/ChannelWrapper.vue | 14 ++++------ client/components/ContextMenu.vue | 19 ++++++++++++++ client/components/NetworkList.vue | 26 ++++++++++++++++++- client/css/style.css | 8 ++++++ client/js/helpers/distance.js | 5 ++++ client/js/helpers/listenForTwoFingerSwipes.js | 6 ++--- 6 files changed, 64 insertions(+), 14 deletions(-) create mode 100644 client/js/helpers/distance.js diff --git a/client/components/ChannelWrapper.vue b/client/components/ChannelWrapper.vue index aa3e507a..62dcb297 100644 --- a/client/components/ChannelWrapper.vue +++ b/client/components/ChannelWrapper.vue @@ -82,15 +82,11 @@ export default { this.$root.switchToChannel(this.channel); }, openContextMenu(event) { - // events.buttons will be 0 when the event is caused by a long - // touch on Android. - if (event.buttons !== 0) { - eventbus.emit("contextmenu:channel", { - event: event, - channel: this.channel, - network: this.network, - }); - } + eventbus.emit("contextmenu:channel", { + event: event, + channel: this.channel, + network: this.network, + }); }, }, }; diff --git a/client/components/ContextMenu.vue b/client/components/ContextMenu.vue index bab07a43..041d5b19 100644 --- a/client/components/ContextMenu.vue +++ b/client/components/ContextMenu.vue @@ -2,6 +2,7 @@
@@ -205,6 +206,8 @@ import JoinChannel from "./JoinChannel.vue"; import socket from "../js/socket"; import collapseNetwork from "../js/helpers/collapseNetwork"; import isIgnoredKeybind from "../js/helpers/isIgnoredKeybind"; +import distance from "../js/helpers/distance"; +import eventbus from "../js/eventbus"; export default { name: "NetworkList", @@ -325,16 +328,25 @@ export default { ); }, onDraggableChoose(event) { - if (this.isTouchEvent(event.originalEvent)) { + const original = event.originalEvent; + + if (this.isTouchEvent(original)) { // onDrag is only triggered when the user actually moves the // dragged object but onChoose is triggered as soon as the // item is eligible for dragging. This gives us an opportunity // to tell the user they've held the touch long enough. event.item.classList.add("ui-sortable-dragging-touch-cue"); + + if (original instanceof TouchEvent && original.touches.length > 0) { + this.startDrag = [original.touches[0].clientX, original.touches[0].clientY]; + } else if (original instanceof PointerEvent) { + this.startDrag = [original.clientX, original.clientY]; + } } }, onDraggableUnchoose(event) { event.item.classList.remove("ui-sortable-dragging-touch-cue"); + this.startDrag = null; }, onDraggableTouchStart() { if (event.touches.length === 1) { @@ -343,6 +355,18 @@ export default { document.body.classList.add("force-no-select"); } }, + onDraggableTouchMove(event) { + if (this.startDrag && event.touches.length > 0) { + const touch = event.touches[0]; + const currentPosition = [touch.clientX, touch.clientY]; + + if (distance(this.startDrag, currentPosition) > 10) { + // Context menu is shown on Android after long touch. + // Dismiss it now that we're sure the user is dragging. + eventbus.emit("contextmenu:cancel"); + } + } + }, onDraggableTouchEnd(event) { if (event.touches.length === 0) { document.body.classList.remove("force-no-select"); diff --git a/client/css/style.css b/client/css/style.css index eb0caa99..a9c4edd5 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -2252,6 +2252,14 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ background: transparent; } +#context-menu-container.passthrough { + pointer-events: none; +} + +#context-menu-container.passthrough > * { + pointer-events: auto; +} + .mentions-popup, #context-menu, .textcomplete-menu { diff --git a/client/js/helpers/distance.js b/client/js/helpers/distance.js new file mode 100644 index 00000000..4a9c01df --- /dev/null +++ b/client/js/helpers/distance.js @@ -0,0 +1,5 @@ +function distance([x1, y1], [x2, y2]) { + return Math.hypot(x1 - x2, y1 - y2); +} + +export default distance; diff --git a/client/js/helpers/listenForTwoFingerSwipes.js b/client/js/helpers/listenForTwoFingerSwipes.js index 7be48e87..8689f083 100644 --- a/client/js/helpers/listenForTwoFingerSwipes.js +++ b/client/js/helpers/listenForTwoFingerSwipes.js @@ -1,5 +1,7 @@ "use strict"; +import distance from "./distance"; + // onTwoFingerSwipe will be called with a cardinal direction ("n", "e", "s" or // "w") as its only argument. function listenForTwoFingerSwipes(onTwoFingerSwipe) { @@ -89,10 +91,6 @@ function getSwipe(hist) { return getCardinalDirection(hist[0].center, hist[hist.length - 1].center); } -function distance([x1, y1], [x2, y2]) { - return Math.hypot(x1 - x2, y1 - y2); -} - function getCardinalDirection([x1, y1], [x2, y2]) { // If θ is the angle of the vector then this is tan(θ) const tangent = (y2 - y1) / (x2 - x1);