diff --git a/client/components/App.vue b/client/components/App.vue index 58629f18..b58ee181 100644 --- a/client/components/App.vue +++ b/client/components/App.vue @@ -49,6 +49,7 @@ export default { return { notified: this.$store.state.isNotified, "menu-open": this.$store.state.sidebarOpen, + "menu-dragging": this.$store.state.sidebarDragging, "userlist-open": this.$store.state.userlistOpen, }; }, diff --git a/client/components/Sidebar.vue b/client/components/Sidebar.vue index 8337ca5a..57dd86ba 100644 --- a/client/components/Sidebar.vue +++ b/client/components/Sidebar.vue @@ -1,6 +1,6 @@ @@ -79,6 +79,106 @@ export default { activeChannel: Object, networks: Array, }, + mounted() { + this.touchStartPos = null; + this.touchCurPos = null; + this.touchStartTime = 0; + this.menuWidth = 0; + this.menuIsMoving = false; + this.menuIsAbsolute = false; + + this.onTouchStart = (e) => { + this.touchStartPos = this.touchCurPos = e.touches.item(0); + + if (e.touches.length !== 1) { + this.onTouchEnd(); + return; + } + + const styles = window.getComputedStyle(this.$refs.sidebar); + + this.menuWidth = parseFloat(styles.width); + this.menuIsAbsolute = styles.position === "absolute"; + + if (!this.$store.state.sidebarOpen || this.touchStartPos.screenX > this.menuWidth) { + this.touchStartTime = Date.now(); + + document.body.addEventListener("touchmove", this.onTouchMove, {passive: true}); + document.body.addEventListener("touchend", this.onTouchEnd, {passive: true}); + } + }; + + this.onTouchMove = (e) => { + const touch = (this.touchCurPos = e.touches.item(0)); + let distX = touch.screenX - this.touchStartPos.screenX; + const distY = touch.screenY - this.touchStartPos.screenY; + + if (!this.menuIsMoving) { + // tan(45°) is 1. Gestures in 0°-45° (< 1) are considered horizontal, so + // menu must be open; gestures in 45°-90° (>1) are considered vertical, so + // chat windows must be scrolled. + if (Math.abs(distY / distX) >= 1) { + this.onTouchEnd(); + return; + } + + const devicePixelRatio = window.devicePixelRatio || 2; + + if (Math.abs(distX) > devicePixelRatio) { + this.$store.commit("sidebarDragging", true); + this.menuIsMoving = true; + } + } + + // Do not animate the menu on desktop view + if (!this.menuIsAbsolute) { + return; + } + + if (this.$store.state.sidebarOpen) { + distX += this.menuWidth; + } + + if (distX > this.menuWidth) { + distX = this.menuWidth; + } else if (distX < 0) { + distX = 0; + } + + this.$refs.sidebar.style.transform = "translate3d(" + distX + "px, 0, 0)"; + this.$refs.overlay.style.opacity = distX / this.menuWidth; + }; + + this.onTouchEnd = () => { + const diff = this.touchCurPos.screenX - this.touchStartPos.screenX; + const absDiff = Math.abs(diff); + + if ( + absDiff > this.menuWidth / 2 || + (Date.now() - this.touchStartTime < 180 && absDiff > 50) + ) { + this.toggle(diff > 0); + } + + document.body.removeEventListener("touchmove", this.onTouchMove); + document.body.removeEventListener("touchend", this.onTouchEnd); + this.$store.commit("sidebarDragging", false); + + this.$refs.sidebar.style.transform = null; + this.$refs.overlay.style.opacity = null; + + this.touchStartPos = null; + this.touchCurPos = null; + this.touchStartTime = 0; + this.menuIsMoving = false; + }; + + this.toggle = (state) => { + this.$store.commit("sidebarOpen", state); + }; + + document.body.addEventListener("touchstart", this.onTouchStart, {passive: true}); + }, methods: { isPublic: () => document.body.classList.contains("public"), }, diff --git a/client/js/slideout.js b/client/js/slideout.js deleted file mode 100644 index 989061d2..00000000 --- a/client/js/slideout.js +++ /dev/null @@ -1,115 +0,0 @@ -"use strict"; - -class SlideoutMenu { - enable() { - this.viewport = document.getElementById("viewport"); - this.menu = document.getElementById("sidebar"); - this.sidebarOverlay = document.getElementById("sidebar-overlay"); - - this.touchStartPos = null; - this.touchCurPos = null; - this.touchStartTime = 0; - this.menuWidth = 0; - this.menuIsOpen = false; - this.menuIsMoving = false; - this.menuIsAbsolute = false; - - this.onTouchStart = (e) => { - this.touchStartPos = this.touchCurPos = e.touches.item(0); - - if (e.touches.length !== 1) { - this.onTouchEnd(); - return; - } - - const styles = window.getComputedStyle(this.menu); - - this.menuWidth = parseFloat(styles.width); - this.menuIsAbsolute = styles.position === "absolute"; - - if (!this.menuIsOpen || this.touchStartPos.screenX > this.menuWidth) { - this.touchStartTime = Date.now(); - - document.body.addEventListener("touchmove", this.onTouchMove, {passive: true}); - document.body.addEventListener("touchend", this.onTouchEnd, {passive: true}); - } - }; - - this.onTouchMove = (e) => { - const touch = (this.touchCurPos = e.touches.item(0)); - let distX = touch.screenX - this.touchStartPos.screenX; - const distY = touch.screenY - this.touchStartPos.screenY; - - if (!this.menuIsMoving) { - // tan(45°) is 1. Gestures in 0°-45° (< 1) are considered horizontal, so - // menu must be open; gestures in 45°-90° (>1) are considered vertical, so - // chat windows must be scrolled. - if (Math.abs(distY / distX) >= 1) { - this.onTouchEnd(); - return; - } - - const devicePixelRatio = window.devicePixelRatio || 2; - - if (Math.abs(distX) > devicePixelRatio) { - this.viewport.classList.toggle("menu-dragging", true); - this.menuIsMoving = true; - } - } - - // Do not animate the menu on desktop view - if (!this.menuIsAbsolute) { - return; - } - - if (this.menuIsOpen) { - distX += this.menuWidth; - } - - if (distX > this.menuWidth) { - distX = this.menuWidth; - } else if (distX < 0) { - distX = 0; - } - - this.menu.style.transform = "translate3d(" + distX + "px, 0, 0)"; - this.sidebarOverlay.style.opacity = distX / this.menuWidth; - }; - - this.onTouchEnd = () => { - const diff = this.touchCurPos.screenX - this.touchStartPos.screenX; - const absDiff = Math.abs(diff); - - if ( - absDiff > this.menuWidth / 2 || - (Date.now() - this.touchStartTime < 180 && absDiff > 50) - ) { - this.toggle(diff > 0); - } - - document.body.removeEventListener("touchmove", this.onTouchMove); - document.body.removeEventListener("touchend", this.onTouchEnd); - this.viewport.classList.toggle("menu-dragging", false); - this.menu.style.transform = null; - this.sidebarOverlay.style.opacity = null; - - this.touchStartPos = null; - this.touchCurPos = null; - this.touchStartTime = 0; - this.menuIsMoving = false; - }; - - document.body.addEventListener("touchstart", this.onTouchStart, {passive: true}); - } - - toggle(state) { - this.menuIsOpen = state; - this.viewport.classList.toggle("menu-open", state); - } - - isOpen() { - return this.menuIsOpen; - } -} - -module.exports = new SlideoutMenu(); diff --git a/client/js/store.js b/client/js/store.js index f334fa65..3f41862b 100644 --- a/client/js/store.js +++ b/client/js/store.js @@ -11,6 +11,7 @@ export default new Vuex.Store({ activeWindow: null, sessions: [], sidebarOpen: false, + sidebarDragging: false, userlistOpen: storage.get("thelounge.state.userlist") !== "false", }, mutations: { @@ -32,6 +33,9 @@ export default new Vuex.Store({ sidebarOpen(state, payload) { state.sidebarOpen = payload; }, + sidebarDragging(state, payload) { + state.sidebarDragging = payload; + }, userlistOpen(state, payload) { state.userlistOpen = payload; }, diff --git a/client/js/vue.js b/client/js/vue.js index cbd2cf33..c439a1a3 100644 --- a/client/js/vue.js +++ b/client/js/vue.js @@ -7,7 +7,6 @@ const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber"); const localetime = require("./libs/handlebars/localetime"); const friendlysize = require("./libs/handlebars/friendlysize"); const colorClass = require("./libs/handlebars/colorClass"); -const slideoutMenu = require("../js/slideout"); const storage = require("./localStorage"); Vue.filter("localetime", localetime); @@ -54,15 +53,11 @@ const vueApp = new Vue({ onSocketInit() { this.initialized = true; this.$store.commit("isConnected", true); - - // TODO: handle slideut in vue - slideoutMenu.enable(); }, setSidebar(state) { const utils = require("./utils"); this.$store.commit("sidebarOpen", state); - slideoutMenu.toggle(false); if (window.outerWidth > utils.mobileViewportPixels) { storage.set("thelounge.state.sidebar", state);