From 3fde87efbc6779417468303c5b722cbcf9d0a201 Mon Sep 17 00:00:00 2001 From: Max Leiter Date: Tue, 12 Dec 2017 20:52:26 -0800 Subject: [PATCH 1/8] Add join channel UI via context menu or plus button next to lobbys --- client/css/style.css | 60 +++++++++++++++++++++++++++++++++-- client/js/lounge.js | 38 ++++++++++++++++++++++ client/views/chan.tpl | 8 +++++ client/views/index.js | 1 + client/views/join_channel.tpl | 5 +++ 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 client/views/join_channel.tpl diff --git a/client/css/style.css b/client/css/style.css index 215d1b24..110dfd28 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -222,6 +222,7 @@ kbd { #viewport .rt::before { content: "\f0c0"; /* http://fontawesome.io/icon/users/ */ } #chat button.menu::before { content: "\f142"; /* http://fontawesome.io/icon/ellipsis-v/ */ } +.context-menu-join::before { content: "\f067"; /* http://fontawesome.io/icon/plus/ */ } .context-menu-user::before { content: "\f007"; /* http://fontawesome.io/icon/user/ */ } .context-menu-close::before { content: "\f00d"; /* http://fontawesome.io/icon/times/ */ } .context-menu-list::before { content: "\f03a"; /* http://fontawesome.io/icon/list/ */ } @@ -457,7 +458,6 @@ kbd { will-change: transform; } -#sidebar button, #sidebar .chan, #sidebar .sign-out, #sidebar .empty { @@ -551,6 +551,7 @@ kbd { } #sidebar .badge, +#sidebar .add-channel, #sidebar .close { float: right; margin-left: 5px; @@ -594,6 +595,32 @@ kbd { color: #fff; } +#sidebar .lobby .add-channel { + border-radius: 3px; + width: 18px; + height: 18px; + transition: opacity 0.2s, background-color 0.2s; +} + +#sidebar .lobby .add-channel::before { + font-size: 20px; + font-weight: normal; + display: inline-block; + line-height: 16px; + text-align: center; + content: "+"; + color: #fff; +} + +#sidebar .lobby .add-channel { + opacity: 0.4; +} + +#sidebar .lobby .add-channel:hover { + background-color: rgba(0, 0, 0, 0.1); + opacity: 1; +} + #sidebar .chan.active .close { opacity: 0.4; display: unset; @@ -685,7 +712,7 @@ kbd { font-size: 14px; } -#windows .input { +.input { background-color: white; border: 1px solid #cdd3da; border-radius: 2px; @@ -894,6 +921,35 @@ kbd { touch-action: pan-y; } + +/** + * Toggled via JavaScript + */ +#sidebar .join-form { + display: none; +} + +#sidebar .join-form .input { + display: block; + margin-left: auto; + margin-right: auto; + margin-top: 5px; + margin-bottom: 5px; + width: 80%; +} + +#sidebar .join-form .btn { + display: block; + width: 80%; + padding: 1px; + margin: auto; + height: 29px; +} + +#sidebar .add-channel-tooltip { + float: right; +} + #chat .show-more { display: none; padding: 10px; diff --git a/client/js/lounge.js b/client/js/lounge.js index c260bd76..f395508d 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -132,6 +132,12 @@ $(function() { text: "List all channels", data: target.data("id"), }); + output += templates.contextmenu_item({ + class: "join", + action: "join", + text: "Join a channel\u2026", + data: target.data("id"), + }); } if (target.hasClass("channel")) { output += templates.contextmenu_item({ @@ -477,7 +483,39 @@ $(function() { closeChan($(this).closest(".chan")); }); + sidebar.on("click", ".add-channel", (e) => { + const id = $(e.target).data("id"); + const joinForm = $(`#join-channel-${id}`); + joinForm.toggle(); + joinForm.find(".input[name='channel']").focus(); + return false; + }); + + sidebar.on("submit", ".join-form", function() { + const form = $(this); + const channel = form.find("input[name='channel']"); + const channelString = channel.val(); + const key = form.find("input[name='key']"); + const keyString = key.val(); + const chan = utils.findCurrentNetworkChan(channelString); + if (chan.length) { + chan.click(); + } else { + socket.emit("input", { + text: `/join ${channelString} ${keyString}`, + target: form.prev().data("id"), + }); + } + channel.val(""); + key.val(""); + form.hide(); + return false; + }); + const contextMenuActions = { + join: function(itemData) { + $(`#join-channel-${itemData}`).show(); + }, close: function(itemData) { closeChan($(`.networks .chan[data-target="${itemData}"]`)); }, diff --git a/client/views/chan.tpl b/client/views/chan.tpl index 857c599f..f7bc0575 100644 --- a/client/views/chan.tpl +++ b/client/views/chan.tpl @@ -1,7 +1,15 @@ {{#each channels}}
+ {{#equal type "lobby"}} + + + + {{/equal}} {{#if unread}}{{roundBadgeNumber unread}}{{/if}} {{#notEqual type "lobby"}}{{/notEqual}} {{name}}
+{{#equal type "lobby"}} + {{> join_channel}} +{{/equal}} {{/each}} diff --git a/client/views/index.js b/client/views/index.js index 9f5bf6c0..3ed35fba 100644 --- a/client/views/index.js +++ b/client/views/index.js @@ -42,6 +42,7 @@ module.exports = { msg_unhandled: require("./msg_unhandled.tpl"), network: require("./network.tpl"), image_viewer: require("./image_viewer.tpl"), + join_channel: require("./join_channel.tpl"), session: require("./session.tpl"), unread_marker: require("./unread_marker.tpl"), user: require("./user.tpl"), diff --git a/client/views/join_channel.tpl b/client/views/join_channel.tpl new file mode 100644 index 00000000..9f55d271 --- /dev/null +++ b/client/views/join_channel.tpl @@ -0,0 +1,5 @@ +
+ + + +
From 3307ced4d8cc237f5bc30e55b734cd1bd305f940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Thu, 21 Dec 2017 13:11:26 -0500 Subject: [PATCH 2/8] Remove background on close/join-channel sidebar buttons --- client/css/style.css | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/css/style.css b/client/css/style.css index 110dfd28..94d8f7e2 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -578,7 +578,6 @@ kbd { } #sidebar .close { - border-radius: 3px; width: 18px; height: 18px; display: none; @@ -617,7 +616,6 @@ kbd { } #sidebar .lobby .add-channel:hover { - background-color: rgba(0, 0, 0, 0.1); opacity: 1; } @@ -627,7 +625,6 @@ kbd { } #sidebar .chan.active .close:hover { - background-color: rgba(0, 0, 0, 0.1); opacity: 1; } From 522bba694b59702155520ae9afa8f9b4047df68d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Thu, 21 Dec 2017 13:11:49 -0500 Subject: [PATCH 3/8] Use an ellipsis for change nick and join channel tooltips --- client/index.html | 2 +- client/js/lounge.js | 2 +- client/views/chan.tpl | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/index.html b/client/index.html index 7a9789a3..3c3b253e 100644 --- a/client/index.html +++ b/client/index.html @@ -70,7 +70,7 @@
diff --git a/client/js/lounge.js b/client/js/lounge.js index f395508d..8c1f1241 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -135,7 +135,7 @@ $(function() { output += templates.contextmenu_item({ class: "join", action: "join", - text: "Join a channel\u2026", + text: "Join a channel…", data: target.data("id"), }); } diff --git a/client/views/chan.tpl b/client/views/chan.tpl index f7bc0575..939a69eb 100644 --- a/client/views/chan.tpl +++ b/client/views/chan.tpl @@ -1,8 +1,8 @@ {{#each channels}}
{{#equal type "lobby"}} - - + + {{/equal}} {{#if unread}}{{roundBadgeNumber unread}}{{/if}} From 9ea39661409fc2b8851500f164e13b1b42047205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Thu, 21 Dec 2017 13:17:10 -0500 Subject: [PATCH 4/8] Move the join channel form to its own component --- client/js/join-channel.js | 37 +++++++++++++++++++++++++++++++++++++ client/js/lounge.js | 29 ----------------------------- 2 files changed, 37 insertions(+), 29 deletions(-) create mode 100644 client/js/join-channel.js diff --git a/client/js/join-channel.js b/client/js/join-channel.js new file mode 100644 index 00000000..738bf9e9 --- /dev/null +++ b/client/js/join-channel.js @@ -0,0 +1,37 @@ +"use strict"; + +const $ = require("jquery"); + +const socket = require("./socket"); +const utils = require("./utils"); + +const sidebar = $("#sidebar"); + +sidebar.on("click", ".add-channel", (e) => { + const id = $(e.target).data("id"); + const joinForm = $(`#join-channel-${id}`); + joinForm.toggle(); + joinForm.find(".input[name='channel']").focus(); + return false; +}); + +sidebar.on("submit", ".join-form", function() { + const form = $(this); + const channel = form.find("input[name='channel']"); + const channelString = channel.val(); + const key = form.find("input[name='key']"); + const keyString = key.val(); + const chan = utils.findCurrentNetworkChan(channelString); + if (chan.length) { + chan.click(); + } else { + socket.emit("input", { + text: `/join ${channelString} ${keyString}`, + target: form.prev().data("id"), + }); + } + channel.val(""); + key.val(""); + form.hide(); + return false; +}); diff --git a/client/js/lounge.js b/client/js/lounge.js index 8c1f1241..52703016 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -483,35 +483,6 @@ $(function() { closeChan($(this).closest(".chan")); }); - sidebar.on("click", ".add-channel", (e) => { - const id = $(e.target).data("id"); - const joinForm = $(`#join-channel-${id}`); - joinForm.toggle(); - joinForm.find(".input[name='channel']").focus(); - return false; - }); - - sidebar.on("submit", ".join-form", function() { - const form = $(this); - const channel = form.find("input[name='channel']"); - const channelString = channel.val(); - const key = form.find("input[name='key']"); - const keyString = key.val(); - const chan = utils.findCurrentNetworkChan(channelString); - if (chan.length) { - chan.click(); - } else { - socket.emit("input", { - text: `/join ${channelString} ${keyString}`, - target: form.prev().data("id"), - }); - } - channel.val(""); - key.val(""); - form.hide(); - return false; - }); - const contextMenuActions = { join: function(itemData) { $(`#join-channel-${itemData}`).show(); From d2b03854319d5ff030ade8fa299773addc6d882f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Thu, 21 Dec 2017 15:40:50 -0500 Subject: [PATCH 5/8] Add a keyboard handler to close the join form when hitting "Escape" --- client/js/join-channel.js | 19 ++++++++++++++++--- client/js/render.js | 4 ++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/client/js/join-channel.js b/client/js/join-channel.js index 738bf9e9..a8858ffd 100644 --- a/client/js/join-channel.js +++ b/client/js/join-channel.js @@ -1,12 +1,20 @@ "use strict"; const $ = require("jquery"); +const Mousetrap = require("mousetrap"); const socket = require("./socket"); const utils = require("./utils"); const sidebar = $("#sidebar"); +function closeForm(network) { + const form = network.find(".join-form"); + form.find("input[name='channel']").val(""); + form.find("input[name='key']").val(""); + form.hide(); +} + sidebar.on("click", ".add-channel", (e) => { const id = $(e.target).data("id"); const joinForm = $(`#join-channel-${id}`); @@ -30,8 +38,13 @@ sidebar.on("submit", ".join-form", function() { target: form.prev().data("id"), }); } - channel.val(""); - key.val(""); - form.hide(); + closeForm(form.closest(".network")); return false; }); + +exports.handleKeybinds = function() { + sidebar.find(".join-form input, .join-form button").each(function() { + const network = $(this).closest(".network"); + Mousetrap(this).bind("esc", () => closeForm(network)); + }); +}; diff --git a/client/js/render.js b/client/js/render.js index 3d8461cb..17772d82 100644 --- a/client/js/render.js +++ b/client/js/render.js @@ -8,6 +8,7 @@ const utils = require("./utils"); const sorting = require("./sorting"); const constants = require("./constants"); const condensed = require("./condensed"); +const JoinChannel = require("./join-channel"); const helpers_parse = require("./libs/handlebars/parse"); const chat = $("#chat"); @@ -183,6 +184,9 @@ function renderNetworks(data, singleNetwork) { }) ); + // Add keyboard handlers to the "Join a channel…" form inputs/button + JoinChannel.handleKeybinds(); + let newChannels; const channels = $.map(data.networks, function(n) { return n.channels; From a03f894888d487516ab83a508bfaad2ea57b359f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Thu, 21 Dec 2017 16:23:22 -0500 Subject: [PATCH 6/8] Change content of tooltip and button when opening/closing join channel form --- client/css/style.css | 11 ++++++----- client/js/join-channel.js | 27 ++++++++++++++++++++++++--- client/js/lounge.js | 2 +- client/views/chan.tpl | 2 +- 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/client/css/style.css b/client/css/style.css index 94d8f7e2..03dfe793 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -598,7 +598,8 @@ kbd { border-radius: 3px; width: 18px; height: 18px; - transition: opacity 0.2s, background-color 0.2s; + opacity: 0.4; + transition: opacity 0.2s, background-color 0.2s, transform 0.2s; } #sidebar .lobby .add-channel::before { @@ -611,14 +612,14 @@ kbd { color: #fff; } -#sidebar .lobby .add-channel { - opacity: 0.4; -} - #sidebar .lobby .add-channel:hover { opacity: 1; } +#sidebar .lobby .add-channel.opened { + transform: rotate(45deg); +} + #sidebar .chan.active .close { opacity: 0.4; display: unset; diff --git a/client/js/join-channel.js b/client/js/join-channel.js index a8858ffd..811a2e70 100644 --- a/client/js/join-channel.js +++ b/client/js/join-channel.js @@ -8,18 +8,39 @@ const utils = require("./utils"); const sidebar = $("#sidebar"); +function toggleButton(network) { + // Transform the + button to a × + network.find("button.add-channel").toggleClass("opened"); + + // Toggle content of tooltip + const tooltip = network.find(".add-channel-tooltip"); + const altLabel = tooltip.data("alt-label"); + tooltip.data("alt-label", tooltip.attr("aria-label")); + tooltip.attr("aria-label", altLabel); +} + function closeForm(network) { const form = network.find(".join-form"); form.find("input[name='channel']").val(""); form.find("input[name='key']").val(""); form.hide(); + + toggleButton(network); } -sidebar.on("click", ".add-channel", (e) => { +sidebar.on("click", ".add-channel", function(e) { const id = $(e.target).data("id"); const joinForm = $(`#join-channel-${id}`); - joinForm.toggle(); - joinForm.find(".input[name='channel']").focus(); + const network = joinForm.closest(".network"); + + if (joinForm.is(":visible")) { + closeForm(network); + } else { + joinForm.show(); + joinForm.find(".input[name='channel']").focus(); + toggleButton(network); + } + return false; }); diff --git a/client/js/lounge.js b/client/js/lounge.js index 52703016..5bcf4d63 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -485,7 +485,7 @@ $(function() { const contextMenuActions = { join: function(itemData) { - $(`#join-channel-${itemData}`).show(); + $(`#join-channel-${itemData}`).closest(".network").find("button.add-channel").click(); }, close: function(itemData) { closeChan($(`.networks .chan[data-target="${itemData}"]`)); diff --git a/client/views/chan.tpl b/client/views/chan.tpl index 939a69eb..c84608a6 100644 --- a/client/views/chan.tpl +++ b/client/views/chan.tpl @@ -1,7 +1,7 @@ {{#each channels}}
{{#equal type "lobby"}} - + {{/equal}} From 1063d7b1d6bea16d53e495f81f99df26e38280c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Fri, 22 Dec 2017 12:15:40 -0500 Subject: [PATCH 7/8] Mark the channel field of "Join a channel" UI as required --- client/views/join_channel.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/views/join_channel.tpl b/client/views/join_channel.tpl index 9f55d271..fdea0dc1 100644 --- a/client/views/join_channel.tpl +++ b/client/views/join_channel.tpl @@ -1,5 +1,5 @@
- +
From 47f95c234dea7d916a4610d2cfcbdc39c1e58a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Fri, 22 Dec 2017 12:18:10 -0500 Subject: [PATCH 8/8] Call the `openForm` function specifically when clicking on the context menu instead of relying on click handler Not defining a `toggleForm` function has the advantage of "fixing" the fact that clicking "Join a channel..." from the context menu would close it when it was already open --- client/js/join-channel.js | 35 ++++++++++++++++++++++++++--------- client/js/lounge.js | 4 +++- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/client/js/join-channel.js b/client/js/join-channel.js index 811a2e70..ebeea808 100644 --- a/client/js/join-channel.js +++ b/client/js/join-channel.js @@ -8,6 +8,11 @@ const utils = require("./utils"); const sidebar = $("#sidebar"); +module.exports = { + handleKeybinds, + openForm, +}; + function toggleButton(network) { // Transform the + button to a × network.find("button.add-channel").toggleClass("opened"); @@ -21,11 +26,25 @@ function toggleButton(network) { function closeForm(network) { const form = network.find(".join-form"); - form.find("input[name='channel']").val(""); - form.find("input[name='key']").val(""); - form.hide(); - toggleButton(network); + if (form.is(":visible")) { + form.find("input[name='channel']").val(""); + form.find("input[name='key']").val(""); + form.hide(); + toggleButton(network); + } +} + +function openForm(network) { + const form = network.find(".join-form"); + + if (form.is(":hidden")) { + form.show(); + toggleButton(network); + } + + // Focus the "Channel" field even if the form was already open + form.find(".input[name='channel']").focus(); } sidebar.on("click", ".add-channel", function(e) { @@ -36,9 +55,7 @@ sidebar.on("click", ".add-channel", function(e) { if (joinForm.is(":visible")) { closeForm(network); } else { - joinForm.show(); - joinForm.find(".input[name='channel']").focus(); - toggleButton(network); + openForm(network); } return false; @@ -63,9 +80,9 @@ sidebar.on("submit", ".join-form", function() { return false; }); -exports.handleKeybinds = function() { +function handleKeybinds() { sidebar.find(".join-form input, .join-form button").each(function() { const network = $(this).closest(".network"); Mousetrap(this).bind("esc", () => closeForm(network)); }); -}; +} diff --git a/client/js/lounge.js b/client/js/lounge.js index 5bcf4d63..826e6036 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -20,6 +20,7 @@ const utils = require("./utils"); require("./webpush"); require("./keybinds"); require("./clipboard"); +const JoinChannel = require("./join-channel"); $(function() { var sidebar = $("#sidebar, #footer"); @@ -485,7 +486,8 @@ $(function() { const contextMenuActions = { join: function(itemData) { - $(`#join-channel-${itemData}`).closest(".network").find("button.add-channel").click(); + const network = $(`#join-channel-${itemData}`).closest(".network"); + JoinChannel.openForm(network); }, close: function(itemData) { closeChan($(`.networks .chan[data-target="${itemData}"]`));