From f86d73972d9dab3102a97dfbe3773b2f6ed5d03d Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Thu, 15 Mar 2018 10:37:32 +0200 Subject: [PATCH] Implement network editing UI and object updating --- client/css/style.css | 1 + client/js/contextMenuFactory.js | 16 ++++++ client/js/lounge.js | 10 ---- client/js/socket-events/changelog.js | 6 +-- client/js/socket-events/configuration.js | 62 ++++++++++++++---------- client/js/socket-events/network.js | 15 ++++++ client/views/windows/connect.tpl | 47 ++++++++++++------ src/models/network.js | 57 ++++++++++++++++++++++ src/server.js | 2 +- test/server.js | 2 +- 10 files changed, 162 insertions(+), 56 deletions(-) diff --git a/client/css/style.css b/client/css/style.css index c5e8baff..68d4f109 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -417,6 +417,7 @@ kbd { margin-right: 9px; } +.context-menu-edit::before, #set-nick::before { content: "\f303"; /* https://fontawesome.com/icons/pencil-alt?style=solid */ } diff --git a/client/js/contextMenuFactory.js b/client/js/contextMenuFactory.js index a822deea..4a94a94f 100644 --- a/client/js/contextMenuFactory.js +++ b/client/js/contextMenuFactory.js @@ -153,6 +153,21 @@ function addFocusItem() { }); } +function addEditNetworkItem() { + function edit(itemData) { + socket.emit("network:get", itemData); + $('button[data-target="#connect"]').trigger("click"); + } + + addContextMenuItem({ + check: (target) => target.hasClass("lobby"), + className: "edit", + displayName: "Edit this network…", + data: (target) => target.closest(".network").data("uuid"), + callback: edit, + }); +} + function addChannelListItem() { function list(itemData) { socket.emit("input", { @@ -207,6 +222,7 @@ function addDefaultItems() { addQueryItem(); addKickItem(); addFocusItem(); + addEditNetworkItem(); addChannelListItem(); addBanListItem(); addJoinItem(); diff --git a/client/js/lounge.js b/client/js/lounge.js index 9d98aef6..809bbfd1 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -18,7 +18,6 @@ const utils = require("./utils"); require("./webpush"); require("./keybinds"); require("./clipboard"); -const Changelog = require("./socket-events/changelog"); const contextMenuFactory = require("./contextMenuFactory"); const contextMenuContainer = $("#context-menu-container"); @@ -322,15 +321,6 @@ $(function() { socket.emit("names", {target: self.data("id")}); } - if (target === "#settings") { - $("#session-list").html("

Loading…

"); - socket.emit("sessions:get"); - } - - if (target === "#help" || target === "#changelog") { - Changelog.requestIfNeeded(); - } - // 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 diff --git a/client/js/socket-events/changelog.js b/client/js/socket-events/changelog.js index 983b15f3..ae6c4b0a 100644 --- a/client/js/socket-events/changelog.js +++ b/client/js/socket-events/changelog.js @@ -4,10 +4,6 @@ const $ = require("jquery"); const socket = require("../socket"); const templates = require("../../views"); -module.exports = { - requestIfNeeded, -}; - // Requests version information if it hasn't been retrieved before (or if it has // been removed from the page, i.e. when clicking on "Check now". Displays a // loading state until received. @@ -54,6 +50,8 @@ socket.on("changelog", function(data) { } }); +$("#help, #changelog").on("show", requestIfNeeded); + // When clicking the "Check now" button, remove current checker information and // request a new one. Loading will be displayed in the meantime. $("#help").on("click", "#check-now", () => { diff --git a/client/js/socket-events/configuration.js b/client/js/socket-events/configuration.js index aa76b203..54232f50 100644 --- a/client/js/socket-events/configuration.js +++ b/client/js/socket-events/configuration.js @@ -5,6 +5,7 @@ const socket = require("../socket"); const templates = require("../../views"); const options = require("../options"); const webpush = require("../webpush"); +const connect = $("#connect"); socket.on("configuration", function(data) { if (options.initialized) { @@ -14,10 +15,14 @@ socket.on("configuration", function(data) { } $("#settings").html(templates.windows.settings(data)); - $("#connect").html(templates.windows.connect(data)); $("#help").html(templates.windows.help(data)); $("#changelog").html(templates.windows.changelog()); + $("#settings").on("show", () => { + $("#session-list").html("

Loading…

"); + socket.emit("sessions:get"); + }); + $("#play").on("click", () => { const pop = new Audio(); pop.src = "audio/pop.ogg"; @@ -33,9 +38,7 @@ socket.on("configuration", function(data) { options.processSetting("theme", data.defaultTheme, true); } - const forms = $("#connect form, #change-password form"); - - forms.on("submit", function() { + function handleFormSubmit() { const form = $(this); const event = form.data("event"); @@ -51,27 +54,34 @@ socket.on("configuration", function(data) { socket.emit(event, values); return false; + } + + $("#change-password form").on("submit", handleFormSubmit); + connect.on("submit", "form", handleFormSubmit); + + connect.on("show", function() { + connect + .html(templates.windows.connect(data)) + .find("#connect\\:nick") + .on("focusin", function() { + // Need to set the first "lastvalue", so it can be used in the below function + const nick = $(this); + nick.data("lastvalue", nick.val()); + }) + .on("input", function() { + const nick = $(this).val(); + const usernameInput = connect.find(".username"); + + // Because this gets called /after/ it has already changed, we need use the previous value + const lastValue = $(this).data("lastvalue"); + + // They were the same before the change, so update the username field + if (usernameInput.val() === lastValue) { + usernameInput.val(nick); + } + + // Store the "previous" value, for next time + $(this).data("lastvalue", nick); + }); }); - - $(".nick") - .on("focusin", function() { - // Need to set the first "lastvalue", so it can be used in the below function - const nick = $(this); - nick.data("lastvalue", nick.val()); - }) - .on("input", function() { - const nick = $(this).val(); - const usernameInput = forms.find(".username"); - - // Because this gets called /after/ it has already changed, we need use the previous value - const lastValue = $(this).data("lastvalue"); - - // They were the same before the change, so update the username field - if (usernameInput.val() === lastValue) { - usernameInput.val(nick); - } - - // Store the "previous" value, for next time - $(this).data("lastvalue", nick); - }); }); diff --git a/client/js/socket-events/network.js b/client/js/socket-events/network.js index af294d4c..e012d211 100644 --- a/client/js/socket-events/network.js +++ b/client/js/socket-events/network.js @@ -3,6 +3,7 @@ const $ = require("jquery"); const socket = require("../socket"); const render = require("../render"); +const templates = require("../../views"); const sidebar = $("#sidebar"); socket.on("network", function(data) { @@ -27,3 +28,17 @@ socket.on("network:status", function(data) { .toggleClass("not-connected", !data.connected) .toggleClass("not-secure", !data.secure); }); + +socket.on("network:info", function(data) { + $("#connect") + .html(templates.windows.connect(data)) + .find("form").on("submit", function() { + const uuid = $(this).find("input[name=uuid]").val(); + const newName = $(this).find("#connect\\:name").val(); + + sidebar.find(`.network[data-uuid="${uuid}"] .chan.lobby .name`) + .attr("title", newName) + .text(newName) + .click(); + }); +}); diff --git a/client/views/windows/connect.tpl b/client/views/windows/connect.tpl index 56ab219d..bb7bef78 100644 --- a/client/views/windows/connect.tpl +++ b/client/views/windows/connect.tpl @@ -1,20 +1,26 @@
-
+

- {{#if public}}The Lounge - {{/if}} - Connect - {{#unless displayNetwork}} - {{#if lockNetwork}} - to {{defaults.name}} - {{/if}} - {{/unless}} + {{#if defaults.uuid}} + + + Edit {{defaults.name}} + {{else}} + {{#if public}}The Lounge - {{/if}} + Connect + {{#unless displayNetwork}} + {{#if lockNetwork}} + to {{defaults.name}} + {{/if}} + {{/unless}} + {{/if}}

- {{#if displayNetwork}} + {{#if displayNetwork}}

Network settings

@@ -29,7 +35,7 @@
- +
@@ -51,7 +57,7 @@
- {{/if}} + {{/if}}

User preferences

@@ -59,16 +65,16 @@
- +
- {{#unless useHexIp}} + {{#unless useHexIp}}
- {{/unless}} + {{/unless}}
@@ -81,6 +87,18 @@
+ {{#if defaults.uuid}} +
+ +
+
+ +
+
+ +
+ {{else}}
@@ -90,5 +108,6 @@
+ {{/if}}
diff --git a/src/models/network.js b/src/models/network.js index 5696a5ee..70fffd21 100644 --- a/src/models/network.js +++ b/src/models/network.js @@ -164,6 +164,63 @@ Network.prototype.createWebIrc = function(client) { }; }; +Network.prototype.edit = function(client, args) { + const oldNick = this.nick; + + this.nick = args.nick; + this.host = String(args.host || ""); + this.name = String(args.name || "") || this.host; + this.port = parseInt(args.port, 10); + this.tls = !!args.tls; + this.rejectUnauthorized = !!args.rejectUnauthorized; + this.password = String(args.password || ""); + this.username = String(args.username || ""); + this.realname = String(args.realname || ""); + + // Split commands into an array + this.commands = String(args.commands || "") + .replace(/\r\n|\r|\n/g, "\n") + .split("\n") + .filter((command) => command.length > 0); + + // Sync lobby channel name + this.channels[0].name = this.name; + + if (!this.validate(client)) { + return; + } + + if (this.irc) { + if (this.nick !== oldNick) { + if (this.irc.connection && this.irc.connection.connected) { + // Send new nick straight away + this.irc.raw("NICK", this.nick); + } else { + this.irc.options.nick = this.irc.user.nick = this.nick; + + // Update UI nick straight away if IRC is not connected + client.emit("nick", { + network: this.id, + nick: this.nick, + }); + } + } + + this.irc.options.host = this.host; + this.irc.options.port = this.port; + this.irc.options.password = this.password; + this.irc.options.gecos = this.irc.user.gecos = this.realname; + this.irc.options.tls = this.tls; + this.irc.options.rejectUnauthorized = this.rejectUnauthorized; + + if (!Helper.config.useHexIp) { + this.irc.options.username = this.irc.user.username = this.username; + } + } + + client.save(); +}; + Network.prototype.destroy = function() { this.channels.forEach((channel) => channel.destroy()); }; diff --git a/src/server.js b/src/server.js index f859bf62..f75e56a0 100644 --- a/src/server.js +++ b/src/server.js @@ -301,7 +301,7 @@ function initializeClient(socket, client, token, lastMessage) { } }); - socket.on("conn", (data) => { + socket.on("network:new", (data) => { if (typeof data === "object") { // prevent people from overriding webirc settings data.ip = null; diff --git a/test/server.js b/test/server.js index 35fa66b0..cf4f3895 100644 --- a/test/server.js +++ b/test/server.js @@ -88,7 +88,7 @@ describe("Server", function() { it("should create network", (done) => { client.on("init", () => { - client.emit("conn", { + client.emit("network:new", { username: "test-user", realname: "The Lounge Test", nick: "test-user",