diff --git a/client/components/App.vue b/client/components/App.vue index ca5d990a..814afa08 100644 --- a/client/components/App.vue +++ b/client/components/App.vue @@ -2,7 +2,7 @@
{ diff --git a/client/components/ChannelWrapper.vue b/client/components/ChannelWrapper.vue index 3638da00..b4a3951b 100644 --- a/client/components/ChannelWrapper.vue +++ b/client/components/ChannelWrapper.vue @@ -1,4 +1,5 @@ diff --git a/client/components/SidebarToggle.vue b/client/components/SidebarToggle.vue new file mode 100644 index 00000000..60cddb60 --- /dev/null +++ b/client/components/SidebarToggle.vue @@ -0,0 +1,13 @@ + + + diff --git a/client/components/Windows/Changelog.vue b/client/components/Windows/Changelog.vue index 449b62e1..04d705ff 100644 --- a/client/components/Windows/Changelog.vue +++ b/client/components/Windows/Changelog.vue @@ -5,10 +5,7 @@ aria-label="Changelog" >
-
diff --git a/client/components/Windows/Settings.vue b/client/components/Windows/Settings.vue index 2c754340..b8a0283b 100644 --- a/client/components/Windows/Settings.vue +++ b/client/components/Windows/Settings.vue @@ -6,10 +6,7 @@ aria-label="Settings" >
-
{ require("./socket-events"); - const slideoutMenu = require("./slideout"); const contextMenuFactory = require("./contextMenuFactory"); - const storage = require("./localStorage"); const utils = require("./utils"); require("./webpush"); require("./keybinds"); @@ -20,40 +18,6 @@ window.vueMounted = () => { const sidebar = $("#sidebar, #footer"); const viewport = $("#viewport"); - function storeSidebarVisibility(name, state) { - storage.set(name, state); - - vueApp.$emit("resize"); - } - - // If sidebar overlay is visible and it is clicked, close the sidebar - $("#sidebar-overlay").on("click", () => { - slideoutMenu.toggle(false); - - if ($(window).outerWidth() > utils.mobileViewportPixels) { - storeSidebarVisibility("thelounge.state.sidebar", false); - } - }); - - $("#windows").on("click", "button.lt", () => { - const isOpen = !slideoutMenu.isOpen(); - - slideoutMenu.toggle(isOpen); - - if ($(window).outerWidth() > utils.mobileViewportPixels) { - storeSidebarVisibility("thelounge.state.sidebar", isOpen); - } - }); - - viewport.on("click", ".rt", function() { - const isOpen = !viewport.hasClass("userlist-open"); - - viewport.toggleClass("userlist-open", isOpen); - storeSidebarVisibility("thelounge.state.userlist", isOpen); - - return false; - }); - viewport.on("contextmenu", ".network .chan", function(e) { return contextMenuFactory.createContextMenu($(this), e).show(); }); @@ -138,8 +102,8 @@ window.vueMounted = () => { socket.emit("open", channel ? channel.channel.id : null); - if ($(window).outerWidth() <= utils.mobileViewportPixels) { - slideoutMenu.toggle(false); + if (!keepSidebarOpen && $(window).outerWidth() <= utils.mobileViewportPixels) { + vueApp.setSidebar(false); } } else { vueApp.activeChannel = null; diff --git a/client/js/slideout.js b/client/js/slideout.js index f285ac3b..ecab355b 100644 --- a/client/js/slideout.js +++ b/client/js/slideout.js @@ -1,112 +1,112 @@ "use strict"; -const viewport = document.getElementById("viewport"); -const menu = document.getElementById("sidebar"); -const sidebarOverlay = document.getElementById("sidebar-overlay"); - -let touchStartPos = null; -let touchCurPos = null; -let touchStartTime = 0; -let menuWidth = 0; -let menuIsOpen = false; -let menuIsMoving = false; -let menuIsAbsolute = false; - class SlideoutMenu { - static enable() { - document.body.addEventListener("touchstart", onTouchStart, {passive: true}); + 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}); } - static toggle(state) { - menuIsOpen = state; - viewport.classList.toggle("menu-open", state); + toggle(state) { + this.menuIsOpen = state; + this.viewport.classList.toggle("menu-open", state); } - static isOpen() { - return menuIsOpen; + isOpen() { + return this.menuIsOpen; } } -function onTouchStart(e) { - touchStartPos = touchCurPos = e.touches.item(0); - - if (e.touches.length !== 1) { - onTouchEnd(); - return; - } - - const styles = window.getComputedStyle(menu); - - menuWidth = parseFloat(styles.width); - menuIsAbsolute = styles.position === "absolute"; - - if (!menuIsOpen || touchStartPos.screenX > menuWidth) { - touchStartTime = Date.now(); - - document.body.addEventListener("touchmove", onTouchMove, {passive: true}); - document.body.addEventListener("touchend", onTouchEnd, {passive: true}); - } -} - -function onTouchMove(e) { - const touch = (touchCurPos = e.touches.item(0)); - let distX = touch.screenX - touchStartPos.screenX; - const distY = touch.screenY - touchStartPos.screenY; - - if (!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) { - onTouchEnd(); - return; - } - - const devicePixelRatio = window.devicePixelRatio || 2; - - if (Math.abs(distX) > devicePixelRatio) { - viewport.classList.toggle("menu-dragging", true); - menuIsMoving = true; - } - } - - // Do not animate the menu on desktop view - if (!menuIsAbsolute) { - return; - } - - if (menuIsOpen) { - distX += menuWidth; - } - - if (distX > menuWidth) { - distX = menuWidth; - } else if (distX < 0) { - distX = 0; - } - - menu.style.transform = "translate3d(" + distX + "px, 0, 0)"; - sidebarOverlay.style.opacity = distX / menuWidth; -} - -function onTouchEnd() { - const diff = touchCurPos.screenX - touchStartPos.screenX; - const absDiff = Math.abs(diff); - - if (absDiff > menuWidth / 2 || (Date.now() - touchStartTime < 180 && absDiff > 50)) { - SlideoutMenu.toggle(diff > 0); - } - - document.body.removeEventListener("touchmove", onTouchMove); - document.body.removeEventListener("touchend", onTouchEnd); - viewport.classList.toggle("menu-dragging", false); - menu.style.transform = null; - sidebarOverlay.style.opacity = null; - - touchStartPos = null; - touchCurPos = null; - touchStartTime = 0; - menuIsMoving = false; -} - -module.exports = SlideoutMenu; +module.exports = (new SlideoutMenu); diff --git a/client/js/socket-events/init.js b/client/js/socket-events/init.js index ac90434f..40d1de23 100644 --- a/client/js/socket-events/init.js +++ b/client/js/socket-events/init.js @@ -4,7 +4,6 @@ const $ = require("jquery"); const escape = require("css.escape"); const socket = require("../socket"); const webpush = require("../webpush"); -const slideoutMenu = require("../slideout"); const sidebar = $("#sidebar"); const storage = require("../localStorage"); const utils = require("../utils"); @@ -21,7 +20,7 @@ socket.on("init", function(data) { vueApp.currentUserVisibleError = null; if (!vueApp.initialized) { - vueApp.initialized = true; + vueApp.onSocketInit(); if (data.token) { storage.set("token", data.token); @@ -29,14 +28,11 @@ socket.on("init", function(data) { webpush.configurePushNotifications(data.pushSubscription, data.applicationServerKey); - slideoutMenu.enable(); - - const viewport = $("#viewport"); - const viewportWidth = $(window).outerWidth(); + const viewportWidth = window.outerWidth; let isUserlistOpen = storage.get("thelounge.state.userlist"); if (viewportWidth > utils.mobileViewportPixels) { - slideoutMenu.toggle(storage.get("thelounge.state.sidebar") !== "false"); + vueApp.setSidebar(storage.get("thelounge.state.sidebar") !== "false"); } // If The Lounge is opened on a small screen (less than 1024px), and we don't have stored @@ -45,7 +41,7 @@ socket.on("init", function(data) { isUserlistOpen = "true"; } - viewport.toggleClass("userlist-open", isUserlistOpen === "true"); + vueApp.setUserlist(isUserlistOpen === "true"); $(document.body).removeClass("signed-out"); $("#loading").remove(); diff --git a/client/js/store.js b/client/js/store.js index bab7f57d..f334fa65 100644 --- a/client/js/store.js +++ b/client/js/store.js @@ -1,5 +1,6 @@ import Vue from "vue"; import Vuex from "vuex"; +const storage = require("./localStorage"); Vue.use(Vuex); @@ -9,6 +10,8 @@ export default new Vuex.Store({ isNotified: false, activeWindow: null, sessions: [], + sidebarOpen: false, + userlistOpen: storage.get("thelounge.state.userlist") !== "false", }, mutations: { isConnected(state, payload) { @@ -26,6 +29,12 @@ export default new Vuex.Store({ sessions(state, payload) { state.sessions = payload; }, + sidebarOpen(state, payload) { + state.sidebarOpen = payload; + }, + userlistOpen(state, payload) { + state.userlistOpen = payload; + }, }, getters: { currentSession: (state) => state.sessions.find((item) => item.current), diff --git a/client/js/vue.js b/client/js/vue.js index 2bec7e5e..cbd2cf33 100644 --- a/client/js/vue.js +++ b/client/js/vue.js @@ -7,6 +7,8 @@ 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); Vue.filter("friendlysize", friendlysize); @@ -48,6 +50,38 @@ const vueApp = new Vue({ mounted() { Vue.nextTick(() => window.vueMounted()); }, + methods: { + 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); + } + + this.$emit("resize"); + }, + toggleSidebar() { + this.setSidebar(!this.$store.state.sidebarOpen); + }, + setUserlist(state) { + storage.set("thelounge.state.userlist", state); + this.$store.commit("userlistOpen", state); + this.$emit("resize"); + }, + toggleUserlist() { + this.setUserlist(!this.$store.state.userlistOpen); + }, + }, render(createElement) { return createElement(App, { ref: "app",