thelounge/client/js/helpers/contextMenu.js

345 lines
7.1 KiB
JavaScript
Raw Normal View History

2019-11-09 23:21:34 +01:00
"use strict";
2019-11-16 18:24:03 +01:00
2019-11-09 23:21:34 +01:00
import socket from "../socket";
2020-03-16 18:58:40 +01:00
import eventbus from "../eventbus";
2019-11-09 23:21:34 +01:00
export function generateChannelContextMenu($root, channel, network) {
const typeMap = {
lobby: "network",
channel: "chan",
query: "query",
special: "chan",
};
const closeMap = {
lobby: "Remove",
channel: "Leave",
query: "Close",
special: "Close",
};
let items = [
{
label: channel.name,
type: "item",
class: typeMap[channel.type],
2021-06-29 06:47:09 +02:00
link: `/${network.host}/${channel.name}`,
2019-11-09 23:21:34 +01:00
},
{
type: "divider",
},
];
// Add menu items for lobbies
if (channel.type === "lobby") {
items = [
...items,
{
label: "Edit this network…",
type: "item",
class: "edit",
link: `/edit-network/${network.uuid}`,
},
{
label: "Join a channel…",
type: "item",
class: "join",
action: () => (network.isJoinChannelShown = true),
},
{
label: "List all channels",
type: "item",
class: "list",
action: () =>
socket.emit("input", {
target: channel.id,
text: "/list",
}),
},
{
label: "List ignored users",
type: "item",
class: "list",
action: () =>
socket.emit("input", {
target: channel.id,
text: "/ignorelist",
}),
},
network.status.connected
? {
label: "Disconnect",
type: "item",
class: "disconnect",
action: () =>
socket.emit("input", {
target: channel.id,
text: "/disconnect",
}),
}
: {
label: "Connect",
type: "item",
class: "connect",
action: () =>
socket.emit("input", {
target: channel.id,
text: "/connect",
}),
},
];
}
// Add menu items for channels
if (channel.type === "channel") {
2019-11-23 15:26:20 +01:00
items.push({
label: "Edit topic",
type: "item",
class: "edit",
action() {
channel.editTopic = true;
$root.switchToChannel(network, channel);
2019-11-09 23:21:34 +01:00
},
2019-11-23 15:26:20 +01:00
});
items.push({
label: "List banned users",
type: "item",
class: "list",
action() {
socket.emit("input", {
target: channel.id,
text: "/banlist",
});
2019-11-09 23:21:34 +01:00
},
2019-11-23 15:26:20 +01:00
});
2019-11-09 23:21:34 +01:00
}
// Add menu items for queries
if (channel.type === "query") {
2020-11-18 23:57:20 +01:00
items.push(
{
label: "User information",
type: "item",
class: "action-whois",
action() {
$root.switchToChannel(network, channel);
2020-11-18 23:57:20 +01:00
socket.emit("input", {
target: channel.id,
text: "/whois " + channel.name,
});
},
2019-11-09 23:21:34 +01:00
},
2020-11-18 23:57:20 +01:00
{
label: "Ignore user",
type: "item",
class: "action-ignore",
action() {
socket.emit("input", {
target: channel.id,
text: "/ignore " + channel.name,
});
},
}
);
2019-11-09 23:21:34 +01:00
}
if (channel.type === "channel" || channel.type === "query") {
items.push({
label: "Clear history",
type: "item",
class: "clear-history",
action() {
2020-03-16 18:58:40 +01:00
eventbus.emit(
2020-02-25 10:16:05 +01:00
"confirm-dialog",
{
title: "Clear history",
text: `Are you sure you want to clear history for ${channel.name}? This cannot be undone.`,
button: "Clear history",
},
(result) => {
if (!result) {
return;
}
2020-02-25 10:16:05 +01:00
socket.emit("history:clear", {
target: channel.id,
});
}
);
},
});
}
2019-11-09 23:21:34 +01:00
// Add close menu item
items.push({
label: closeMap[channel.type],
type: "item",
class: "close",
action() {
2019-11-23 17:44:23 +01:00
$root.closeChannel(channel);
2019-11-09 23:21:34 +01:00
},
});
return items;
}
export function generateUserContextMenu($root, channel, network, user) {
2020-04-24 09:20:40 +02:00
const currentChannelUser = channel
? channel.users.find((u) => u.nick === network.nick) || {}
: {};
2019-11-09 23:21:34 +01:00
const whois = () => {
2020-04-24 09:20:40 +02:00
const chan = network.channels.find((c) => c.name === user.nick);
2019-11-09 23:21:34 +01:00
if (chan) {
$root.switchToChannel(network, chan);
2019-11-09 23:21:34 +01:00
}
socket.emit("input", {
target: channel.id,
text: "/whois " + user.nick,
});
};
const items = [
{
label: user.nick,
type: "item",
class: "user",
action: whois,
},
{
type: "divider",
},
{
label: "User information",
type: "item",
class: "action-whois",
action: whois,
},
2020-11-18 23:57:20 +01:00
{
label: "Ignore user",
type: "item",
class: "action-ignore",
action() {
socket.emit("input", {
target: channel.id,
text: "/ignore " + user.nick,
});
},
},
2019-11-09 23:21:34 +01:00
{
label: "Direct messages",
type: "item",
class: "action-query",
action() {
const chan = $root.$store.getters.findChannelOnCurrentNetwork(user.nick);
if (chan) {
$root.switchToChannel(network, chan);
2019-11-09 23:21:34 +01:00
}
socket.emit("input", {
target: channel.id,
text: "/query " + user.nick,
});
},
},
];
// Bail because we're in a query or we don't have a special mode.
if (!currentChannelUser.modes || currentChannelUser.modes.length < 1) {
return items;
}
// Names of the modes we are able to change
const modes = {
"~": ["owner", "q"],
"&": ["admin", "a"],
"@": ["operator", "o"],
"%": ["half-op", "h"],
"+": ["voice", "v"],
};
// Labels for the mode changes. For example .rev(['admin', 'a']) => 'Revoke admin (-a)'
const modeTextTemplate = {
revoke: (m) => `Revoke ${m[0]} (-${m[1]})`,
give: (m) => `Give ${m[0]} (+${m[1]})`,
};
2019-11-09 23:21:34 +01:00
const networkModes = network.serverOptions.PREFIX;
2019-11-09 23:21:34 +01:00
/**
* Determine whether the prefix of mode p1 has access to perform actions on p2.
*
* EXAMPLE:
* compare('@', '@') => true
* compare('&', '@') => true
* compare('+', '~') => false
* @param {string} p1 The mode performing an action
* @param {string} p2 The target mode
*
* @return {boolean} whether p1 can perform an action on p2
*/
function compare(p1, p2) {
// The modes ~ and @ can perform actions on their own mode. The others on modes below.
return "~@".indexOf(p1) > -1
? networkModes.indexOf(p1) <= networkModes.indexOf(p2)
: networkModes.indexOf(p1) < networkModes.indexOf(p2);
}
networkModes.forEach((prefix) => {
if (!compare(currentChannelUser.modes[0], prefix)) {
// Our highest mode is below the current mode. Bail.
return;
}
if (!user.modes.includes(prefix)) {
// The target doesn't already have this mode, therefore we can set it.
2019-11-09 23:21:34 +01:00
items.push({
label: modeTextTemplate.give(modes[prefix]),
2019-11-09 23:21:34 +01:00
type: "item",
class: "action-set-mode",
2019-11-09 23:21:34 +01:00
action() {
socket.emit("input", {
target: channel.id,
text: "/mode +" + modes[prefix][1] + " " + user.nick,
2019-11-09 23:21:34 +01:00
});
},
});
} else {
items.push({
label: modeTextTemplate.revoke(modes[prefix]),
2019-11-09 23:21:34 +01:00
type: "item",
class: "action-revoke-mode",
2019-11-09 23:21:34 +01:00
action() {
socket.emit("input", {
target: channel.id,
text: "/mode -" + modes[prefix][1] + " " + user.nick,
2019-11-09 23:21:34 +01:00
});
},
});
}
});
2019-11-09 23:21:34 +01:00
// Determine if we are half-op or op depending on the network modes so we can kick.
if (!compare(networkModes.indexOf("%") > -1 ? "%" : "@", currentChannelUser.modes[0])) {
if (user.modes.length === 0 || compare(currentChannelUser.modes[0], user.modes[0])) {
// Check if the target user has no mode or a mode lower than ours.
2019-11-09 23:21:34 +01:00
items.push({
label: "Kick",
2019-11-09 23:21:34 +01:00
type: "item",
class: "action-kick",
2019-11-09 23:21:34 +01:00
action() {
socket.emit("input", {
target: channel.id,
text: "/kick " + user.nick,
2019-11-09 23:21:34 +01:00
});
},
});
}
}
return items;
}