mirror of
https://github.com/thelounge/thelounge
synced 2024-11-22 12:03:11 +00:00
Implement vue-router.
This commit is contained in:
parent
7355c91839
commit
737afc759b
19 changed files with 221 additions and 230 deletions
|
@ -3,12 +3,7 @@
|
||||||
<Sidebar :networks="networks" :active-channel="activeChannel" :overlay="$refs.overlay" />
|
<Sidebar :networks="networks" :active-channel="activeChannel" :overlay="$refs.overlay" />
|
||||||
<div id="sidebar-overlay" ref="overlay" @click="$root.setSidebar(false)" />
|
<div id="sidebar-overlay" ref="overlay" @click="$root.setSidebar(false)" />
|
||||||
<article id="windows">
|
<article id="windows">
|
||||||
<Chat
|
<router-view></router-view>
|
||||||
v-if="activeChannel"
|
|
||||||
:network="activeChannel.network"
|
|
||||||
:channel="activeChannel.channel"
|
|
||||||
/>
|
|
||||||
<component :is="$store.state.activeWindow" ref="window" />
|
|
||||||
</article>
|
</article>
|
||||||
<ImageViewer ref="imageViewer" />
|
<ImageViewer ref="imageViewer" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -18,29 +13,13 @@
|
||||||
const throttle = require("lodash/throttle");
|
const throttle = require("lodash/throttle");
|
||||||
|
|
||||||
import Sidebar from "./Sidebar.vue";
|
import Sidebar from "./Sidebar.vue";
|
||||||
import NetworkList from "./NetworkList.vue";
|
|
||||||
import Chat from "./Chat.vue";
|
|
||||||
import ImageViewer from "./ImageViewer.vue";
|
import ImageViewer from "./ImageViewer.vue";
|
||||||
import SignIn from "./Windows/SignIn.vue";
|
|
||||||
import Settings from "./Windows/Settings.vue";
|
|
||||||
import NetworkEdit from "./Windows/NetworkEdit.vue";
|
|
||||||
import Connect from "./Windows/Connect.vue";
|
|
||||||
import Help from "./Windows/Help.vue";
|
|
||||||
import Changelog from "./Windows/Changelog.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "App",
|
name: "App",
|
||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
NetworkList,
|
|
||||||
ImageViewer,
|
ImageViewer,
|
||||||
Chat,
|
|
||||||
SignIn,
|
|
||||||
Settings,
|
|
||||||
NetworkEdit,
|
|
||||||
Connect,
|
|
||||||
Help,
|
|
||||||
Changelog,
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
activeWindow: String,
|
activeWindow: String,
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
:aria-selected="activeChannel && channel === activeChannel.channel"
|
:aria-selected="activeChannel && channel === activeChannel.channel"
|
||||||
:style="closed ? {transition: 'none', opacity: 0.4} : null"
|
:style="closed ? {transition: 'none', opacity: 0.4} : null"
|
||||||
role="tab"
|
role="tab"
|
||||||
|
@click="click"
|
||||||
>
|
>
|
||||||
<slot :network="network" :channel="channel" :activeChannel="activeChannel" />
|
<slot :network="network" :channel="channel" :activeChannel="activeChannel" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -80,6 +81,11 @@ export default {
|
||||||
|
|
||||||
return this.channel.name;
|
return this.channel.name;
|
||||||
},
|
},
|
||||||
|
click() {
|
||||||
|
// TODO: Find out why this sometimes throws `uncaught exception: Object`
|
||||||
|
this.$router.push("chan-" + this.channel.id);
|
||||||
|
this.$root.closeSidebarIfNeeded();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -133,7 +133,38 @@ export default {
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
channel(_, previousChannel) {
|
||||||
|
this.channelChanged(previousChannel);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.channelChanged();
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
channelChanged(previousChannel) {
|
||||||
|
// Triggered when active channel is set or changed
|
||||||
|
|
||||||
|
if (previousChannel) {
|
||||||
|
this.$root.switchOutOfChannel(previousChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.channel.highlight = 0;
|
||||||
|
this.channel.unread = 0;
|
||||||
|
|
||||||
|
this.$store.commit("activeWindow", null);
|
||||||
|
socket.emit("open", this.channel.id);
|
||||||
|
|
||||||
|
if (this.channel.usersOutdated) {
|
||||||
|
this.channel.usersOutdated = false;
|
||||||
|
|
||||||
|
socket.emit("names", {
|
||||||
|
target: this.channel.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$root.synchronizeNotifiedState();
|
||||||
|
},
|
||||||
hideUserVisibleError() {
|
hideUserVisibleError() {
|
||||||
this.$root.currentUserVisibleError = null;
|
this.$root.currentUserVisibleError = null;
|
||||||
},
|
},
|
||||||
|
|
35
client/components/RoutedChat.vue
Normal file
35
client/components/RoutedChat.vue
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<template>
|
||||||
|
<Chat v-if="activeChannel" :network="activeChannel.network" :channel="activeChannel.channel" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Temporary component for routing channels and lobbies
|
||||||
|
import Chat from "./Chat.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "RoutedChat",
|
||||||
|
components: {
|
||||||
|
Chat,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
activeChannel() {
|
||||||
|
const chan_id = parseInt(this.$route.params.pathMatch);
|
||||||
|
const channel = this.$root.findChannel(chan_id);
|
||||||
|
return channel;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
activeChannel() {
|
||||||
|
this.setActiveChannel();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.setActiveChannel();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setActiveChannel() {
|
||||||
|
this.$root.activeChannel = this.activeChannel;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -25,6 +25,7 @@
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="sign-in"
|
aria-controls="sign-in"
|
||||||
:aria-selected="$store.state.activeWindow === 'SignIn'"
|
:aria-selected="$store.state.activeWindow === 'SignIn'"
|
||||||
|
@click="navigate('sign-in')"
|
||||||
/></span>
|
/></span>
|
||||||
<span
|
<span
|
||||||
class="tooltipped tooltipped-n tooltipped-no-touch"
|
class="tooltipped tooltipped-n tooltipped-no-touch"
|
||||||
|
@ -37,6 +38,7 @@
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="connect"
|
aria-controls="connect"
|
||||||
:aria-selected="$store.state.activeWindow === 'Connect'"
|
:aria-selected="$store.state.activeWindow === 'Connect'"
|
||||||
|
@click="navigate('connect')"
|
||||||
/></span>
|
/></span>
|
||||||
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Settings"
|
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Settings"
|
||||||
><button
|
><button
|
||||||
|
@ -52,6 +54,7 @@
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="settings"
|
aria-controls="settings"
|
||||||
:aria-selected="$store.state.activeWindow === 'Settings'"
|
:aria-selected="$store.state.activeWindow === 'Settings'"
|
||||||
|
@click="navigate('settings')"
|
||||||
/></span>
|
/></span>
|
||||||
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Help"
|
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Help"
|
||||||
><button
|
><button
|
||||||
|
@ -62,6 +65,7 @@
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="help"
|
aria-controls="help"
|
||||||
:aria-selected="$store.state.activeWindow === 'Help'"
|
:aria-selected="$store.state.activeWindow === 'Help'"
|
||||||
|
@click="navigate('help')"
|
||||||
/></span>
|
/></span>
|
||||||
</footer>
|
</footer>
|
||||||
</aside>
|
</aside>
|
||||||
|
@ -178,6 +182,16 @@ export default {
|
||||||
this.$store.commit("sidebarOpen", state);
|
this.$store.commit("sidebarOpen", state);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.navigate = (to) => {
|
||||||
|
if (this.activeChannel && this.activeChannel.channel) {
|
||||||
|
this.$root.switchOutOfChannel(this.activeChannel.channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$root.activeChannel = null;
|
||||||
|
this.$root.closeSidebarIfNeeded();
|
||||||
|
this.$router.push(to);
|
||||||
|
};
|
||||||
|
|
||||||
document.body.addEventListener("touchstart", this.onTouchStart, {passive: true});
|
document.body.addEventListener("touchstart", this.onTouchStart, {passive: true});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<SidebarToggle />
|
<SidebarToggle />
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a id="back-to-help" href="#" data-target="#help" data-component="Help">« Help</a>
|
<router-link id="back-to-help" to="help">« Help</router-link>
|
||||||
|
|
||||||
<template
|
<template
|
||||||
v-if="
|
v-if="
|
||||||
|
@ -65,6 +65,10 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
patchChangelog() {
|
patchChangelog() {
|
||||||
|
if (!this.$refs.changelog) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const links = this.$refs.changelog.querySelectorAll("a");
|
const links = this.$refs.changelog.querySelectorAll("a");
|
||||||
|
|
||||||
for (const link of links) {
|
for (const link of links) {
|
||||||
|
|
|
@ -8,12 +8,10 @@
|
||||||
|
|
||||||
<h2>
|
<h2>
|
||||||
<small class="pull-right">
|
<small class="pull-right">
|
||||||
v{{ $root.serverConfiguration.version }} (<a
|
v{{ $root.serverConfiguration.version }} (<router-link
|
||||||
id="view-changelog"
|
id="view-changelog"
|
||||||
href="#"
|
to="changelog"
|
||||||
data-target="#changelog"
|
>release notes</router-link
|
||||||
data-component="Changelog"
|
|
||||||
>release notes</a
|
|
||||||
>)
|
>)
|
||||||
</small>
|
</small>
|
||||||
About The Lounge
|
About The Lounge
|
||||||
|
|
|
@ -36,4 +36,6 @@ module.exports = {
|
||||||
condensedTypesQuery,
|
condensedTypesQuery,
|
||||||
timeFormats,
|
timeFormats,
|
||||||
sizeUnits,
|
sizeUnits,
|
||||||
|
// Same value as media query in CSS that forces sidebars to become overlays
|
||||||
|
mobileViewportPixels: 768,
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,135 +6,12 @@ const $ = require("jquery");
|
||||||
// our libraries
|
// our libraries
|
||||||
const socket = require("./socket");
|
const socket = require("./socket");
|
||||||
|
|
||||||
const {vueApp, findChannel} = require("./vue");
|
|
||||||
|
|
||||||
window.vueMounted = () => {
|
window.vueMounted = () => {
|
||||||
require("./socket-events");
|
require("./socket-events");
|
||||||
require("./contextMenuFactory");
|
require("./contextMenuFactory");
|
||||||
const utils = require("./utils");
|
|
||||||
require("./webpush");
|
require("./webpush");
|
||||||
require("./keybinds");
|
require("./keybinds");
|
||||||
|
|
||||||
const sidebar = $("#sidebar, #footer");
|
|
||||||
|
|
||||||
const openWindow = function openWindow(e, {pushState, replaceHistory} = {}) {
|
|
||||||
const self = $(this);
|
|
||||||
const target = self.attr("data-target");
|
|
||||||
|
|
||||||
if (!target) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is a rather gross hack to account for sources that are in the
|
|
||||||
// sidebar specifically. Needs to be done better when window management gets
|
|
||||||
// refactored.
|
|
||||||
const inSidebar = self.parents("#sidebar, #footer").length > 0;
|
|
||||||
const channel = inSidebar ? findChannel(Number(self.attr("data-id"))) : null;
|
|
||||||
|
|
||||||
if (vueApp.activeChannel) {
|
|
||||||
const {channel: lastChannel} = vueApp.activeChannel;
|
|
||||||
|
|
||||||
// If user clicks on the currently active channel, do nothing
|
|
||||||
if (channel && lastChannel === channel.channel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastChannel.messages.length > 0) {
|
|
||||||
lastChannel.firstUnread = lastChannel.messages[lastChannel.messages.length - 1].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastChannel.messages.length > 100) {
|
|
||||||
lastChannel.messages.splice(0, lastChannel.messages.length - 100);
|
|
||||||
lastChannel.moreHistoryAvailable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (channel) {
|
|
||||||
vueApp.$store.commit("activeWindow", null);
|
|
||||||
vueApp.activeChannel = channel;
|
|
||||||
|
|
||||||
if (channel) {
|
|
||||||
channel.channel.highlight = 0;
|
|
||||||
channel.channel.unread = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.emit("open", channel ? channel.channel.id : null);
|
|
||||||
|
|
||||||
if ($(window).outerWidth() <= utils.mobileViewportPixels) {
|
|
||||||
vueApp.setSidebar(false);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
vueApp.activeChannel = null;
|
|
||||||
const component = self.attr("data-component");
|
|
||||||
vueApp.$store.commit("activeWindow", component);
|
|
||||||
|
|
||||||
if ($(window).outerWidth() <= utils.mobileViewportPixels) {
|
|
||||||
vueApp.setSidebar(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.synchronizeNotifiedState();
|
|
||||||
|
|
||||||
if (self.hasClass("chan")) {
|
|
||||||
vueApp.$nextTick(() => $("#chat-container").addClass("active"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO: move to ChatInput.vue
|
|
||||||
const chanChat = chan.find(".chat");
|
|
||||||
|
|
||||||
if (chanChat.length > 0 && channel.type !== "special") {
|
|
||||||
// On touch devices unfocus (blur) the input to correctly close the virtual keyboard
|
|
||||||
// An explicit blur is required, as the keyboard may open back up if the focus remains
|
|
||||||
// See https://github.com/thelounge/thelounge/issues/2257
|
|
||||||
$("#input").trigger("ontouchstart" in window ? "blur" : "focus");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (channel && channel.channel.usersOutdated) {
|
|
||||||
channel.channel.usersOutdated = false;
|
|
||||||
|
|
||||||
socket.emit("names", {
|
|
||||||
target: channel.channel.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pushes states to history web API when clicking elements with a data-target attribute.
|
|
||||||
// States are very trivial and only contain a single `clickTarget` property which
|
|
||||||
// contains a CSS selector that targets elements which takes the user to a different view
|
|
||||||
// when clicked. The `popstate` event listener will trigger synthetic click events using that
|
|
||||||
// selector and thus take the user to a different view/state.
|
|
||||||
if (pushState === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = {};
|
|
||||||
|
|
||||||
if (self.prop("id")) {
|
|
||||||
state.clickTarget = `#${self.prop("id")}`;
|
|
||||||
} else if (self.hasClass("chan")) {
|
|
||||||
state.clickTarget = `#sidebar .chan[data-id="${self.attr("data-id")}"]`;
|
|
||||||
} else {
|
|
||||||
state.clickTarget = `#footer button[data-target="${target}"]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (history && history.pushState) {
|
|
||||||
if (replaceHistory && history.replaceState) {
|
|
||||||
history.replaceState(state, null, target);
|
|
||||||
} else {
|
|
||||||
history.pushState(state, null, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
sidebar.on("click", ".chan, button", openWindow);
|
|
||||||
$("#windows").on("click", "#view-changelog, #back-to-help", openWindow);
|
|
||||||
|
|
||||||
$(document).on("visibilitychange focus click", () => {
|
|
||||||
utils.synchronizeNotifiedState();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("popstate", (e) => {
|
window.addEventListener("popstate", (e) => {
|
||||||
const {state} = e;
|
const {state} = e;
|
||||||
|
|
||||||
|
|
25
client/js/router.js
Normal file
25
client/js/router.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const Vue = require("vue").default;
|
||||||
|
const VueRouter = require("vue-router").default;
|
||||||
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
const SignIn = require("../components/Windows/SignIn.vue").default;
|
||||||
|
const Connect = require("../components/Windows/Connect.vue").default;
|
||||||
|
const Settings = require("../components/Windows/Settings.vue").default;
|
||||||
|
const Help = require("../components/Windows/Help.vue").default;
|
||||||
|
const Changelog = require("../components/Windows/Changelog.vue").default;
|
||||||
|
const RoutedChat = require("../components/RoutedChat.vue").default;
|
||||||
|
|
||||||
|
const router = new VueRouter({
|
||||||
|
routes: [
|
||||||
|
{path: "/sign-in", component: SignIn},
|
||||||
|
{path: "/connect", component: Connect},
|
||||||
|
{path: "/settings", component: Settings},
|
||||||
|
{path: "/help", component: Help},
|
||||||
|
{path: "/changelog", component: Changelog},
|
||||||
|
{path: "/chan-*", component: RoutedChat},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
|
@ -19,8 +19,6 @@ socket.on("auth", function(data) {
|
||||||
|
|
||||||
if (data.serverHash > -1) {
|
if (data.serverHash > -1) {
|
||||||
utils.serverHash = data.serverHash;
|
utils.serverHash = data.serverHash;
|
||||||
|
|
||||||
vueApp.$store.commit("activeWindow", "SignIn");
|
|
||||||
} else {
|
} else {
|
||||||
getActiveWindowComponent().inFlight = false;
|
getActiveWindowComponent().inFlight = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ const webpush = require("../webpush");
|
||||||
const sidebar = $("#sidebar");
|
const sidebar = $("#sidebar");
|
||||||
const storage = require("../localStorage");
|
const storage = require("../localStorage");
|
||||||
const utils = require("../utils");
|
const utils = require("../utils");
|
||||||
|
const constants = require("../constants");
|
||||||
const {vueApp, initChannel} = require("../vue");
|
const {vueApp, initChannel} = require("../vue");
|
||||||
|
|
||||||
socket.on("init", function(data) {
|
socket.on("init", function(data) {
|
||||||
|
@ -31,7 +32,7 @@ socket.on("init", function(data) {
|
||||||
const viewportWidth = window.outerWidth;
|
const viewportWidth = window.outerWidth;
|
||||||
let isUserlistOpen = storage.get("thelounge.state.userlist");
|
let isUserlistOpen = storage.get("thelounge.state.userlist");
|
||||||
|
|
||||||
if (viewportWidth > utils.mobileViewportPixels) {
|
if (viewportWidth > constants.mobileViewportPixels) {
|
||||||
vueApp.setSidebar(storage.get("thelounge.state.sidebar") !== "false");
|
vueApp.setSidebar(storage.get("thelounge.state.sidebar") !== "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ socket.on("init", function(data) {
|
||||||
vueApp.$nextTick(() => openCorrectChannel(previousActive, data.active));
|
vueApp.$nextTick(() => openCorrectChannel(previousActive, data.active));
|
||||||
|
|
||||||
utils.confirmExit();
|
utils.confirmExit();
|
||||||
utils.synchronizeNotifiedState();
|
vueApp.synchronizeNotifiedState();
|
||||||
});
|
});
|
||||||
|
|
||||||
function openCorrectChannel(clientActive, serverActive) {
|
function openCorrectChannel(clientActive, serverActive) {
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const utils = require("../utils");
|
|
||||||
const options = require("../options");
|
const options = require("../options");
|
||||||
const cleanIrcMessage = require("../libs/handlebars/ircmessageparser/cleanIrcMessage");
|
const cleanIrcMessage = require("../libs/handlebars/ircmessageparser/cleanIrcMessage");
|
||||||
const webpush = require("../webpush");
|
const webpush = require("../webpush");
|
||||||
|
@ -91,7 +90,7 @@ socket.on("msg", function(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.msg.self || data.msg.highlight) {
|
if (data.msg.self || data.msg.highlight) {
|
||||||
utils.synchronizeNotifiedState();
|
vueApp.synchronizeNotifiedState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const utils = require("../utils");
|
|
||||||
const {vueApp, findChannel} = require("../vue");
|
const {vueApp, findChannel} = require("../vue");
|
||||||
|
|
||||||
// Sync unread badge and marker when other clients open a channel
|
// Sync unread badge and marker when other clients open a channel
|
||||||
|
@ -28,5 +27,5 @@ socket.on("open", function(id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.synchronizeNotifiedState();
|
vueApp.synchronizeNotifiedState();
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
const $ = require("jquery");
|
const $ = require("jquery");
|
||||||
const socket = require("../socket");
|
const socket = require("../socket");
|
||||||
const utils = require("../utils");
|
|
||||||
const {vueApp, findChannel} = require("../vue");
|
const {vueApp, findChannel} = require("../vue");
|
||||||
|
|
||||||
socket.on("part", function(data) {
|
socket.on("part", function(data) {
|
||||||
|
@ -23,5 +22,5 @@ socket.on("part", function(data) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.synchronizeNotifiedState();
|
vueApp.synchronizeNotifiedState();
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,15 +7,12 @@ const {vueApp} = require("./vue");
|
||||||
var serverHash = -1; // eslint-disable-line no-var
|
var serverHash = -1; // eslint-disable-line no-var
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
// Same value as media query in CSS that forces sidebars to become overlays
|
|
||||||
mobileViewportPixels: 768,
|
|
||||||
findCurrentNetworkChan,
|
findCurrentNetworkChan,
|
||||||
serverHash,
|
serverHash,
|
||||||
confirmExit,
|
confirmExit,
|
||||||
scrollIntoViewNicely,
|
scrollIntoViewNicely,
|
||||||
hasRoleInChannel,
|
hasRoleInChannel,
|
||||||
move,
|
move,
|
||||||
synchronizeNotifiedState,
|
|
||||||
requestIdleCallback,
|
requestIdleCallback,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,60 +42,6 @@ function scrollIntoViewNicely(el) {
|
||||||
el.scrollIntoView({block: "center", inline: "nearest"});
|
el.scrollIntoView({block: "center", inline: "nearest"});
|
||||||
}
|
}
|
||||||
|
|
||||||
const favicon = $("#favicon");
|
|
||||||
|
|
||||||
function synchronizeNotifiedState() {
|
|
||||||
updateTitle();
|
|
||||||
|
|
||||||
let hasAnyHighlights = false;
|
|
||||||
|
|
||||||
for (const network of vueApp.networks) {
|
|
||||||
for (const chan of network.channels) {
|
|
||||||
if (chan.highlight > 0) {
|
|
||||||
hasAnyHighlights = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleNotificationMarkers(hasAnyHighlights);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleNotificationMarkers(newState) {
|
|
||||||
if (vueApp.$store.state.isNotified !== newState) {
|
|
||||||
// Toggles a dot on the menu icon when there are unread notifications
|
|
||||||
vueApp.$store.commit("isNotified", newState);
|
|
||||||
|
|
||||||
// Toggles the favicon to red when there are unread notifications
|
|
||||||
const old = favicon.prop("href");
|
|
||||||
favicon.prop("href", favicon.data("other"));
|
|
||||||
favicon.data("other", old);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTitle() {
|
|
||||||
let title = vueApp.appName;
|
|
||||||
|
|
||||||
if (vueApp.activeChannel) {
|
|
||||||
title = `${vueApp.activeChannel.channel.name} — ${title}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add highlight count to title
|
|
||||||
let alertEventCount = 0;
|
|
||||||
|
|
||||||
for (const network of vueApp.networks) {
|
|
||||||
for (const channel of network.channels) {
|
|
||||||
alertEventCount += channel.highlight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alertEventCount > 0) {
|
|
||||||
title = `(${alertEventCount}) ${title}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmExit() {
|
function confirmExit() {
|
||||||
if ($(document.body).hasClass("public")) {
|
if ($(document.body).hasClass("public")) {
|
||||||
window.onbeforeunload = function() {
|
window.onbeforeunload = function() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const Vue = require("vue").default;
|
const Vue = require("vue").default;
|
||||||
|
|
||||||
const store = require("./store").default;
|
const store = require("./store").default;
|
||||||
const App = require("../components/App.vue").default;
|
const App = require("../components/App.vue").default;
|
||||||
const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
|
const roundBadgeNumber = require("./libs/handlebars/roundBadgeNumber");
|
||||||
|
@ -8,6 +9,8 @@ const localetime = require("./libs/handlebars/localetime");
|
||||||
const friendlysize = require("./libs/handlebars/friendlysize");
|
const friendlysize = require("./libs/handlebars/friendlysize");
|
||||||
const colorClass = require("./libs/handlebars/colorClass");
|
const colorClass = require("./libs/handlebars/colorClass");
|
||||||
const storage = require("./localStorage");
|
const storage = require("./localStorage");
|
||||||
|
const router = require("./router").default;
|
||||||
|
const constants = require("./constants");
|
||||||
|
|
||||||
Vue.filter("localetime", localetime);
|
Vue.filter("localetime", localetime);
|
||||||
Vue.filter("friendlysize", friendlysize);
|
Vue.filter("friendlysize", friendlysize);
|
||||||
|
@ -46,12 +49,17 @@ const vueApp = new Vue({
|
||||||
userStyles: "",
|
userStyles: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
router,
|
||||||
mounted() {
|
mounted() {
|
||||||
Vue.nextTick(() => window.vueMounted());
|
Vue.nextTick(() => window.vueMounted());
|
||||||
|
|
||||||
if (navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)) {
|
if (navigator.platform.match(/(Mac|iPhone|iPod|iPad)/i)) {
|
||||||
document.body.classList.add("is-apple");
|
document.body.classList.add("is-apple");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.addEventListener("visibilitychange", this.synchronizeNotifiedState());
|
||||||
|
document.addEventListener("focus", this.synchronizeNotifiedState());
|
||||||
|
document.addEventListener("click", this.synchronizeNotifiedState());
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onSocketInit() {
|
onSocketInit() {
|
||||||
|
@ -59,11 +67,9 @@ const vueApp = new Vue({
|
||||||
this.$store.commit("isConnected", true);
|
this.$store.commit("isConnected", true);
|
||||||
},
|
},
|
||||||
setSidebar(state) {
|
setSidebar(state) {
|
||||||
const utils = require("./utils");
|
|
||||||
|
|
||||||
this.$store.commit("sidebarOpen", state);
|
this.$store.commit("sidebarOpen", state);
|
||||||
|
|
||||||
if (window.outerWidth > utils.mobileViewportPixels) {
|
if (window.outerWidth > constants.mobileViewportPixels) {
|
||||||
storage.set("thelounge.state.sidebar", state);
|
storage.set("thelounge.state.sidebar", state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +78,11 @@ const vueApp = new Vue({
|
||||||
toggleSidebar() {
|
toggleSidebar() {
|
||||||
this.setSidebar(!this.$store.state.sidebarOpen);
|
this.setSidebar(!this.$store.state.sidebarOpen);
|
||||||
},
|
},
|
||||||
|
closeSidebarIfNeeded() {
|
||||||
|
if (window.innerWidth <= constants.mobileViewportPixels) {
|
||||||
|
this.setSidebar(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
setUserlist(state) {
|
setUserlist(state) {
|
||||||
storage.set("thelounge.state.userlist", state);
|
storage.set("thelounge.state.userlist", state);
|
||||||
this.$store.commit("userlistOpen", state);
|
this.$store.commit("userlistOpen", state);
|
||||||
|
@ -80,6 +91,78 @@ const vueApp = new Vue({
|
||||||
toggleUserlist() {
|
toggleUserlist() {
|
||||||
this.setUserlist(!this.$store.state.userlistOpen);
|
this.setUserlist(!this.$store.state.userlistOpen);
|
||||||
},
|
},
|
||||||
|
findChannel(id) {
|
||||||
|
for (const network of this.networks) {
|
||||||
|
for (const channel of network.channels) {
|
||||||
|
if (channel.id === id) {
|
||||||
|
return {network, channel};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
switchOutOfChannel(channel) {
|
||||||
|
// When switching out of a channel, mark everything as read
|
||||||
|
if (channel.messages.length > 0) {
|
||||||
|
channel.firstUnread = channel.messages[channel.messages.length - 1].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel.messages.length > 100) {
|
||||||
|
channel.messages.splice(0, channel.messages.length - 100);
|
||||||
|
channel.moreHistoryAvailable = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
synchronizeNotifiedState() {
|
||||||
|
this.updateTitle();
|
||||||
|
|
||||||
|
let hasAnyHighlights = false;
|
||||||
|
|
||||||
|
for (const network of this.networks) {
|
||||||
|
for (const chan of network.channels) {
|
||||||
|
if (chan.highlight > 0) {
|
||||||
|
hasAnyHighlights = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.toggleNotificationMarkers(hasAnyHighlights);
|
||||||
|
},
|
||||||
|
updateTitle() {
|
||||||
|
let title = this.appName;
|
||||||
|
|
||||||
|
if (this.activeChannel) {
|
||||||
|
title = `${this.activeChannel.channel.name} — ${title}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add highlight count to title
|
||||||
|
let alertEventCount = 0;
|
||||||
|
|
||||||
|
for (const network of this.networks) {
|
||||||
|
for (const channel of network.channels) {
|
||||||
|
alertEventCount += channel.highlight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alertEventCount > 0) {
|
||||||
|
title = `(${alertEventCount}) ${title}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.title = title;
|
||||||
|
},
|
||||||
|
toggleNotificationMarkers(newState) {
|
||||||
|
if (this.$store.state.isNotified !== newState) {
|
||||||
|
// Toggles a dot on the menu icon when there are unread notifications
|
||||||
|
this.$store.commit("isNotified", newState);
|
||||||
|
|
||||||
|
// Toggles the favicon to red when there are unread notifications
|
||||||
|
const favicon = document.getElementById("favicon");
|
||||||
|
const old = favicon.getAttribute("href");
|
||||||
|
favicon.setAttribute("href", favicon.dataset.other);
|
||||||
|
favicon.dataset.other = old;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
render(createElement) {
|
render(createElement) {
|
||||||
return createElement(App, {
|
return createElement(App, {
|
||||||
|
@ -96,15 +179,7 @@ Vue.config.errorHandler = function(e) {
|
||||||
};
|
};
|
||||||
|
|
||||||
function findChannel(id) {
|
function findChannel(id) {
|
||||||
for (const network of vueApp.networks) {
|
return vueApp.findChannel(id);
|
||||||
for (const channel of network.channels) {
|
|
||||||
if (channel.id === id) {
|
|
||||||
return {network, channel};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initChannel(channel) {
|
function initChannel(channel) {
|
||||||
|
|
|
@ -114,6 +114,7 @@
|
||||||
"undate": "0.3.0",
|
"undate": "0.3.0",
|
||||||
"vue": "2.6.10",
|
"vue": "2.6.10",
|
||||||
"vue-loader": "15.7.2",
|
"vue-loader": "15.7.2",
|
||||||
|
"vue-router": "3.1.3",
|
||||||
"vue-server-renderer": "2.6.10",
|
"vue-server-renderer": "2.6.10",
|
||||||
"vue-template-compiler": "2.6.10",
|
"vue-template-compiler": "2.6.10",
|
||||||
"vuedraggable": "2.23.2",
|
"vuedraggable": "2.23.2",
|
||||||
|
|
|
@ -9078,6 +9078,11 @@ vue-loader@15.7.2:
|
||||||
vue-hot-reload-api "^2.3.0"
|
vue-hot-reload-api "^2.3.0"
|
||||||
vue-style-loader "^4.1.0"
|
vue-style-loader "^4.1.0"
|
||||||
|
|
||||||
|
vue-router@3.1.3:
|
||||||
|
version "3.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.3.tgz#e6b14fabc0c0ee9fda0e2cbbda74b350e28e412b"
|
||||||
|
integrity sha512-8iSa4mGNXBjyuSZFCCO4fiKfvzqk+mhL0lnKuGcQtO1eoj8nq3CmbEG8FwK5QqoqwDgsjsf1GDuisDX4cdb/aQ==
|
||||||
|
|
||||||
vue-server-renderer@2.6.10:
|
vue-server-renderer@2.6.10:
|
||||||
version "2.6.10"
|
version "2.6.10"
|
||||||
resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz#cb2558842ead360ae2ec1f3719b75564a805b375"
|
resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.6.10.tgz#cb2558842ead360ae2ec1f3719b75564a805b375"
|
||||||
|
|
Loading…
Reference in a new issue