Improve router experience

This commit is contained in:
Pavel Djundik 2019-11-11 21:18:55 +02:00
parent 5a0f1c1f4e
commit f2309c7c89
18 changed files with 60 additions and 84 deletions

View file

@ -134,25 +134,19 @@ export default {
}, },
}, },
watch: { watch: {
channel(_, previousChannel) { channel() {
this.channelChanged(previousChannel); this.channelChanged();
}, },
}, },
mounted() { mounted() {
this.channelChanged(); this.channelChanged();
}, },
methods: { methods: {
channelChanged(previousChannel) { channelChanged() {
// Triggered when active channel is set or changed // Triggered when active channel is set or changed
if (previousChannel) {
this.$root.switchOutOfChannel(previousChannel);
}
this.channel.highlight = 0; this.channel.highlight = 0;
this.channel.unread = 0; this.channel.unread = 0;
this.$store.commit("activeWindow", null);
socket.emit("open", this.channel.id); socket.emit("open", this.channel.id);
if (this.channel.usersOutdated) { if (this.channel.usersOutdated) {

View file

@ -13,8 +13,8 @@ export default {
}, },
computed: { computed: {
activeChannel() { activeChannel() {
const chan_id = parseInt(this.$route.params.pathMatch); const chanId = parseInt(this.$route.params.id, 10);
const channel = this.$store.getters.findChannel(chan_id); const channel = this.$store.getters.findChannel(chanId);
return channel; return channel;
}, },
}, },

View file

@ -22,12 +22,10 @@
tag="button" tag="button"
active-class="active" active-class="active"
:class="['icon', 'sign-in']" :class="['icon', 'sign-in']"
data-target="#sign-in"
data-component="SignIn"
aria-label="Sign in" aria-label="Sign in"
role="tab" role="tab"
aria-controls="sign-in" aria-controls="sign-in"
:aria-selected="$store.state.activeWindow === 'SignIn'" :aria-selected="$route.name === 'SignIn'"
/></span> /></span>
<span <span
class="tooltipped tooltipped-n tooltipped-no-touch" class="tooltipped tooltipped-n tooltipped-no-touch"
@ -37,12 +35,10 @@
tag="button" tag="button"
active-class="active" active-class="active"
:class="['icon', 'connect']" :class="['icon', 'connect']"
data-target="#connect"
data-component="Connect"
aria-label="Connect to network" aria-label="Connect to network"
role="tab" role="tab"
aria-controls="connect" aria-controls="connect"
:aria-selected="$store.state.activeWindow === 'Connect'" :aria-selected="$route.name === '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"
><router-link ><router-link
@ -50,13 +46,10 @@
tag="button" tag="button"
active-class="active" active-class="active"
:class="['icon', 'settings']" :class="['icon', 'settings']"
class="icon settings"
data-target="#settings"
data-component="Settings"
aria-label="Settings" aria-label="Settings"
role="tab" role="tab"
aria-controls="settings" aria-controls="settings"
:aria-selected="$store.state.activeWindow === 'Settings'" :aria-selected="$route.name === '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"
><router-link ><router-link
@ -64,12 +57,10 @@
tag="button" tag="button"
active-class="active" active-class="active"
:class="['icon', 'help']" :class="['icon', 'help']"
data-target="#help"
data-component="Help"
aria-label="Help" aria-label="Help"
role="tab" role="tab"
aria-controls="help" aria-controls="help"
:aria-selected="$store.state.activeWindow === 'Help'" :aria-selected="$route.name === 'Help'"
/></span> /></span>
</footer> </footer>
</aside> </aside>

View file

@ -2,10 +2,9 @@
const socket = require("../socket"); const socket = require("../socket");
const store = require("../store").default; const store = require("../store").default;
const {switchToChannel} = require("../router");
exports.input = function(args) { exports.input = function(args) {
const {vueApp} = require("../vue");
if (args.length > 0) { if (args.length > 0) {
let channels = args[0]; let channels = args[0];
@ -26,7 +25,7 @@ exports.input = function(args) {
const chan = store.getters.findChannelOnCurrentNetwork(channels); const chan = store.getters.findChannelOnCurrentNetwork(channels);
if (chan) { if (chan) {
vueApp.switchToChannel(chan); switchToChannel(chan);
} else { } else {
socket.emit("input", { socket.emit("input", {
text: `/join ${channels} ${args.length > 1 ? args[1] : ""}`, text: `/join ${channels} ${args.length > 1 ? args[1] : ""}`,

View file

@ -7,6 +7,7 @@ const ContextMenu = require("./contextMenu");
const contextMenuActions = []; const contextMenuActions = [];
const contextMenuItems = []; const contextMenuItems = [];
const {vueApp} = require("./vue"); const {vueApp} = require("./vue");
const {switchToChannel, navigate} = require("./router");
const store = require("./store").default; const store = require("./store").default;
addDefaultItems(); addDefaultItems();
@ -51,7 +52,7 @@ function addWhoisItem() {
const chan = store.getters.findChannelOnCurrentNetwork(itemData); const chan = store.getters.findChannelOnCurrentNetwork(itemData);
if (chan) { if (chan) {
vueApp.switchToChannel(chan); switchToChannel(chan);
} }
socket.emit("input", { socket.emit("input", {
@ -86,7 +87,7 @@ function addQueryItem() {
const chan = store.getters.findChannelOnCurrentNetwork(itemData); const chan = store.getters.findChannelOnCurrentNetwork(itemData);
if (chan) { if (chan) {
vueApp.switchToChannel(chan); switchToChannel(chan);
} }
socket.emit("input", { socket.emit("input", {
@ -292,7 +293,7 @@ function addFocusItem() {
function addEditNetworkItem() { function addEditNetworkItem() {
function edit(networkUuid) { function edit(networkUuid) {
vueApp.$router.push("/edit-network/" + networkUuid); navigate("NetworkEdit", {uuid: networkUuid});
} }
addContextMenuItem({ addContextMenuItem({

View file

@ -2,8 +2,8 @@
const $ = require("jquery"); const $ = require("jquery");
const Mousetrap = require("mousetrap"); const Mousetrap = require("mousetrap");
const {vueApp} = require("./vue");
const store = require("./store").default; const store = require("./store").default;
const {switchToChannel} = require("./router");
// Switch to the next/previous window in the channel list. // Switch to the next/previous window in the channel list.
Mousetrap.bind(["alt+up", "alt+down"], function(e, keys) { Mousetrap.bind(["alt+up", "alt+down"], function(e, keys) {
@ -92,7 +92,7 @@ Mousetrap.bind(["alt+a"], function() {
} }
if (targetchan) { if (targetchan) {
vueApp.switchToChannel(targetchan); switchToChannel(targetchan);
} }
return false; return false;

View file

@ -36,14 +36,13 @@ const router = new VueRouter({
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
// Disallow navigating to non-existing routes // Disallow navigating to non-existing routes
if (store.state.appLoaded && !to.matched.length) { if (store.state.appLoaded && !to.matched.length) {
next(false); next(false);
return; return;
} }
// Disallow navigating to invalid channels // Disallow navigating to invalid channels
if (to.name === "RoutedChat" && !store.getters.findChannel(Number(to.params.pathMatch))) { if (to.name === "RoutedChat" && !store.getters.findChannel(Number(to.params.id))) {
next(false); next(false);
return; return;
} }
@ -65,22 +64,26 @@ router.beforeEach((to, from, next) => {
next(); next();
}); });
router.afterEach((to) => { router.afterEach(() => {
if (store.state.appLoaded) { if (store.state.appLoaded) {
if (window.innerWidth <= constants.mobileViewportPixels) { if (window.innerWidth <= constants.mobileViewportPixels) {
store.commit("sidebarOpen", false); store.commit("sidebarOpen", false);
} }
} }
if (to.name !== "RoutedChat") { if (store.state.activeChannel) {
// Navigating out of a chat window const channel = store.state.activeChannel.channel;
store.commit("activeWindow", to.name); store.commit("activeChannel", null);
if (store.state.activeChannel && store.state.activeChannel.channel) { // When switching out of a channel, mark everything as read
router.app.switchOutOfChannel(store.state.activeChannel.channel); if (channel.messages.length > 0) {
channel.firstUnread = channel.messages[channel.messages.length - 1].id;
} }
store.commit("activeChannel", null); if (channel.messages.length > 100) {
channel.messages.splice(0, channel.messages.length - 100);
channel.moreHistoryAvailable = true;
}
} }
}); });
@ -114,13 +117,19 @@ function initialize() {
}, },
{ {
name: "RoutedChat", name: "RoutedChat",
path: "/chan-*", path: "/chan-:id",
component: RoutedChat, component: RoutedChat,
}, },
]); ]);
} }
function navigate(routeName, params = {}) {
router.push({name: routeName, params}).catch(() => {});
}
module.exports = { module.exports = {
initialize, initialize,
router, router,
navigate,
switchToChannel: (channel) => navigate("RoutedChat", {id: channel.id}),
}; };

View file

@ -3,6 +3,7 @@
const socket = require("../socket"); const socket = require("../socket");
const storage = require("../localStorage"); const storage = require("../localStorage");
const {vueApp} = require("../vue"); const {vueApp} = require("../vue");
const {navigate} = require("../router");
const store = require("../store").default; const store = require("../store").default;
let lastServerHash = null; let lastServerHash = null;
@ -76,7 +77,7 @@ function showSignIn() {
} }
if (vueApp.$route.name !== "SignIn") { if (vueApp.$route.name !== "SignIn") {
vueApp.$router.push("/sign-in"); navigate("SignIn");
} }
} }

View file

@ -5,6 +5,7 @@ const webpush = require("../webpush");
const storage = require("../localStorage"); const storage = require("../localStorage");
const constants = require("../constants"); const constants = require("../constants");
const {vueApp, initChannel} = require("../vue"); const {vueApp, initChannel} = require("../vue");
const {switchToChannel, navigate} = require("../router");
const router = require("../router"); const router = require("../router");
const store = require("../store").default; const store = require("../store").default;
@ -49,13 +50,13 @@ socket.on("init", function(data) {
const channel = store.getters.findChannel(data.active); const channel = store.getters.findChannel(data.active);
if (channel) { if (channel) {
vueApp.switchToChannel(channel.channel); switchToChannel(channel.channel);
} else if (store.state.networks.length > 0) { } else if (store.state.networks.length > 0) {
// Server is telling us to open a channel that does not exist // Server is telling us to open a channel that does not exist
// For example, it can be unset if you first open the page after server start // For example, it can be unset if you first open the page after server start
vueApp.switchToChannel(store.state.networks[0].channels[0]); switchToChannel(store.state.networks[0].channels[0]);
} else { } else {
vueApp.$router.push("/connect"); navigate("Connect");
} }
} }
} }

View file

@ -1,8 +1,9 @@
"use strict"; "use strict";
const socket = require("../socket"); const socket = require("../socket");
const {vueApp, initChannel} = require("../vue"); const {initChannel} = require("../vue");
const store = require("../store").default; const store = require("../store").default;
const {switchToChannel} = require("../router");
socket.on("join", function(data) { socket.on("join", function(data) {
initChannel(data.chan); initChannel(data.chan);
@ -20,5 +21,5 @@ socket.on("join", function(data) {
return; return;
} }
vueApp.switchToChannel(store.getters.findChannel(data.chan.id).channel); switchToChannel(store.getters.findChannel(data.chan.id).channel);
}); });

View file

@ -3,8 +3,8 @@
const socket = require("../socket"); const socket = require("../socket");
const cleanIrcMessage = require("../helpers/ircmessageparser/cleanIrcMessage"); const cleanIrcMessage = require("../helpers/ircmessageparser/cleanIrcMessage");
const webpush = require("../webpush"); const webpush = require("../webpush");
const {vueApp} = require("../vue");
const store = require("../store").default; const store = require("../store").default;
const {switchToChannel} = require("../router");
let pop; let pop;
@ -154,7 +154,7 @@ function notifyMessage(targetId, channel, activeChannel, msg) {
const channelTarget = store.getters.findChannel(targetId); const channelTarget = store.getters.findChannel(targetId);
if (channelTarget) { if (channelTarget) {
vueApp.switchToChannel(channelTarget); switchToChannel(channelTarget);
} }
}); });
} }

View file

@ -1,11 +1,11 @@
"use strict"; "use strict";
const socket = require("../socket"); const socket = require("../socket");
const {vueApp} = require("../vue");
const store = require("../store").default; const store = require("../store").default;
const {switchToChannel} = require("../router");
socket.on("msg:special", function(data) { socket.on("msg:special", function(data) {
const channel = store.getters.findChannel(data.chan); const channel = store.getters.findChannel(data.chan);
channel.channel.data = data.data; channel.channel.data = data.data;
vueApp.switchToChannel(channel.channel); switchToChannel(channel.channel);
}); });

View file

@ -3,6 +3,7 @@
const socket = require("../socket"); const socket = require("../socket");
const {vueApp, initChannel} = require("../vue"); const {vueApp, initChannel} = require("../vue");
const store = require("../store").default; const store = require("../store").default;
const {switchToChannel} = require("../router");
socket.on("network", function(data) { socket.on("network", function(data) {
const network = data.networks[0]; const network = data.networks[0];
@ -12,7 +13,7 @@ socket.on("network", function(data) {
network.channels.forEach(initChannel); network.channels.forEach(initChannel);
store.commit("networks", [...store.state.networks, network]); store.commit("networks", [...store.state.networks, network]);
vueApp.switchToChannel(network.channels[0]); switchToChannel(network.channels[0]);
}); });
socket.on("network:options", function(data) { socket.on("network:options", function(data) {

View file

@ -1,13 +1,13 @@
"use strict"; "use strict";
const socket = require("../socket"); const socket = require("../socket");
const {vueApp} = require("../vue");
const store = require("../store").default; const store = require("../store").default;
const {switchToChannel} = require("../router");
socket.on("part", function(data) { socket.on("part", function(data) {
// When parting from the active channel/query, jump to the network's lobby // When parting from the active channel/query, jump to the network's lobby
if (store.state.activeChannel && store.state.activeChannel.channel.id === data.chan) { if (store.state.activeChannel && store.state.activeChannel.channel.id === data.chan) {
vueApp.switchToChannel(store.state.activeChannel.network.channels[0]); switchToChannel(store.state.activeChannel.network.channels[0]);
} }
const channel = store.getters.findChannel(data.chan); const channel = store.getters.findChannel(data.chan);

View file

@ -1,7 +1,7 @@
"use strict"; "use strict";
const socket = require("../socket"); const socket = require("../socket");
const {vueApp} = require("../vue"); const {switchToChannel, navigate} = require("../router");
const store = require("../store").default; const store = require("../store").default;
socket.on("quit", function(data) { socket.on("quit", function(data) {
@ -17,8 +17,8 @@ socket.on("quit", function(data) {
} }
if (store.state.networks.length > 0) { if (store.state.networks.length > 0) {
vueApp.switchToChannel(store.state.networks[0].channels[0]); switchToChannel(store.state.networks[0].channels[0]);
} else { } else {
vueApp.$router.push("/connect"); navigate("Connect");
} }
}); });

View file

@ -26,7 +26,6 @@ const store = new Vuex.Store({
isAutoCompleting: false, isAutoCompleting: false,
isConnected: false, isConnected: false,
isFileUploadEnabled: false, isFileUploadEnabled: false,
activeWindow: null,
networks: [], networks: [],
pushNotificationState: "unsupported", pushNotificationState: "unsupported",
serverConfiguration: {}, serverConfiguration: {},
@ -60,9 +59,6 @@ const store = new Vuex.Store({
isFileUploadEnabled(state, isFileUploadEnabled) { isFileUploadEnabled(state, isFileUploadEnabled) {
state.isFileUploadEnabled = isFileUploadEnabled; state.isFileUploadEnabled = isFileUploadEnabled;
}, },
activeWindow(state, payload) {
state.activeWindow = payload;
},
networks(state, networks) { networks(state, networks) {
state.networks = networks; state.networks = networks;
}, },

View file

@ -6,7 +6,7 @@ const store = require("./store").default;
const App = require("../components/App.vue").default; const App = require("../components/App.vue").default;
const localetime = require("./helpers/localetime"); const localetime = require("./helpers/localetime");
const storage = require("./localStorage"); const storage = require("./localStorage");
const {router} = require("./router"); const {router, navigate} = require("./router");
const constants = require("./constants"); const constants = require("./constants");
Vue.filter("localetime", localetime); Vue.filter("localetime", localetime);
@ -35,25 +35,7 @@ const vueApp = new Vue({
}, },
methods: { methods: {
switchToChannel(channel) { switchToChannel(channel) {
if ( navigate("RoutedChat", {id: channel.id});
this.$store.state.activeChannel &&
this.$store.state.activeChannel.channel.id === channel.id
) {
return;
}
this.$router.push("/chan-" + channel.id);
},
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;
}
}, },
}, },
render(createElement) { render(createElement) {

View file

@ -2,8 +2,8 @@
const storage = require("./localStorage"); const storage = require("./localStorage");
const socket = require("./socket"); const socket = require("./socket");
const vueApp = require("./vue");
const store = require("./store").default; const store = require("./store").default;
const {switchToChannel} = require("./router");
let clientSubscribed = null; let clientSubscribed = null;
let applicationServerKey; let applicationServerKey;
@ -15,7 +15,7 @@ if ("serviceWorker" in navigator) {
const channelTarget = store.getters.findChannel(id); const channelTarget = store.getters.findChannel(id);
if (channelTarget) { if (channelTarget) {
vueApp.switchToChannel(channelTarget); switchToChannel(channelTarget);
} }
} }
}); });