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); diff --git a/package.json b/package.json index 923d8c21..93d4e3d0 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,6 @@ } }, "resolutions": { - "sortablejs": "1.14.0" + "sortablejs": "git+https://github.com/itsjohncs/Sortable.git" } } diff --git a/yarn.lock b/yarn.lock index b839365d..c40f9247 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7527,10 +7527,9 @@ socks@^2.6.1: ip "^1.1.5" smart-buffer "^4.1.0" -sortablejs@1.10.2, sortablejs@1.14.0: +sortablejs@1.10.2, "sortablejs@git+https://github.com/itsjohncs/Sortable.git": version "1.14.0" - resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8" - integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w== + resolved "git+https://github.com/itsjohncs/Sortable.git#21053e18ea6501e2aac8cac9029872115ab82844" source-list-map@^2.0.0, source-list-map@^2.0.1: version "2.0.1"