Add whois and conversation as action to user contextmenu

Add Op specific actions to contextMenu

Show context menu when left clicking user

Switch to using data attributes as selectors

remove ban as possible action

Move `isOpInChannel()` to utils.js

Capitalize strings

use CSS.escape for `ownNick`

use string interpolation

properly point to findCurrentNetworkChan

Move context menu item actions to command pattern

add icons for context menu actions

Make list in context menu always list.

remove empty lines in style.css

use info circle instead of question circle

change context menu labels.

change contextMenuActions.execute to more explicit method.
This commit is contained in:
Erik Vosseberg 2017-11-01 10:16:19 +01:00 committed by Jérémie Astori
parent 9b75b5727a
commit 33d865501d
No known key found for this signature in database
GPG Key ID: B9A4F245CD67BDE8
5 changed files with 101 additions and 32 deletions

View File

@ -224,6 +224,9 @@ kbd {
.context-menu-chan::before { content: "\f292"; /* http://fontawesome.io/icon/hashtag/ */ } .context-menu-chan::before { content: "\f292"; /* http://fontawesome.io/icon/hashtag/ */ }
.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/ */ }
.context-menu-action-whois::before { content: "\f05a"; /* http://fontawesome.io/icon/info-circle/ */ }
.context-menu-action-query::before { content: "\f0e6"; /* http://fontawesome.io/icon/comments-o/ */ }
.context-menu-action-kick::before { content: "\f05e"; /* http://fontawesome.io/icon/ban/ */ }
.context-menu-network::before, .context-menu-network::before,
#sidebar .chan.lobby::before, #sidebar .chan.lobby::before,

View File

@ -79,9 +79,34 @@ $(function() {
if (target.hasClass("user")) { if (target.hasClass("user")) {
output = templates.contextmenu_item({ output = templates.contextmenu_item({
class: "user", class: "user",
action: "whois",
text: target.text(), text: target.text(),
data: target.data("name"), data: target.data("name"),
}); });
output += templates.contextmenu_divider();
output += templates.contextmenu_item({
class: "action-whois",
action: "whois",
text: "User information",
data: target.data("name"),
});
output += templates.contextmenu_item({
class: "action-query",
action: "query",
text: "Direct messages",
data: target.data("name"),
});
const channel = target.closest(".chan");
if (utils.isOpInChannel(channel) && channel.data("type") === "channel") {
output += templates.contextmenu_divider();
output += templates.contextmenu_item({
class: "action-kick",
action: "kick",
text: "Kick",
data: target.data("name"),
});
}
} else if (target.hasClass("chan")) { } else if (target.hasClass("chan")) {
let itemClass; let itemClass;
@ -95,6 +120,7 @@ $(function() {
output = templates.contextmenu_item({ output = templates.contextmenu_item({
class: itemClass, class: itemClass,
action: "focusChan",
text: target.data("title"), text: target.data("title"),
data: target.data("target"), data: target.data("target"),
}); });
@ -102,12 +128,14 @@ $(function() {
if (target.hasClass("lobby")) { if (target.hasClass("lobby")) {
output += templates.contextmenu_item({ output += templates.contextmenu_item({
class: "list", class: "list",
action: "list",
text: "List all channels", text: "List all channels",
data: target.data("target"), data: target.data("id"),
}); });
} }
output += templates.contextmenu_item({ output += templates.contextmenu_item({
class: "close", class: "close",
action: "close",
text: target.hasClass("lobby") ? "Disconnect" : target.hasClass("channel") ? "Leave" : "Close", text: target.hasClass("lobby") ? "Disconnect" : target.hasClass("channel") ? "Leave" : "Close",
data: target.data("target"), data: target.data("target"),
}); });
@ -121,7 +149,11 @@ $(function() {
return false; return false;
} }
viewport.on("contextmenu", ".user, .network .chan", function(e) { viewport.on("contextmenu", ".network .chan", function(e) {
return showContextMenu(this, e);
});
viewport.on("click contextmenu", ".user", function(e) {
return showContextMenu(this, e); return showContextMenu(this, e);
}); });
@ -285,20 +317,6 @@ $(function() {
$(this).closest(".msg.condensed").toggleClass("closed"); $(this).closest(".msg.condensed").toggleClass("closed");
}); });
chat.on("click", ".user", function() {
var name = $(this).data("name");
var chan = utils.findCurrentNetworkChan(name);
if (chan.length) {
chan.click();
}
socket.emit("input", {
target: chat.data("id"),
text: "/whois " + name,
});
});
sidebar.on("click", ".chan, button", function(e, data) { sidebar.on("click", ".chan, button", function(e, data) {
// Pushes states to history web API when clicking elements with a data-target attribute. // 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 // States are very trivial and only contain a single `clickTarget` property which
@ -447,24 +465,60 @@ $(function() {
return false; return false;
}); });
contextMenu.on("click", ".context-menu-item", function() { const contextMenuActions = {
switch ($(this).data("action")) { close: function(itemData) {
case "close": $(`.networks .chan[data-target="${itemData}"] .close`).click();
$(".networks .chan[data-target='" + $(this).data("data") + "'] .close").click(); },
break; focusChan: function(itemData) {
case "chan": $(`.networks .chan[data-target="${itemData}"]`).click();
$(".networks .chan[data-target='" + $(this).data("data") + "']").click(); },
break; list: function(itemData) {
case "user":
$(".channel.active .users .user[data-name='" + $(this).data("data") + "']").click();
break;
case "list":
socket.emit("input", { socket.emit("input", {
target: chat.data("id"), target: itemData,
text: "/list", text: "/list",
}); });
break; },
} whois: function(itemData) {
const chan = utils.findCurrentNetworkChan(itemData);
if (chan.length) {
chan.click();
}
socket.emit("input", {
target: $("#chat").data("id"),
text: "/whois " + itemData,
});
$(`.channel.active .users .user[data-name="${itemData}"]`).click();
},
query: function(itemData) {
const chan = utils.findCurrentNetworkChan(itemData);
if (chan.length) {
chan.click();
}
socket.emit("input", {
target: $("#chat").data("id"),
text: "/query " + itemData,
});
},
kick: function(itemData) {
socket.emit("input", {
target: $("#chat").data("id"),
text: "/kick " + itemData,
});
},
};
contextMenuActions.execute = (name, ...args) => contextMenuActions[name] && contextMenuActions[name](...args);
contextMenu.on("click", ".context-menu-item", function() {
const $this = $(this);
const itemData = $this.data("data");
const contextAction = $this.data("action");
contextMenuActions.execute(contextAction, itemData);
}); });
chat.on("input", ".search", function() { chat.on("input", ".search", function() {

View File

@ -1,6 +1,7 @@
"use strict"; "use strict";
const $ = require("jquery"); const $ = require("jquery");
const escape = require("css.escape");
const input = $("#input"); const input = $("#input");
var serverHash = -1; var serverHash = -1;
@ -19,6 +20,7 @@ module.exports = {
toggleNickEditor, toggleNickEditor,
toggleNotificationMarkers, toggleNotificationMarkers,
requestIdleCallback, requestIdleCallback,
isOpInChannel,
}; };
function findCurrentNetworkChan(name) { function findCurrentNetworkChan(name) {
@ -37,6 +39,15 @@ function resetHeight(element) {
element.style.height = element.style.minHeight; element.style.height = element.style.minHeight;
} }
// Given a channel element will determine if the lounge user is Op in that channel
function isOpInChannel(channel) {
const channelID = channel.data("id");
const network = $("#sidebar .network").has(`.chan[data-id="${channelID}"]`);
const ownNick = network.data("nick");
const isOP = channel.find(`.users .user-mode.op .user[data-name="${escape(ownNick)}"]`).length;
return isOP;
}
// Triggering click event opens the virtual keyboard on mobile // Triggering click event opens the virtual keyboard on mobile
// This can only be called from another interactive event (e.g. button click) // This can only be called from another interactive event (e.g. button click)
function forceFocus() { function forceFocus() {

View File

@ -1,3 +1,3 @@
<li class="context-menu-item context-menu-{{class}}" data-action="{{class}}"{{#if data}} data-data="{{data}}"{{/if}}> <li class="context-menu-item context-menu-{{class}}" data-action="{{action}}"{{#if data}} data-data="{{data}}"{{/if}}>
{{text}} {{text}}
</li> </li>

View File

@ -48,5 +48,6 @@ exports.input = function(network, chan, cmd, args) {
this.emit("join", { this.emit("join", {
network: network.id, network: network.id,
chan: newChan.getFilteredClone(true), chan: newChan.getFilteredClone(true),
shouldOpen: true,
}); });
}; };