Merge pull request #1836 from MaxLeiter/join_channel

Join channel UI
This commit is contained in:
Jérémie Astori 2017-12-22 13:13:34 -05:00 committed by GitHub
commit 5490235f4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 176 additions and 5 deletions

View File

@ -222,6 +222,7 @@ kbd {
#viewport .rt::before { content: "\f0c0"; /* http://fontawesome.io/icon/users/ */ } #viewport .rt::before { content: "\f0c0"; /* http://fontawesome.io/icon/users/ */ }
#chat button.menu::before { content: "\f142"; /* http://fontawesome.io/icon/ellipsis-v/ */ } #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-user::before { content: "\f007"; /* http://fontawesome.io/icon/user/ */ }
.context-menu-close::before { content: "\f00d"; /* http://fontawesome.io/icon/times/ */ } .context-menu-close::before { content: "\f00d"; /* http://fontawesome.io/icon/times/ */ }
.context-menu-list::before { content: "\f03a"; /* http://fontawesome.io/icon/list/ */ } .context-menu-list::before { content: "\f03a"; /* http://fontawesome.io/icon/list/ */ }
@ -457,7 +458,6 @@ kbd {
will-change: transform; will-change: transform;
} }
#sidebar button,
#sidebar .chan, #sidebar .chan,
#sidebar .sign-out, #sidebar .sign-out,
#sidebar .empty { #sidebar .empty {
@ -551,6 +551,7 @@ kbd {
} }
#sidebar .badge, #sidebar .badge,
#sidebar .add-channel,
#sidebar .close { #sidebar .close {
float: right; float: right;
margin-left: 5px; margin-left: 5px;
@ -577,7 +578,6 @@ kbd {
} }
#sidebar .close { #sidebar .close {
border-radius: 3px;
width: 18px; width: 18px;
height: 18px; height: 18px;
display: none; display: none;
@ -594,13 +594,38 @@ kbd {
color: #fff; color: #fff;
} }
#sidebar .lobby .add-channel {
border-radius: 3px;
width: 18px;
height: 18px;
opacity: 0.4;
transition: opacity 0.2s, background-color 0.2s, transform 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:hover {
opacity: 1;
}
#sidebar .lobby .add-channel.opened {
transform: rotate(45deg);
}
#sidebar .chan.active .close { #sidebar .chan.active .close {
opacity: 0.4; opacity: 0.4;
display: unset; display: unset;
} }
#sidebar .chan.active .close:hover { #sidebar .chan.active .close:hover {
background-color: rgba(0, 0, 0, 0.1);
opacity: 1; opacity: 1;
} }
@ -685,7 +710,7 @@ kbd {
font-size: 14px; font-size: 14px;
} }
#windows .input { .input {
background-color: white; background-color: white;
border: 1px solid #cdd3da; border: 1px solid #cdd3da;
border-radius: 2px; border-radius: 2px;
@ -894,6 +919,35 @@ kbd {
touch-action: pan-y; 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 { #chat .show-more {
display: none; display: none;
padding: 10px; padding: 10px;

View File

@ -70,7 +70,7 @@
<div class="input"> <div class="input">
<span id="nick"> <span id="nick">
<span id="nick-value" spellcheck="false"></span><!-- Comments here remove spaces between elements <span id="nick-value" spellcheck="false"></span><!-- Comments here remove spaces between elements
--><span id="set-nick-tooltip" class="tooltipped tooltipped-e" aria-label="Change nick"><button id="set-nick" type="button" aria-label="Change nick"></button></span><!-- --><span id="set-nick-tooltip" class="tooltipped tooltipped-e" aria-label="Change nick"><button id="set-nick" type="button" aria-label="Change nick"></button></span><!--
--><span id="cancel-nick-tooltip" class="tooltipped tooltipped-e" aria-label="Cancel"><button id="cancel-nick" type="button" aria-label="Cancel"></button></span><!-- --><span id="cancel-nick-tooltip" class="tooltipped tooltipped-e" aria-label="Cancel"><button id="cancel-nick" type="button" aria-label="Cancel"></button></span><!--
--><span id="save-nick-tooltip" class="tooltipped tooltipped-e" aria-label="Save"><button id="submit-nick" type="button" aria-label="Save"></button></span> --><span id="save-nick-tooltip" class="tooltipped tooltipped-e" aria-label="Save"><button id="submit-nick" type="button" aria-label="Save"></button></span>
</span> </span>

88
client/js/join-channel.js Normal file
View File

@ -0,0 +1,88 @@
"use strict";
const $ = require("jquery");
const Mousetrap = require("mousetrap");
const socket = require("./socket");
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");
// 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");
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) {
const id = $(e.target).data("id");
const joinForm = $(`#join-channel-${id}`);
const network = joinForm.closest(".network");
if (joinForm.is(":visible")) {
closeForm(network);
} else {
openForm(network);
}
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"),
});
}
closeForm(form.closest(".network"));
return false;
});
function handleKeybinds() {
sidebar.find(".join-form input, .join-form button").each(function() {
const network = $(this).closest(".network");
Mousetrap(this).bind("esc", () => closeForm(network));
});
}

View File

@ -20,6 +20,7 @@ const utils = require("./utils");
require("./webpush"); require("./webpush");
require("./keybinds"); require("./keybinds");
require("./clipboard"); require("./clipboard");
const JoinChannel = require("./join-channel");
$(function() { $(function() {
var sidebar = $("#sidebar, #footer"); var sidebar = $("#sidebar, #footer");
@ -132,6 +133,12 @@ $(function() {
text: "List all channels", text: "List all channels",
data: target.data("id"), data: target.data("id"),
}); });
output += templates.contextmenu_item({
class: "join",
action: "join",
text: "Join a channel…",
data: target.data("id"),
});
} }
if (target.hasClass("channel")) { if (target.hasClass("channel")) {
output += templates.contextmenu_item({ output += templates.contextmenu_item({
@ -478,6 +485,10 @@ $(function() {
}); });
const contextMenuActions = { const contextMenuActions = {
join: function(itemData) {
const network = $(`#join-channel-${itemData}`).closest(".network");
JoinChannel.openForm(network);
},
close: function(itemData) { close: function(itemData) {
closeChan($(`.networks .chan[data-target="${itemData}"]`)); closeChan($(`.networks .chan[data-target="${itemData}"]`));
}, },

View File

@ -8,6 +8,7 @@ const utils = require("./utils");
const sorting = require("./sorting"); const sorting = require("./sorting");
const constants = require("./constants"); const constants = require("./constants");
const condensed = require("./condensed"); const condensed = require("./condensed");
const JoinChannel = require("./join-channel");
const helpers_parse = require("./libs/handlebars/parse"); const helpers_parse = require("./libs/handlebars/parse");
const chat = $("#chat"); 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; let newChannels;
const channels = $.map(data.networks, function(n) { const channels = $.map(data.networks, function(n) {
return n.channels; return n.channels;

View File

@ -1,7 +1,15 @@
{{#each channels}} {{#each channels}}
<div data-id="{{id}}" data-target="#chan-{{id}}" data-title="{{name}}" class="chan {{type}} chan-{{slugify name}}"> <div data-id="{{id}}" data-target="#chan-{{id}}" data-title="{{name}}" class="chan {{type}} chan-{{slugify name}}">
{{#equal type "lobby"}}
<span class="add-channel-tooltip tooltipped tooltipped-w tooltipped-no-touch" aria-label="Join a channel…" data-alt-label="Cancel">
<button class="add-channel" aria-label="Join a channel…" data-id="{{id}}"></button>
</span>
{{/equal}}
<span class="badge{{#if highlight}} highlight{{/if}}">{{#if unread}}{{roundBadgeNumber unread}}{{/if}}</span> <span class="badge{{#if highlight}} highlight{{/if}}">{{#if unread}}{{roundBadgeNumber unread}}{{/if}}</span>
{{#notEqual type "lobby"}}<button class="close" aria-label="Close"></button>{{/notEqual}} {{#notEqual type "lobby"}}<button class="close" aria-label="Close"></button>{{/notEqual}}
<span class="name" title="{{name}}">{{name}}</span> <span class="name" title="{{name}}">{{name}}</span>
</div> </div>
{{#equal type "lobby"}}
{{> join_channel}}
{{/equal}}
{{/each}} {{/each}}

View File

@ -42,6 +42,7 @@ module.exports = {
msg_unhandled: require("./msg_unhandled.tpl"), msg_unhandled: require("./msg_unhandled.tpl"),
network: require("./network.tpl"), network: require("./network.tpl"),
image_viewer: require("./image_viewer.tpl"), image_viewer: require("./image_viewer.tpl"),
join_channel: require("./join_channel.tpl"),
session: require("./session.tpl"), session: require("./session.tpl"),
unread_marker: require("./unread_marker.tpl"), unread_marker: require("./unread_marker.tpl"),
user: require("./user.tpl"), user: require("./user.tpl"),

View File

@ -0,0 +1,5 @@
<form id="join-channel-{{id}}" class="join-form" method="post" action="" autocomplete="off">
<input type="text" class="input" name="channel" placeholder="Channel" pattern="[^\s]+" maxlength="200" title="Should be a valid channel name" required>
<input type="password" class="input" name="key" placeholder="Password (optional)" pattern="[^\s]+" title="Should be a valid channel key" maxlength="200" >
<button type="submit" class="btn joinchan:submit" data-id="{{id}}">Join</button>
</form>