mirror of
https://github.com/thelounge/thelounge
synced 2024-11-22 03:53:08 +00:00
Fix context menu generation
This commit is contained in:
parent
de76a86757
commit
2a6c57abaa
13 changed files with 97 additions and 124 deletions
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
/> <ParsedMessage :network="network" :message="message" />
|
||||
<Username :user="message.from" dir="auto" /> <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"><</span>
|
||||
<Username :user="message.from" :context-menu-callback="openUserContextMenu" />
|
||||
<Username :user="message.from" />
|
||||
<span class="only-copy">> </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>
|
||||
|
|
|
@ -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
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>",
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue