From e719e4ff81edd4d1bfb59981454f2a990fc4970a Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Sun, 4 Mar 2018 22:03:11 +0200 Subject: [PATCH] Cleanup chat/userlist to use flexbox, fix a couple of bugs --- client/css/style.css | 76 ++++++++++++++-------------------- client/js/autocompletion.js | 2 +- client/js/lounge.js | 11 ++--- client/js/render.js | 8 ++-- client/js/socket-events/msg.js | 2 +- client/js/userlist.js | 38 ++++++++--------- client/js/utils.js | 2 +- client/themes/crypto.css | 2 +- client/themes/morning.css | 4 +- client/themes/zenburn.css | 4 +- client/views/chat.tpl | 22 +++++----- 11 files changed, 76 insertions(+), 95 deletions(-) diff --git a/client/css/style.css b/client/css/style.css index 6edd61a4..5c49ce01 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -498,8 +498,8 @@ kbd { float: right; } -#viewport.rt #chat .sidebar { - right: -180px; +#viewport.rt #chat .userlist { + display: none; } #sidebar { @@ -954,7 +954,8 @@ button.collapse-network:first-child:nth-last-child(3) { } #chat .chan.active { - display: block; + display: flex; + flex-direction: column; } #chat .condensed { @@ -1006,48 +1007,40 @@ button.collapse-network:first-child:nth-last-child(3) { #windows #form .input, .messages .msg, -.sidebar { +.userlist { font-size: 14px; line-height: 1.4; } #windows #chat .header { display: flex; + flex-shrink: 0; } -#chat .chat, -#chat .sidebar { - top: 48px; +#chat .chat-content { + display: flex; + flex-grow: 1; } #chat .chat { - bottom: 0; - left: 0; - right: 0; overflow: auto; - will-change: transform, scroll-position; - -webkit-overflow-scrolling: touch; - position: absolute; display: flex; + flex-grow: 1; flex-direction: column; -} - -#chat .channel .chat { - right: 180px; + -webkit-overflow-scrolling: touch; } #viewport.rt .chat { right: 0; } -#chat .sidebar { +#chat .userlist { background: #fff; border-left: 1px solid #e7e7e7; - bottom: 0; - position: absolute; - right: 0; width: 180px; - transition: right 0.4s; + display: flex; + flex-direction: column; + flex-shrink: 0; touch-action: pan-y; } @@ -1456,16 +1449,13 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ display: none; } -#chat .count { +#chat .userlist .count { background: #fafafa; height: 48px; - left: 0; - position: absolute; - right: 0; - top: 0; + flex-shrink: 0; } -#chat .search { +#chat .userlist .search { color: #222; border: 0; background: none; @@ -1476,17 +1466,14 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ width: 100%; } -#chat .names { - bottom: 0; +#chat .userlist .names { + flex-grow: 1; overflow: auto; overflow-x: hidden; - will-change: transform, scroll-position; - -webkit-overflow-scrolling: touch; padding-bottom: 10px; - position: absolute; - top: 48px; width: 100%; touch-action: pan-y; + -webkit-overflow-scrolling: touch; } #chat .names-filtered { @@ -1973,7 +1960,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ .context-menu-item:hover, .textcomplete-item:hover, .textcomplete-menu .active, -#chat .users .user.active { +#chat .userlist .user.active { background-color: #f6f6f6; transition: none; } @@ -2320,10 +2307,6 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ left: 0; } - #chat .chat { - right: 0; - } - #viewport .lt, #viewport .channel .rt { display: flex; @@ -2333,16 +2316,17 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */ display: block; } - #chat .channel .chat { + #chat .userlist { + height: 100%; + position: absolute; right: 0; + transform: translateX(180px); + transition: transform 0.2s; } - #chat .sidebar { - right: -180px; - } - - #viewport.rt #chat .sidebar { - right: 0; + #viewport.rt #chat .userlist { + display: flex; + transform: translateX(0); } #chat .header .title { diff --git a/client/js/autocompletion.js b/client/js/autocompletion.js index 1b95d009..079bc7b7 100644 --- a/client/js/autocompletion.js +++ b/client/js/autocompletion.js @@ -263,7 +263,7 @@ function fuzzyGrep(term, array) { function rawNicks() { const chan = chat.find(".active"); - const users = chan.find(".users"); + const users = chan.find(".userlist"); // If this channel has a list of nicks, just return it if (users.length > 0) { diff --git a/client/js/lounge.js b/client/js/lounge.js index cbe2852b..0cc67053 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -41,11 +41,12 @@ $(function() { } }); - viewport.on("click", ".rt", function(e) { + viewport.on("click", ".rt", function() { const self = $(this); viewport.toggleClass(self.prop("class")); - e.stopPropagation(); - chat.find(".chan.active .chat").trigger("msg.sticky"); + chat.find(".chan.active .chat").trigger("keepToBottom"); + + return false; }); function positionContextMenu(that, e) { @@ -212,7 +213,7 @@ $(function() { + Math.round(parseFloat(style.borderBottomWidth) || 0) ) + "px"; - chat.find(".chan.active .chat").trigger("msg.sticky"); // fix growing + chat.find(".chan.active .chat").trigger("keepToBottom"); // fix growing }); let focus = $.noop; @@ -553,7 +554,7 @@ $(function() { text: "/whois " + itemData, }); - $(`.channel.active .users .user[data-name="${itemData}"]`).trigger("click"); + $(`.channel.active .userlist .user[data-name="${itemData}"]`).trigger("click"); }, query: function(itemData) { const chan = utils.findCurrentNetworkChan(itemData); diff --git a/client/js/render.js b/client/js/render.js index cd012a3e..95dc8c2a 100644 --- a/client/js/render.js +++ b/client/js/render.js @@ -158,7 +158,7 @@ function renderUnreadMarker(template, firstUnread, channel) { } function renderChannelUsers(data) { - const users = chat.find("#chan-" + data.id).find(".users"); + const users = chat.find("#chan-" + data.id).find(".userlist"); const nicks = data.users .concat() // Make a copy of the user list, sort is applied in-place .sort((a, b) => b.lastMessage - a.lastMessage) @@ -167,7 +167,7 @@ function renderChannelUsers(data) { // Before re-rendering the list of names, there might have been an entry // marked as active (i.e. that was highlighted by keyboard navigation). // It is `undefined` if there was none. - const previouslyActive = users.find(".active").data("name"); + const previouslyActive = users.find(".active"); const search = users .find(".search") @@ -185,11 +185,11 @@ function renderChannelUsers(data) { // If a nick was highlighted before re-rendering the lists, re-highlight it in // the newly-rendered list. - if (previouslyActive) { + if (previouslyActive.length > 0) { // We need to un-highlight everything first because triggering `input` with // a value highlights the first entry. users.find(".user").removeClass("active"); - users.find(`.user[data-name="${previouslyActive}"]`).addClass("active"); + users.find(`.user[data-name="${previouslyActive.data("name")}"]`).addClass("active"); } return users; diff --git a/client/js/socket-events/msg.js b/client/js/socket-events/msg.js index 2fbc6482..c8f8162b 100644 --- a/client/js/socket-events/msg.js +++ b/client/js/socket-events/msg.js @@ -102,7 +102,7 @@ function processReceivedMessage(data) { } if ((data.msg.type === "message" || data.msg.type === "action") && channel.hasClass("channel")) { - const nicks = channel.find(".users").data("nicks"); + const nicks = channel.find(".userlist").data("nicks"); if (nicks) { const find = nicks.indexOf(data.msg.from.nick); diff --git a/client/js/userlist.js b/client/js/userlist.js index 93874a5b..90595e3f 100644 --- a/client/js/userlist.js +++ b/client/js/userlist.js @@ -8,9 +8,9 @@ const templates = require("../views"); const chat = $("#chat"); -chat.on("input", ".users .search", function() { +chat.on("input", ".userlist .search", function() { const value = $(this).val(); - const parent = $(this).closest(".users"); + const parent = $(this).closest(".userlist"); const names = parent.find(".names-original"); const container = parent.find(".names-filtered"); @@ -42,22 +42,24 @@ chat.on("input", ".users .search", function() { container.find(".user").first().addClass("active"); }); -chat.on("mouseenter", ".users .user", function() { +chat.on("mouseenter", ".userlist .user", function() { // Reset any potential selection, this is required in cas there is already a // nick previously selected by keyboard - $(".users .user").removeClass("active"); + $(this).parent().find(".user.active").removeClass("active"); $(this).addClass("active"); }); -chat.on("mouseleave", ".users .user", function() { +chat.on("mouseleave", ".userlist .user", function() { // Reset any potential selection - $(".users .user").removeClass("active"); + $(this).parent().find(".user.active").removeClass("active"); }); exports.handleKeybinds = function(input) { - Mousetrap(input.get(0)).bind(["up", "down"], (_e, key) => { - const userlists = input.closest(".users"); + Mousetrap(input.get(0)).bind(["up", "down"], (e, key) => { + e.preventDefault(); + + const userlists = input.closest(".userlist"); let userlist; // If input field has content, use the filtered list instead @@ -69,13 +71,17 @@ exports.handleKeybinds = function(input) { const users = userlist.find(".user"); + if (users.length === 0) { + return; + } + // Find which item in the array of users is currently selected, if any. // Returns -1 if none. const activeIndex = users.toArray() .findIndex((user) => user.classList.contains("active")); // Now that we know which user is active, reset any selection - userlists.find(".user").removeClass("active"); + userlist.find(".user.active").removeClass("active"); // Mark next/previous user as active. if (key === "down") { @@ -87,23 +93,13 @@ exports.handleKeybinds = function(input) { } // Adjust scroll when active item is outside of the visible area - const userlistHeight = userlist.height(); - const userlistScroll = userlist.scrollTop(); - const active = $(".user.active"); - const activeTop = active.position().top; - const activeHeight = active.height(); - - if (activeTop > userlistHeight - activeHeight) { - userlist.scrollTop(userlistScroll + activeTop - userlistHeight + activeHeight); - } else if (activeTop < 0) { - userlist.scrollTop(userlistScroll + activeTop - activeHeight); - } + userlist.find(".user.active")[0].scrollIntoView(false); }); // When pressing Enter, open the context menu (emit a click) on the active // user Mousetrap(input.get(0)).bind("enter", () => { - const user = input.closest(".users").find(".user.active"); + const user = input.closest(".userlist").find(".user.active"); if (user.length) { const clickEvent = new $.Event("click"); diff --git a/client/js/utils.js b/client/js/utils.js index ea1574a5..6684ae71 100644 --- a/client/js/utils.js +++ b/client/js/utils.js @@ -48,7 +48,7 @@ function hasRoleInChannel(channel, roles) { const channelID = channel.data("id"); const network = $("#sidebar .network").has(`.chan[data-id="${channelID}"]`); const ownNick = network.data("nick"); - const user = channel.find(`.users .user[data-name="${escape(ownNick)}"]`).first(); + const user = channel.find(`.names-original .user[data-name="${escape(ownNick)}"]`).first(); return user.parent().is("." + roles.join(", .")); } diff --git a/client/themes/crypto.css b/client/themes/crypto.css index 239b4fc2..e34671c3 100644 --- a/client/themes/crypto.css +++ b/client/themes/crypto.css @@ -91,7 +91,7 @@ a:hover, #windows .header .topic, .messages .msg, -.sidebar { +.userlist { line-height: 1.8; } diff --git a/client/themes/morning.css b/client/themes/morning.css index 8f5db662..6b646f52 100644 --- a/client/themes/morning.css +++ b/client/themes/morning.css @@ -31,7 +31,7 @@ body { } #main, -#chat .sidebar, +#chat .userlist, #windows .chan, #windows .window { background: #333c4a; @@ -50,7 +50,7 @@ body { #chat .content, #windows .header, #chat .user-mode::before, -#chat .sidebar { +#chat .userlist { border-color: #2a323d; } diff --git a/client/themes/zenburn.css b/client/themes/zenburn.css index 3eb02766..3c1e7ee0 100644 --- a/client/themes/zenburn.css +++ b/client/themes/zenburn.css @@ -32,7 +32,7 @@ body { } #main, -#chat .sidebar, +#chat .userlist, #windows .chan, #windows .window { background: #3f3f3f; @@ -76,7 +76,7 @@ body { #chat .content, #windows .header, #chat .user-mode::before, -#chat .sidebar { +#chat .userlist { border-color: #333; } diff --git a/client/views/chat.tpl b/client/views/chat.tpl index 2ffe65ea..dbd4d1c0 100644 --- a/client/views/chat.tpl +++ b/client/views/chat.tpl @@ -18,22 +18,22 @@ {{/equal}} -
-
- +
+
+
+ +
+
-
-
- {{#equal type "channel"}} - - {{/equal}} + + {{/equal}} +
{{/each}}