Fix context menu generation

This commit is contained in:
Pavel Djundik 2019-11-23 16:26:20 +02:00
parent de76a86757
commit 2a6c57abaa
13 changed files with 97 additions and 124 deletions

View File

@ -15,7 +15,6 @@
import throttle from "lodash/throttle";
import constants from "../js/constants";
import storage from "../js/localStorage";
import {generateUserContextMenu} from "../js/helpers/contextMenu";
import Sidebar from "./Sidebar.vue";
import ImageViewer from "./ImageViewer.vue";
@ -89,27 +88,6 @@ export default {
this.$store.commit("userlistOpen", isUserlistOpen === "true");
},
openContextMenu(event, items) {
// TODO: maybe move this method to the store or some other more accessible place
this.$refs.contextMenu.open(event, items);
},
openContextMenuForMentionedNick(event, network, nick) {
// TODO: Find a better way to do this
const channel = this.$store.state.activeChannel.channel;
let user = channel.users.find((u) => u.nick === nick);
if (!user) {
user = {
nick: nick,
mode: "",
};
}
const items = generateUserContextMenu(this.$root, channel, network, user);
this.openContextMenu(event, items);
},
},
};
</script>

View File

@ -25,7 +25,6 @@
<script>
import socket from "../js/socket";
import {generateChannelContextMenu} from "../js/helpers/contextMenu.js";
import isChannelCollapsed from "../js/helpers/isChannelCollapsed";
export default {
@ -77,8 +76,11 @@ export default {
this.$root.switchToChannel(this.channel);
},
openContextMenu(event) {
const items = generateChannelContextMenu(this.$root, this.channel, this.network);
this.$root.$refs.app.openContextMenu(event, items);
this.$root.$emit("contextmenu:channel", {
event: event,
channel: this.channel,
network: this.network,
});
},
},
};

View File

@ -95,7 +95,6 @@
<script>
import socket from "../js/socket";
import {generateChannelContextMenu} from "../js/helpers/contextMenu.js";
import ParsedMessage from "./ParsedMessage.vue";
import MessageList from "./MessageList.vue";
import ChatInput from "./ChatInput.vue";
@ -182,8 +181,11 @@ export default {
}
},
openContextMenu(event) {
const items = generateChannelContextMenu(this.$root, this.channel, this.network);
this.$root.$refs.app.openContextMenu(event, items);
this.$root.$emit("contextmenu:channel", {
event: event,
channel: this.channel,
network: this.network,
});
},
},
};

View File

@ -32,7 +32,6 @@
:on-hover="hoverUser"
:active="user.original === activeUser"
:user="user"
:context-menu-callback="openContextMenu"
/>
</template>
<template v-else>
@ -42,7 +41,6 @@
:on-hover="hoverUser"
:active="user === activeUser"
:user="user"
:context-menu-callback="openContextMenu"
/>
</template>
</div>
@ -54,7 +52,6 @@
import {filter as fuzzyFilter} from "fuzzy";
import Username from "./Username.vue";
import UsernameFiltered from "./UsernameFiltered.vue";
import {generateUserContextMenu} from "../js/helpers/contextMenu.js";
const modes = {
"~": "owner",
@ -197,11 +194,6 @@ export default {
el.scrollIntoView({block: "nearest", inline: "nearest"});
});
},
openContextMenu(event, user) {
const {network} = this.$store.getters.findChannel(this.channel.id);
const items = generateUserContextMenu(this.$root, this.channel, network, user);
this.$root.$refs.app.openContextMenu(event, items);
},
},
};
</script>

View File

@ -35,6 +35,7 @@
<script>
import Mousetrap from "mousetrap";
import {generateUserContextMenu, generateChannelContextMenu} from "../js/helpers/contextMenu.js";
export default {
name: "ContextMenu",
@ -56,12 +57,33 @@ export default {
mounted() {
Mousetrap.bind("esc", this.close);
Mousetrap.bind(["up", "down", "tab", "shift+tab"], this.navigateMenu);
this.$root.$on("contextmenu:user", this.openUserContextMenu);
this.$root.$on("contextmenu:channel", this.openChannelContextMenu);
},
destroyed() {
Mousetrap.unbind("esc", this.close);
Mousetrap.unbind(["up", "down", "tab", "shift+tab"], this.navigateMenu);
this.$root.$off("contextmenu:user", this.openUserContextMenu);
this.$root.$off("contextmenu:channel", this.openChannelContextMenu);
},
methods: {
openChannelContextMenu(data) {
const items = generateChannelContextMenu(this.$root, data.channel, data.network);
this.open(data.event, items);
},
openUserContextMenu(data) {
const {network, channel} = this.$store.state.activeChannel;
const items = generateUserContextMenu(
this.$root,
channel,
network,
channel.users.find((u) => u.nick === data.user.nick) || {nick: data.user.nick}
);
this.open(data.event, items);
},
open(event, items) {
event.preventDefault();

View File

@ -20,11 +20,9 @@
<template v-else-if="message.type === 'action'">
<span class="from"><span class="only-copy">* </span></span>
<span class="content" dir="auto">
<Username
:user="message.from"
dir="auto"
:context-menu-callback="openUserContextMenu"
/>&#32;<ParsedMessage :network="network" :message="message" />
<Username :user="message.from" dir="auto" />&#32;<ParsedMessage
:message="message"
/>
<LinkPreview
v-for="preview in message.previews"
:key="preview.link"
@ -37,7 +35,7 @@
<span v-if="message.type === 'message'" class="from">
<template v-if="message.from && message.from.nick">
<span class="only-copy">&lt;</span>
<Username :user="message.from" :context-menu-callback="openUserContextMenu" />
<Username :user="message.from" />
<span class="only-copy">&gt; </span>
</template>
</span>
@ -51,7 +49,7 @@
<span v-else class="from">
<template v-if="message.from && message.from.nick">
<span class="only-copy">-</span>
<Username :user="message.from" :context-menu-callback="openUserContextMenu" />
<Username :user="message.from" />
<span class="only-copy">- </span>
</template>
</span>
@ -74,7 +72,6 @@ import Username from "./Username.vue";
import LinkPreview from "./LinkPreview.vue";
import ParsedMessage from "./ParsedMessage.vue";
import MessageTypes from "./MessageTypes";
import {generateUserContextMenu} from "../js/helpers/contextMenu.js";
import constants from "../js/constants";
MessageTypes.ParsedMessage = ParsedMessage;
@ -106,10 +103,6 @@ export default {
isAction() {
return typeof MessageTypes["message-" + this.message.type] !== "undefined";
},
openUserContextMenu(event, user) {
const items = generateUserContextMenu(this.$root, this.channel, this.network, user);
this.$root.$refs.app.openContextMenu(event, items);
},
},
};
</script>

View File

@ -16,8 +16,7 @@ export default {
? context.props.text
: context.props.message.text,
context.props.message,
context.props.network,
context.parent.$root
context.props.network
);
},
};

View File

@ -4,8 +4,8 @@
:data-name="user.nick"
role="button"
v-on="onHover ? {mouseover: hover} : {}"
@click.prevent="rightClick($event)"
@contextmenu.prevent="rightClick($event)"
@click.prevent="openContextMenu"
@contextmenu.prevent="openContextMenu"
>{{ user.mode }}{{ user.nick }}</span
>
</template>
@ -19,7 +19,6 @@ export default {
user: Object,
active: Boolean,
onHover: Function,
contextMenuCallback: Function,
},
computed: {
nickColor() {
@ -30,10 +29,11 @@ export default {
hover() {
return this.onHover(this.user);
},
rightClick($event) {
if (this.contextMenuCallback) {
this.contextMenuCallback($event, this.user);
}
openContextMenu(event) {
this.$root.$emit("contextmenu:user", {
event: event,
user: this.user,
});
},
},
};

View File

@ -3,9 +3,9 @@
:class="['user', nickColor, {active: active}]"
:data-name="user.original.nick"
role="button"
@mouseover="hover"
@click.prevent="rightClick($event)"
@contextmenu.prevent="rightClick($event)"
@mouseover="onHover(user.original)"
@click.prevent="openContextMenu"
@contextmenu.prevent="openContextMenu"
v-html="user.original.mode + user.string"
/>
</template>
@ -19,7 +19,6 @@ export default {
user: Object,
active: Boolean,
onHover: Function,
contextMenuCallback: Function,
},
computed: {
nickColor() {
@ -27,13 +26,11 @@ export default {
},
},
methods: {
hover() {
this.onHover ? this.onHover(this.user.original) : null;
},
rightClick($event) {
if (this.contextMenuCallback) {
this.contextMenuCallback($event, this.user);
}
openContextMenu(event) {
this.$root.$emit("contextmenu:user", {
event: event,
user: this.user.original,
});
},
},
};

View File

@ -2133,6 +2133,7 @@ part/quit messages where we don't load previews (adds a blank line otherwise) */
line-height: 1.4;
transition: background-color 0.2s;
border-radius: 3px;
white-space: nowrap;
}
.context-menu-item.active,

View File

@ -91,52 +91,46 @@ export function generateChannelContextMenu($root, channel, network) {
// Add menu items for channels
if (channel.type === "channel") {
items = [
...items,
{
label: "Edit topic",
type: "item",
class: "edit",
action() {
channel.editTopic = true;
$root.switchToChannel(channel);
items.push({
label: "Edit topic",
type: "item",
class: "edit",
action() {
channel.editTopic = true;
$root.switchToChannel(channel);
$root.$nextTick(() =>
document.querySelector(`#chan-${channel.id} .topic-input`).focus()
);
},
$root.$nextTick(() =>
document.querySelector(`#chan-${channel.id} .topic-input`).focus()
);
},
{
label: "List banned users",
type: "item",
class: "list",
action() {
socket.emit("input", {
target: channel.id,
text: "/banlist",
});
},
});
items.push({
label: "List banned users",
type: "item",
class: "list",
action() {
socket.emit("input", {
target: channel.id,
text: "/banlist",
});
},
];
});
}
// Add menu items for queries
if (channel.type === "query") {
items = [
...items,
{
label: "User information",
type: "item",
class: "action-whois",
action() {
$root.switchToChannel(channel);
socket.emit("input", {
target: $root.$store.state.activeChannel.channel.id,
text: "/whois " + channel.name,
});
},
items.push({
label: "User information",
type: "item",
class: "action-whois",
action() {
$root.switchToChannel(channel);
socket.emit("input", {
target: channel.id,
text: "/whois " + channel.name,
});
},
];
});
}
// Add close menu item
@ -159,7 +153,7 @@ export function generateChannelContextMenu($root, channel, network) {
}
export function generateUserContextMenu($root, channel, network, user) {
const currentChannelUser = channel.users.filter((u) => u.nick === network.nick)[0];
const currentChannelUser = channel.users.find((u) => u.nick === network.nick) || {};
const whois = () => {
const chan = $root.$store.getters.findChannelOnCurrentNetwork(user.nick);

View File

@ -6,11 +6,11 @@ import findLinks from "./ircmessageparser/findLinks";
import findEmoji from "./ircmessageparser/findEmoji";
import findNames from "./ircmessageparser/findNames";
import merge from "./ircmessageparser/merge";
import colorClass from "./colorClass";
import emojiMap from "./fullnamemap.json";
import LinkPreviewToggle from "../../components/LinkPreviewToggle.vue";
import LinkPreviewFileSize from "../../components/LinkPreviewFileSize.vue";
import InlineChannel from "../../components/InlineChannel.vue";
import Username from "../../components/Username.vue";
const emojiModifiersRegex = /[\u{1f3fb}-\u{1f3ff}]/gu;
@ -70,7 +70,7 @@ function createFragment(fragment, createElement) {
// Transform an IRC message potentially filled with styling control codes, URLs,
// nicknames, and channels into a string of HTML elements to display on the client.
function parse(createElement, text, message = undefined, network = undefined, $root) {
function parse(createElement, text, message = undefined, network = undefined) {
// Extract the styling information and get the plain text version from it
const styleFragments = parseStyle(text);
const cleanText = styleFragments.map((fragment) => fragment.text).join("");
@ -180,23 +180,16 @@ function parse(createElement, text, message = undefined, network = undefined, $r
fragments
);
} else if (textPart.nick) {
// TODO: This really does not belong here, find a better way
const openContextMenu = (event) => {
$root.$refs.app.openContextMenuForMentionedNick(event, network, textPart.nick);
};
return createElement(
"span",
Username,
{
class: ["user", colorClass(textPart.nick)],
attrs: {
role: "button",
dir: "auto",
"data-name": textPart.nick,
props: {
user: {
nick: textPart.nick,
},
},
on: {
contextmenu: openContextMenu,
click: openContextMenu,
attrs: {
dir: "auto",
},
},
fragments

View File

@ -358,7 +358,7 @@ describe("IRC formatted message parser", () => {
input: "test, MaxLeiter",
expected:
"test, " +
'<span role="button" dir="auto" data-name="MaxLeiter" class="user color-12">' +
'<span data-name="MaxLeiter" role="button" dir="auto" class="user color-12">' +
"MaxLeiter" +
"</span>",
},