diff --git a/client/components/ChannelWrapper.vue b/client/components/ChannelWrapper.vue index c979007f..71c6b1a6 100644 --- a/client/components/ChannelWrapper.vue +++ b/client/components/ChannelWrapper.vue @@ -72,7 +72,7 @@ export default { return; } - this.$root.switchToChannel(this.channel); + this.$root.switchToChannel(this.network, this.channel); }, openContextMenu(event) { eventbus.emit("contextmenu:channel", { diff --git a/client/components/InlineChannel.vue b/client/components/InlineChannel.vue index 2a1478cf..03cd3b4d 100644 --- a/client/components/InlineChannel.vue +++ b/client/components/InlineChannel.vue @@ -17,7 +17,7 @@ export default { const existingChannel = this.$store.getters.findChannelOnCurrentNetwork(this.channel); if (existingChannel) { - this.$root.switchToChannel(existingChannel); + this.$root.switchToChannel(this.$store.activeChannel.network, existingChannel); } socket.emit("input", { diff --git a/client/components/JoinChannel.vue b/client/components/JoinChannel.vue index 129d5383..d7004318 100644 --- a/client/components/JoinChannel.vue +++ b/client/components/JoinChannel.vue @@ -64,7 +64,7 @@ export default { ); if (existingChannel) { - this.$root.switchToChannel(existingChannel); + this.$root.switchToChannel(this.$store.activeChannel.network, existingChannel); } else { const chanTypes = this.network.serverOptions.CHANTYPES; let channel = this.inputChannel; diff --git a/client/components/NetworkList.vue b/client/components/NetworkList.vue index 607f3752..91c340d0 100644 --- a/client/components/NetworkList.vue +++ b/client/components/NetworkList.vue @@ -355,14 +355,17 @@ export default { channel = this.results[0].channel; } - this.activeSearchItem = channel; + this.activeSearchItem = {channel, network}; }, selectResult() { if (!this.searchText || !this.results.length) { return; } - this.$root.switchToChannel(this.activeSearchItem); + this.$root.switchToChannel( + this.activeSearchItem.network, + this.activeSearchItem.channel + ); this.deactivateSearch(); this.scrollToActive(); }, diff --git a/client/components/RoutedChat.vue b/client/components/RoutedChat.vue index b84a2e89..30e1fcf5 100644 --- a/client/components/RoutedChat.vue +++ b/client/components/RoutedChat.vue @@ -13,8 +13,10 @@ export default { }, computed: { activeChannel() { - const chanId = parseInt(this.$route.params.id, 10); - const channel = this.$store.getters.findChannel(chanId); + let channel = this.$store.getters.findChannelByName( + this.$route.params.networkHost, + this.$route.params.channelName + ); return channel; }, }, diff --git a/client/components/Windows/Connect.vue b/client/components/Windows/Connect.vue index 11fc5661..3464441e 100644 --- a/client/components/Windows/Connect.vue +++ b/client/components/Windows/Connect.vue @@ -15,6 +15,7 @@ export default { queryParams: Object, }, data() { + console.log(this.queryParams); // Merge settings from url params into default settings const defaults = Object.assign( {}, diff --git a/client/components/Windows/NetworkEdit.vue b/client/components/Windows/NetworkEdit.vue index e0bc4899..b59820a2 100644 --- a/client/components/Windows/NetworkEdit.vue +++ b/client/components/Windows/NetworkEdit.vue @@ -43,7 +43,7 @@ export default { const network = this.$store.getters.findNetwork(data.uuid); network.name = network.channels[0].name = data.name; - this.$root.switchToChannel(network.channels[0]); + this.$root.switchToChannel(network, network.channels[0]); }, }, }; diff --git a/client/js/helpers/contextMenu.js b/client/js/helpers/contextMenu.js index ba0f402c..ab720e38 100644 --- a/client/js/helpers/contextMenu.js +++ b/client/js/helpers/contextMenu.js @@ -98,7 +98,7 @@ export function generateChannelContextMenu($root, channel, network) { class: "edit", action() { channel.editTopic = true; - $root.switchToChannel(channel); + $root.switchToChannel(network, channel); }, }); items.push({ @@ -122,7 +122,7 @@ export function generateChannelContextMenu($root, channel, network) { type: "item", class: "action-whois", action() { - $root.switchToChannel(channel); + $root.switchToChannel(network, channel); socket.emit("input", { target: channel.id, text: "/whois " + channel.name, @@ -191,7 +191,7 @@ export function generateUserContextMenu($root, channel, network, user) { const chan = $root.$store.getters.findChannelOnCurrentNetwork(user.nick); if (chan) { - $root.switchToChannel(chan); + $root.switchToChannel(network, chan); } socket.emit("input", { @@ -235,7 +235,7 @@ export function generateUserContextMenu($root, channel, network, user) { const chan = $root.$store.getters.findChannelOnCurrentNetwork(user.nick); if (chan) { - $root.switchToChannel(chan); + $root.switchToChannel(network, chan); } socket.emit("input", { diff --git a/client/js/keybinds.js b/client/js/keybinds.js index daee796f..be9878c4 100644 --- a/client/js/keybinds.js +++ b/client/js/keybinds.js @@ -85,30 +85,32 @@ Mousetrap.bind(["alt+a"], function (e) { return true; } - let targetChannel; + let targetNetwork, targetChannel; outer_loop: for (const network of store.state.networks) { for (const chan of network.channels) { if (chan.highlight) { targetChannel = chan; + targetNetwork = network; break outer_loop; } if (chan.unread && !targetChannel) { targetChannel = chan; + targetNetwork = network; } } } if (targetChannel) { - jumpToChannel(targetChannel); + jumpToChannel(targetNetwork, targetChannel); } return false; }); -function jumpToChannel(targetChannel) { - switchToChannel(targetChannel); +function jumpToChannel(targetNetwork, targetChannel) { + switchToChannel(targetNetwork, targetChannel); const element = document.querySelector( `#sidebar .channel-list-item[aria-controls="#chan-${targetChannel.id}"]` diff --git a/client/js/router.js b/client/js/router.js index 58381238..0bb4678b 100644 --- a/client/js/router.js +++ b/client/js/router.js @@ -60,7 +60,7 @@ const router = new VueRouter({ }, { name: "RoutedChat", - path: "/chan-:id", + path: "/:networkHost/:channelName?", component: RoutedChat, }, ], @@ -88,8 +88,27 @@ router.beforeEach((to, from, next) => { return; } - // Disallow navigating to invalid channels - if (to.name === "RoutedChat" && !store.getters.findChannel(Number(to.params.id))) { + // If trying to navigate to an invalid channel, + // we attempt to either open a connection dialog to the network + // or populate the Join Channel field in the exiting network. + if ( + to.name === "RoutedChat" && + !store.getters.findChannelByName(to.params.networkHost, to.params.channelName) + ) { + const existingNetwork = store.state.networks.find( + (network) => network.host === to.params.networkHost + ); + + if (existingNetwork) { + // Join UI + } else { + // Connect UI + next({ + path: "/connect", + query: {...to.query, host: to.params.networkHost, channels: to.params.channelName}, + }); + return; + } next(false); return; } @@ -154,8 +173,8 @@ function navigate(routeName, params = {}) { } } -function switchToChannel(channel) { - return navigate("RoutedChat", {id: channel.id}); +function switchToChannel(network, channel) { + return navigate("RoutedChat", {networkHost: network.host, channelName: channel.name}); } if ("serviceWorker" in navigator) { @@ -164,9 +183,8 @@ if ("serviceWorker" in navigator) { const id = parseInt(event.data.channel.substr(5), 10); // remove "chan-" prefix const channelTarget = store.getters.findChannel(id); - if (channelTarget) { - switchToChannel(channelTarget.channel); + switchToChannel(channelTarget.network, channelTarget.channel); } } }); diff --git a/client/js/socket-events/init.js b/client/js/socket-events/init.js index fd0451d9..1920c838 100644 --- a/client/js/socket-events/init.js +++ b/client/js/socket-events/init.js @@ -35,11 +35,14 @@ socket.on("init", function (data) { const channel = store.getters.findChannel(data.active); if (channel) { - switchToChannel(channel.channel); + switchToChannel(channel.network, channel.channel); } else if (store.state.networks.length > 0) { // 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 - switchToChannel(store.state.networks[0].channels[0]); + switchToChannel( + store.state.networks[0], + store.state.networks[0].channels[0] + ); } else { navigate("Connect"); } diff --git a/client/js/socket-events/join.js b/client/js/socket-events/join.js index f7159dcb..4c87e5ef 100644 --- a/client/js/socket-events/join.js +++ b/client/js/socket-events/join.js @@ -20,5 +20,7 @@ socket.on("join", function (data) { return; } - switchToChannel(store.getters.findChannel(data.chan.id).channel); + const channel = store.getters.findChannel(data.chan.id); + + switchToChannel(channel.network, channel.channel); }); diff --git a/client/js/socket-events/msg.js b/client/js/socket-events/msg.js index 682274a2..2a1caecd 100644 --- a/client/js/socket-events/msg.js +++ b/client/js/socket-events/msg.js @@ -158,7 +158,7 @@ function notifyMessage(targetId, channel, activeChannel, msg) { const channelTarget = store.getters.findChannel(targetId); if (channelTarget) { - switchToChannel(channelTarget); + switchToChannel(channelTarget.network, channelTarget.channel); } }); } diff --git a/client/js/socket-events/msg_special.js b/client/js/socket-events/msg_special.js index b0d15692..2254327f 100644 --- a/client/js/socket-events/msg_special.js +++ b/client/js/socket-events/msg_special.js @@ -7,5 +7,5 @@ import {switchToChannel} from "../router"; socket.on("msg:special", function (data) { const channel = store.getters.findChannel(data.chan); channel.channel.data = data.data; - switchToChannel(channel.channel); + switchToChannel(channel.network, channel.channel); }); diff --git a/client/js/socket-events/network.js b/client/js/socket-events/network.js index 9f248657..264d2cc8 100644 --- a/client/js/socket-events/network.js +++ b/client/js/socket-events/network.js @@ -16,7 +16,7 @@ socket.on("network", function (data) { store.commit("networks", [...store.state.networks, network]); // Open last channel specified in `join` - switchToChannel(network.channels[network.channels.length - 1]); + switchToChannel(network, network.channels[network.channels.length - 1]); }); socket.on("network:options", function (data) { diff --git a/client/js/socket-events/part.js b/client/js/socket-events/part.js index 58be3a57..bcc2b123 100644 --- a/client/js/socket-events/part.js +++ b/client/js/socket-events/part.js @@ -7,7 +7,10 @@ import {switchToChannel} from "../router"; socket.on("part", function (data) { // When parting from the active channel/query, jump to the network's lobby if (store.state.activeChannel && store.state.activeChannel.channel.id === data.chan) { - switchToChannel(store.state.activeChannel.network.channels[0]); + switchToChannel( + store.state.activeChannel.network, + store.state.activeChannel.network.channels[0] + ); } const channel = store.getters.findChannel(data.chan); diff --git a/client/js/socket-events/quit.js b/client/js/socket-events/quit.js index ed3da8d4..8e0fd467 100644 --- a/client/js/socket-events/quit.js +++ b/client/js/socket-events/quit.js @@ -17,7 +17,7 @@ socket.on("quit", function (data) { } if (store.state.networks.length > 0) { - switchToChannel(store.state.networks[0].channels[0]); + switchToChannel(store.state.networks[0], store.state.networks[0].channels[0]); } else { navigate("Connect"); } diff --git a/client/js/store.js b/client/js/store.js index 3d336d06..92cdf40a 100644 --- a/client/js/store.js +++ b/client/js/store.js @@ -129,6 +129,19 @@ const store = new Vuex.Store({ return null; }, + findChannelByName: (state) => (networkHost, channelName) => { + for (const network of state.networks) { + if (network.host.toLowerCase() === networkHost.toLowerCase()) { + for (const channel of network.channels) { + if (channel.name.toLowerCase() === channelName.toLowerCase()) { + return {network, channel}; + } + } + } + } + + return null; + }, findNetwork: (state) => (uuid) => { for (const network of state.networks) { if (network.uuid === uuid) { diff --git a/client/js/vue.js b/client/js/vue.js index 18f913da..392b962c 100644 --- a/client/js/vue.js +++ b/client/js/vue.js @@ -7,7 +7,7 @@ import Vue from "vue"; import store from "./store"; import App from "../components/App.vue"; import storage from "./localStorage"; -import {router, navigate} from "./router"; +import {router, switchToChannel} from "./router"; import socket from "./socket"; import eventbus from "./eventbus"; @@ -26,8 +26,8 @@ new Vue({ socket.open(); }, methods: { - switchToChannel(channel) { - navigate("RoutedChat", {id: channel.id}); + switchToChannel(network, channel) { + switchToChannel(network, channel); }, closeChannel(channel) { if (channel.type === "lobby") { diff --git a/src/models/network.js b/src/models/network.js index bc2dbce1..7480e518 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -19,6 +19,7 @@ const fieldsForClient = { name: true, nick: true, serverOptions: true, + host: true, }; function Network(attr) {