2017-05-18 20:08:54 +00:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
const socket = require("../socket");
|
2017-07-10 19:47:03 +00:00
|
|
|
const webpush = require("../webpush");
|
2017-05-18 20:08:54 +00:00
|
|
|
const storage = require("../localStorage");
|
2019-11-12 10:39:46 +00:00
|
|
|
const {initChannel} = require("../vue");
|
|
|
|
const {router, switchToChannel, navigate} = require("../router");
|
2019-11-02 19:40:59 +00:00
|
|
|
const store = require("../store").default;
|
2017-05-18 20:08:54 +00:00
|
|
|
|
|
|
|
socket.on("init", function(data) {
|
2019-11-02 19:40:59 +00:00
|
|
|
store.commit("networks", mergeNetworkData(data.networks));
|
|
|
|
store.commit("isConnected", true);
|
|
|
|
store.commit("currentUserVisibleError", null);
|
2018-07-08 17:53:23 +00:00
|
|
|
|
2019-11-12 11:09:12 +00:00
|
|
|
if (data.token) {
|
|
|
|
storage.set("token", data.token);
|
|
|
|
}
|
|
|
|
|
2019-11-05 19:29:51 +00:00
|
|
|
if (!store.state.appLoaded) {
|
|
|
|
store.commit("appLoaded");
|
2018-07-15 20:23:49 +00:00
|
|
|
|
2019-11-12 11:09:12 +00:00
|
|
|
// TODO: Try to move webpush key to configuration event
|
2017-08-28 15:03:27 +00:00
|
|
|
webpush.configurePushNotifications(data.pushSubscription, data.applicationServerKey);
|
2017-07-10 19:47:03 +00:00
|
|
|
|
2019-11-05 19:29:51 +00:00
|
|
|
document.body.classList.remove("signed-out");
|
2017-12-01 18:04:50 +00:00
|
|
|
|
2019-11-05 19:29:51 +00:00
|
|
|
if (window.g_TheLoungeRemoveLoading) {
|
|
|
|
window.g_TheLoungeRemoveLoading();
|
2017-12-01 18:04:50 +00:00
|
|
|
}
|
2019-11-03 17:09:10 +00:00
|
|
|
|
2019-11-12 11:09:12 +00:00
|
|
|
// TODO: Review this code and make it better
|
2019-11-12 10:39:46 +00:00
|
|
|
if (!router.currentRoute.name || router.currentRoute.name === "SignIn") {
|
2019-11-03 17:09:10 +00:00
|
|
|
const channel = store.getters.findChannel(data.active);
|
|
|
|
|
|
|
|
if (channel) {
|
2019-11-11 19:18:55 +00:00
|
|
|
switchToChannel(channel.channel);
|
2019-11-03 17:09:10 +00:00
|
|
|
} 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
|
2019-11-11 19:18:55 +00:00
|
|
|
switchToChannel(store.state.networks[0].channels[0]);
|
2019-11-03 17:09:10 +00:00
|
|
|
} else {
|
2019-11-11 19:18:55 +00:00
|
|
|
navigate("Connect");
|
2019-11-03 17:09:10 +00:00
|
|
|
}
|
|
|
|
}
|
2017-05-18 20:08:54 +00:00
|
|
|
|
2019-11-12 11:09:12 +00:00
|
|
|
if ("URLSearchParams" in window) {
|
|
|
|
handleQueryParams();
|
|
|
|
}
|
2019-10-20 14:45:38 +00:00
|
|
|
}
|
2017-05-18 20:08:54 +00:00
|
|
|
});
|
2017-08-28 20:06:28 +00:00
|
|
|
|
2019-07-15 19:26:32 +00:00
|
|
|
function mergeNetworkData(newNetworks) {
|
|
|
|
const collapsedNetworks = new Set(JSON.parse(storage.get("thelounge.networks.collapsed")));
|
|
|
|
|
|
|
|
for (let n = 0; n < newNetworks.length; n++) {
|
|
|
|
const network = newNetworks[n];
|
2019-11-03 14:59:43 +00:00
|
|
|
const currentNetwork = store.getters.findNetwork(network.uuid);
|
2019-07-15 19:26:32 +00:00
|
|
|
|
|
|
|
// If this network is new, set some default variables and initalize channel variables
|
|
|
|
if (!currentNetwork) {
|
|
|
|
network.isJoinChannelShown = false;
|
|
|
|
network.isCollapsed = collapsedNetworks.has(network.uuid);
|
|
|
|
network.channels.forEach(initChannel);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merge received network object into existing network object on the client
|
2019-11-12 10:39:46 +00:00
|
|
|
// so the object reference stays the same (e.g. for currentChannel state)
|
2019-07-15 19:26:32 +00:00
|
|
|
for (const key in network) {
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(network, key)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Channels require extra care to be merged correctly
|
|
|
|
if (key === "channels") {
|
2019-07-17 09:33:59 +00:00
|
|
|
currentNetwork.channels = mergeChannelData(
|
|
|
|
currentNetwork.channels,
|
|
|
|
network.channels
|
|
|
|
);
|
2019-07-15 19:26:32 +00:00
|
|
|
} else {
|
|
|
|
currentNetwork[key] = network[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newNetworks[n] = currentNetwork;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newNetworks;
|
|
|
|
}
|
|
|
|
|
|
|
|
function mergeChannelData(oldChannels, newChannels) {
|
|
|
|
for (let c = 0; c < newChannels.length; c++) {
|
|
|
|
const channel = newChannels[c];
|
|
|
|
const currentChannel = oldChannels.find((chan) => chan.id === channel.id);
|
|
|
|
|
|
|
|
// This is a new channel that was joined while client was disconnected, initialize it
|
|
|
|
if (!currentChannel) {
|
|
|
|
initChannel(channel);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Merge received channel object into existing currentChannel
|
2019-11-02 19:40:59 +00:00
|
|
|
// so the object references are exactly the same (e.g. in store.state.activeChannel)
|
2019-07-15 19:26:32 +00:00
|
|
|
for (const key in channel) {
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(channel, key)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Server sends an empty users array, client requests it whenever needed
|
|
|
|
if (key === "users") {
|
|
|
|
if (channel.type === "channel") {
|
2019-11-02 19:40:59 +00:00
|
|
|
if (
|
|
|
|
store.state.activeChannel &&
|
|
|
|
store.state.activeChannel.channel === currentChannel
|
|
|
|
) {
|
2019-10-15 17:33:03 +00:00
|
|
|
// For currently open channel, request the user list straight away
|
|
|
|
socket.emit("names", {
|
|
|
|
target: channel.id,
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// For all other channels, mark the user list as outdated
|
|
|
|
// so an update will be requested whenever user switches to these channels
|
|
|
|
currentChannel.usersOutdated = true;
|
|
|
|
}
|
2019-07-15 19:26:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-10-17 10:27:15 +00:00
|
|
|
// Server sends total count of messages in memory, we compare it to amount of messages
|
|
|
|
// on the client, and decide whether theres more messages to load from server
|
|
|
|
if (key === "totalMessages") {
|
|
|
|
currentChannel.moreHistoryAvailable =
|
|
|
|
channel.totalMessages > currentChannel.messages.length;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-07-15 19:26:32 +00:00
|
|
|
// Reconnection only sends new messages, so merge it on the client
|
|
|
|
// Only concat if server sent us less than 100 messages so we don't introduce gaps
|
|
|
|
if (key === "messages" && currentChannel.messages && channel.messages.length < 100) {
|
|
|
|
currentChannel.messages = currentChannel.messages.concat(channel.messages);
|
|
|
|
} else {
|
|
|
|
currentChannel[key] = channel[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
newChannels[c] = currentChannel;
|
|
|
|
}
|
|
|
|
|
|
|
|
return newChannels;
|
|
|
|
}
|
2019-11-12 11:09:12 +00:00
|
|
|
|
|
|
|
function handleQueryParams() {
|
|
|
|
const params = new URLSearchParams(document.location.search);
|
|
|
|
|
|
|
|
const cleanParams = () => {
|
|
|
|
// Remove query parameters from url without reloading the page
|
|
|
|
const cleanUri = window.location.origin + window.location.pathname + window.location.hash;
|
|
|
|
window.history.replaceState({}, document.title, cleanUri);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (params.has("uri")) {
|
|
|
|
// Set default connection settings from IRC protocol links
|
|
|
|
const uri =
|
|
|
|
params.get("uri") +
|
|
|
|
(location.hash.startsWith("#/") ? `#${location.hash.substring(2)}` : location.hash);
|
|
|
|
const queryParams = parseIrcUri(uri);
|
|
|
|
|
|
|
|
cleanParams();
|
|
|
|
router.router.push({name: "Connect", query: queryParams});
|
|
|
|
} else if (document.body.classList.contains("public") && document.location.search) {
|
|
|
|
// Set default connection settings from url params
|
|
|
|
const queryParams = Object.fromEntries(params.entries());
|
|
|
|
|
|
|
|
cleanParams();
|
|
|
|
router.router.push({name: "Connect", query: queryParams});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function parseIrcUri(stringUri) {
|
|
|
|
const data = {};
|
|
|
|
|
|
|
|
try {
|
|
|
|
// https://tools.ietf.org/html/draft-butcher-irc-url-04
|
|
|
|
const uri = new URL(stringUri);
|
|
|
|
|
|
|
|
// Replace protocol with a "special protocol" (that's what it's called in WHATWG spec)
|
|
|
|
// So that the uri can be properly parsed
|
|
|
|
if (uri.protocol === "irc:") {
|
|
|
|
uri.protocol = "http:";
|
|
|
|
|
|
|
|
if (!uri.port) {
|
|
|
|
uri.port = 6667;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.tls = false;
|
|
|
|
} else if (uri.protocol === "ircs:") {
|
|
|
|
uri.protocol = "https:";
|
|
|
|
|
|
|
|
if (!uri.port) {
|
|
|
|
uri.port = 6697;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.tls = true;
|
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
data.host = data.name = uri.hostname;
|
|
|
|
data.port = uri.port;
|
|
|
|
data.username = window.decodeURIComponent(uri.username);
|
|
|
|
data.password = window.decodeURIComponent(uri.password);
|
|
|
|
|
|
|
|
let channel = (uri.pathname + uri.hash).substr(1);
|
|
|
|
const index = channel.indexOf(",");
|
|
|
|
|
|
|
|
if (index > -1) {
|
|
|
|
channel = channel.substring(0, index);
|
|
|
|
}
|
|
|
|
|
|
|
|
data.join = channel;
|
|
|
|
} catch (e) {
|
|
|
|
// do nothing on invalid uri
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|