From ebe39b26dc0f78593a81ea3d8b04ff0bf970d3c6 Mon Sep 17 00:00:00 2001
From: itsjohncs
Date: Wed, 6 Oct 2021 22:59:19 -0700
Subject: [PATCH 1/2] Two-finger swipe now switches windows (#3901)
The Alt+Up and Alt+Down keybindings on Desktop did not have an
equivalent for Mobile users. Now a two-finger swipe left on a
touchscreen is equivalent to Alt+Up (similarly swipe right is
equivalent to Alt+Down).
---
client/js/helpers/listenForTwoFingerSwipes.js | 108 ++++++++++++++++++
client/js/keybinds.js | 20 +++-
2 files changed, 123 insertions(+), 5 deletions(-)
create mode 100644 client/js/helpers/listenForTwoFingerSwipes.js
diff --git a/client/js/helpers/listenForTwoFingerSwipes.js b/client/js/helpers/listenForTwoFingerSwipes.js
new file mode 100644
index 00000000..7be48e87
--- /dev/null
+++ b/client/js/helpers/listenForTwoFingerSwipes.js
@@ -0,0 +1,108 @@
+"use strict";
+
+// onTwoFingerSwipe will be called with a cardinal direction ("n", "e", "s" or
+// "w") as its only argument.
+function listenForTwoFingerSwipes(onTwoFingerSwipe) {
+ let history = [];
+
+ document.body.addEventListener(
+ "touchmove",
+ function (event) {
+ if (event.touches.length !== 2) {
+ return;
+ }
+
+ const a = event.touches.item(0);
+ const b = event.touches.item(1);
+
+ const timestamp = window.performance.now();
+ const center = [(a.screenX + b.screenX) / 2, (a.screenY + b.screenY) / 2];
+
+ if (history.length > 0) {
+ const last = history[history.length - 1];
+ const centersAreEqual =
+ last.center[0] === center[0] && last.center[1] === center[1];
+
+ if (last.timestamp === timestamp || centersAreEqual) {
+ // Touches with the same timestamps or center don't help us
+ // see the speed of movement. Ignore them.
+ return;
+ }
+ }
+
+ history.push({timestamp, center});
+ },
+ {passive: true}
+ );
+
+ document.body.addEventListener(
+ "touchend",
+ function () {
+ if (event.touches.length >= 2) {
+ return;
+ }
+
+ try {
+ const direction = getSwipe(history);
+
+ if (direction) {
+ onTwoFingerSwipe(direction);
+ }
+ } finally {
+ history = [];
+ }
+ },
+ {passive: true}
+ );
+
+ document.body.addEventListener(
+ "touchcancel",
+ function () {
+ history = [];
+ },
+ {passive: true}
+ );
+}
+
+// Returns the cardinal direction of the swipe or null if there is no swipe.
+function getSwipe(hist) {
+ // Speed is in pixels/millisecond. Must be maintained throughout swipe.
+ const MIN_SWIPE_SPEED = 0.2;
+
+ if (hist.length < 2) {
+ return null;
+ }
+
+ for (let i = 1; i < hist.length; ++i) {
+ const previous = hist[i - 1];
+ const current = hist[i];
+
+ const speed =
+ distance(previous.center, current.center) /
+ Math.abs(previous.timestamp - current.timestamp);
+
+ if (speed < MIN_SWIPE_SPEED) {
+ return null;
+ }
+ }
+
+ 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);
+
+ // All values of |tan(-45° to 45°)| are less than 1, same for 145° to 225°
+ if (Math.abs(tangent) < 1) {
+ return x1 < x2 ? "e" : "w";
+ }
+
+ return y1 < y2 ? "s" : "n";
+}
+
+export default listenForTwoFingerSwipes;
diff --git a/client/js/keybinds.js b/client/js/keybinds.js
index daee796f..04e31514 100644
--- a/client/js/keybinds.js
+++ b/client/js/keybinds.js
@@ -6,6 +6,7 @@ import store from "./store";
import {switchToChannel} from "./router";
import isChannelCollapsed from "./helpers/isChannelCollapsed";
import isIgnoredKeybind from "./helpers/isIgnoredKeybind";
+import listenForTwoFingerSwipes from "./helpers/listenForTwoFingerSwipes";
// Switch to the next/previous window in the channel list.
Mousetrap.bind(["alt+up", "alt+down"], function (e, keys) {
@@ -13,11 +14,22 @@ Mousetrap.bind(["alt+up", "alt+down"], function (e, keys) {
return true;
}
+ navigateWindow(keys.split("+").pop() === "up" ? -1 : 1);
+
+ return false;
+});
+
+listenForTwoFingerSwipes(function (cardinalDirection) {
+ if (cardinalDirection === "e" || cardinalDirection === "w") {
+ navigateWindow(cardinalDirection === "e" ? -1 : 1);
+ }
+});
+
+function navigateWindow(direction) {
if (store.state.networks.length === 0) {
- return false;
+ return;
}
- const direction = keys.split("+").pop() === "up" ? -1 : 1;
const flatChannels = [];
let index = -1;
@@ -44,9 +56,7 @@ Mousetrap.bind(["alt+up", "alt+down"], function (e, keys) {
index = (((index + direction) % length) + length) % length;
jumpToChannel(flatChannels[index]);
-
- return false;
-});
+}
// Switch to the next/previous lobby in the channel list
Mousetrap.bind(["alt+shift+up", "alt+shift+down"], function (e, keys) {
From 91a0815bb5f8be8e34dd6e8c09d431ceb47f82e2 Mon Sep 17 00:00:00 2001
From: itsjohncs
Date: Thu, 7 Oct 2021 13:36:56 -0700
Subject: [PATCH 2/2] Add Gestures section to help window.
This documents the two gestures that The Lounge currently supports.
The section is only visible if your device supports touch.
---
client/components/Windows/Help.vue | 31 ++++++++++++++++++++++++++++++
client/css/style.css | 4 ++++
2 files changed, 35 insertions(+)
diff --git a/client/components/Windows/Help.vue b/client/components/Windows/Help.vue
index 95067dff..fd1408d6 100644
--- a/client/components/Windows/Help.vue
+++ b/client/components/Windows/Help.vue
@@ -87,6 +87,36 @@
+ Gestures
+
+
+
Single-Finger Swipe Left
+
+
+
+
+
Single-Finger Swipe Right
+
+
+
+
+
Two-Finger Swipe Left
+
+
Switch to the next window in the channel list.
+
+
+
+
+
Two-Finger Swipe Right
+
+
Switch to the previous window in the channel list.
+
+
+
Keyboard Shortcuts
@@ -764,6 +794,7 @@ export default {
data() {
return {
isApple: navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i) || false,
+ isTouch: navigator.maxTouchPoints > 0,
};
},
};
diff --git a/client/css/style.css b/client/css/style.css
index 30bceaaa..44c4d3dd 100644
--- a/client/css/style.css
+++ b/client/css/style.css
@@ -2024,6 +2024,10 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
padding-right: 15px;
}
+#help .help-item .subject.gesture {
+ font-weight: bold;
+}
+
#help .help-item .description p {
margin-bottom: 0;
}