Merge pull request #4332 from itsjohncs/android-context-menu
Enable Android's context menus in network list.
This commit is contained in:
commit
a8d438261a
@ -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,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -2,6 +2,7 @@
|
||||
<div
|
||||
v-if="isOpen"
|
||||
id="context-menu-container"
|
||||
:class="{passthrough}"
|
||||
@click="containerClick"
|
||||
@contextmenu.prevent="containerClick"
|
||||
@keydown.exact.up.prevent="navigateMenu(-1)"
|
||||
@ -49,6 +50,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
isOpen: false,
|
||||
passthrough: false,
|
||||
previousActiveElement: null,
|
||||
items: [],
|
||||
activeItem: -1,
|
||||
@ -60,18 +62,35 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
eventbus.on("escapekey", this.close);
|
||||
eventbus.on("contextmenu:cancel", this.close);
|
||||
eventbus.on("contextmenu:user", this.openUserContextMenu);
|
||||
eventbus.on("contextmenu:channel", this.openChannelContextMenu);
|
||||
},
|
||||
destroyed() {
|
||||
eventbus.off("escapekey", this.close);
|
||||
eventbus.off("contextmenu:cancel", this.close);
|
||||
eventbus.off("contextmenu:user", this.openUserContextMenu);
|
||||
eventbus.off("contextmenu:channel", this.openChannelContextMenu);
|
||||
|
||||
this.close();
|
||||
},
|
||||
methods: {
|
||||
enablePointerEvents() {
|
||||
this.passthrough = false;
|
||||
document.body.removeEventListener("pointerup", this.enablePointerEvents, {
|
||||
passive: true,
|
||||
});
|
||||
},
|
||||
openChannelContextMenu(data) {
|
||||
if (data.event.type === "contextmenu") {
|
||||
// Pass through all pointer events to allow the network list's
|
||||
// dragging events to continue triggering.
|
||||
this.passthrough = true;
|
||||
document.body.addEventListener("pointerup", this.enablePointerEvents, {
|
||||
passive: true,
|
||||
});
|
||||
}
|
||||
|
||||
const items = generateChannelContextMenu(this.$root, data.channel, data.network);
|
||||
this.open(data.event, items);
|
||||
},
|
||||
|
@ -82,6 +82,7 @@
|
||||
role="region"
|
||||
aria-live="polite"
|
||||
@touchstart="onDraggableTouchStart"
|
||||
@touchmove="onDraggableTouchMove"
|
||||
@touchend="onDraggableTouchEnd"
|
||||
@touchcancel="onDraggableTouchEnd"
|
||||
>
|
||||
@ -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");
|
||||
|
@ -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 {
|
||||
|
5
client/js/helpers/distance.js
Normal file
5
client/js/helpers/distance.js
Normal file
@ -0,0 +1,5 @@
|
||||
function distance([x1, y1], [x2, y2]) {
|
||||
return Math.hypot(x1 - x2, y1 - y2);
|
||||
}
|
||||
|
||||
export default distance;
|
@ -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);
|
||||
|
@ -127,6 +127,6 @@
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"sortablejs": "1.14.0"
|
||||
"sortablejs": "git+https://github.com/itsjohncs/Sortable.git"
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user