Fix context menus in search results.
This commit is contained in:
parent
5d017b09b8
commit
b4d02c3c56
@ -75,14 +75,19 @@ export default {
|
|||||||
this.open(data.event, items);
|
this.open(data.event, items);
|
||||||
},
|
},
|
||||||
openUserContextMenu(data) {
|
openUserContextMenu(data) {
|
||||||
const {network, channel} = this.$store.state.activeChannel;
|
const activeChannel = this.$store.state.activeChannel;
|
||||||
|
// If there's an active network and channel use them
|
||||||
|
let {network, channel} = activeChannel ? activeChannel : {network: null, channel: null};
|
||||||
|
|
||||||
const items = generateUserContextMenu(
|
// Use network and channel from event if specified
|
||||||
this.$root,
|
network = data.network ? data.network : network;
|
||||||
channel,
|
channel = data.channel ? data.channel : channel;
|
||||||
network,
|
|
||||||
channel.users.find((u) => u.nick === data.user.nick) || {nick: data.user.nick}
|
const defaultUser = {nick: data.user.nick};
|
||||||
);
|
let user = channel ? channel.users.find((u) => u.nick === data.user.nick) : defaultUser;
|
||||||
|
user = user ? user : defaultUser;
|
||||||
|
|
||||||
|
const items = generateUserContextMenu(this.$root, channel, network, user);
|
||||||
this.open(data.event, items);
|
this.open(data.event, items);
|
||||||
},
|
},
|
||||||
open(event, items) {
|
open(event, items) {
|
||||||
|
@ -24,9 +24,12 @@
|
|||||||
<template v-else-if="message.type === 'action'">
|
<template v-else-if="message.type === 'action'">
|
||||||
<span class="from"><span class="only-copy">* </span></span>
|
<span class="from"><span class="only-copy">* </span></span>
|
||||||
<span class="content" dir="auto">
|
<span class="content" dir="auto">
|
||||||
<Username :user="message.from" dir="auto" /> <ParsedMessage
|
<Username
|
||||||
:message="message"
|
:user="message.from"
|
||||||
/>
|
:network="network"
|
||||||
|
:channel="channel"
|
||||||
|
dir="auto"
|
||||||
|
/> <ParsedMessage :message="message" />
|
||||||
<LinkPreview
|
<LinkPreview
|
||||||
v-for="preview in message.previews"
|
v-for="preview in message.previews"
|
||||||
:key="preview.link"
|
:key="preview.link"
|
||||||
@ -40,7 +43,7 @@
|
|||||||
<span v-if="message.type === 'message'" class="from">
|
<span v-if="message.type === 'message'" class="from">
|
||||||
<template v-if="message.from && message.from.nick">
|
<template v-if="message.from && message.from.nick">
|
||||||
<span class="only-copy"><</span>
|
<span class="only-copy"><</span>
|
||||||
<Username :user="message.from" />
|
<Username :user="message.from" :network="network" :channel="channel" />
|
||||||
<span class="only-copy">> </span>
|
<span class="only-copy">> </span>
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
@ -54,7 +57,7 @@
|
|||||||
<span v-else class="from">
|
<span v-else class="from">
|
||||||
<template v-if="message.from && message.from.nick">
|
<template v-if="message.from && message.from.nick">
|
||||||
<span class="only-copy">-</span>
|
<span class="only-copy">-</span>
|
||||||
<Username :user="message.from" />
|
<Username :user="message.from" :network="network" :channel="channel" />
|
||||||
<span class="only-copy">- </span>
|
<span class="only-copy">- </span>
|
||||||
</template>
|
</template>
|
||||||
</span>
|
</span>
|
||||||
|
@ -19,6 +19,8 @@ export default {
|
|||||||
user: Object,
|
user: Object,
|
||||||
active: Boolean,
|
active: Boolean,
|
||||||
onHover: Function,
|
onHover: Function,
|
||||||
|
channel: Object,
|
||||||
|
network: Object,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
nickColor() {
|
nickColor() {
|
||||||
@ -33,6 +35,8 @@ export default {
|
|||||||
this.$root.$emit("contextmenu:user", {
|
this.$root.$emit("contextmenu:user", {
|
||||||
event: event,
|
event: event,
|
||||||
user: this.user,
|
user: this.user,
|
||||||
|
network: this.network,
|
||||||
|
channel: this.channel,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1,340 +0,0 @@
|
|||||||
/*
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const constants = require("./constants");
|
|
||||||
|
|
||||||
import Mousetrap from "mousetrap";
|
|
||||||
import {Textcomplete, Textarea} from "textcomplete";
|
|
||||||
import fuzzy from "fuzzy";
|
|
||||||
|
|
||||||
import emojiMap from "./helpers/simplemap.json";
|
|
||||||
import store from "./store";
|
|
||||||
|
|
||||||
export default enableAutocomplete;
|
|
||||||
|
|
||||||
export default class CustomTextarea extends Textarea {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojiSearchTerms = Object.keys(emojiMap);
|
|
||||||
const emojiStrategy = {
|
|
||||||
id: "emoji",
|
|
||||||
match: /(^|\s):([-+\w:?]{2,}):?$/,
|
|
||||||
search(term, callback) {
|
|
||||||
// Trim colon from the matched term,
|
|
||||||
// as we are unable to get a clean string from match regex
|
|
||||||
term = term.replace(/:$/, "");
|
|
||||||
callback(fuzzyGrep(term, emojiSearchTerms));
|
|
||||||
},
|
|
||||||
template([string, original]) {
|
|
||||||
return `<span class="emoji">${emojiMap[original]}</span> ${string}`;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return "$1" + emojiMap[original];
|
|
||||||
},
|
|
||||||
index: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const nicksStrategy = {
|
|
||||||
id: "nicks",
|
|
||||||
match: /(^|\s)(@([a-zA-Z_[\]\\^{}|`@][a-zA-Z0-9_[\]\\^{}|`-]*)?)$/,
|
|
||||||
search(term, callback) {
|
|
||||||
term = term.slice(1);
|
|
||||||
|
|
||||||
if (term[0] === "@") {
|
|
||||||
callback(completeNicks(term.slice(1), true).map((val) => ["@" + val[0], "@" + val[1]]));
|
|
||||||
} else {
|
|
||||||
callback(completeNicks(term, true));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
template([string]) {
|
|
||||||
return string;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return "$1" + replaceNick(original);
|
|
||||||
},
|
|
||||||
index: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const chanStrategy = {
|
|
||||||
id: "chans",
|
|
||||||
match: /(^|\s)((?:#|\+|&|![A-Z0-9]{5})(?:[^\s]+)?)$/,
|
|
||||||
search(term, callback) {
|
|
||||||
callback(completeChans(term));
|
|
||||||
},
|
|
||||||
template([string]) {
|
|
||||||
return string;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return "$1" + original;
|
|
||||||
},
|
|
||||||
index: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
const commandStrategy = {
|
|
||||||
id: "commands",
|
|
||||||
match: /^\/(\w*)$/,
|
|
||||||
search(term, callback) {
|
|
||||||
callback(completeCommands("/" + term));
|
|
||||||
},
|
|
||||||
template([string]) {
|
|
||||||
return string;
|
|
||||||
},
|
|
||||||
replace([, original]) {
|
|
||||||
return original;
|
|
||||||
},
|
|
||||||
index: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const foregroundColorStrategy = {
|
|
||||||
id: "foreground-colors",
|
|
||||||
match: /\x03(\d{0,2}|[A-Za-z ]{0,10})$/,
|
|
||||||
search(term, callback) {
|
|
||||||
term = term.toLowerCase();
|
|
||||||
|
|
||||||
const matchingColorCodes = constants.colorCodeMap
|
|
||||||
.filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
|
|
||||||
.map((i) => {
|
|
||||||
if (fuzzy.test(term, i[1])) {
|
|
||||||
return [
|
|
||||||
i[0],
|
|
||||||
fuzzy.match(term, i[1], {
|
|
||||||
pre: "<b>",
|
|
||||||
post: "</b>",
|
|
||||||
}).rendered,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return i;
|
|
||||||
});
|
|
||||||
|
|
||||||
callback(matchingColorCodes);
|
|
||||||
},
|
|
||||||
template(value) {
|
|
||||||
return `<span class="irc-fg${parseInt(value[0], 10)}">${value[1]}</span>`;
|
|
||||||
},
|
|
||||||
replace(value) {
|
|
||||||
return "\x03" + value[0];
|
|
||||||
},
|
|
||||||
index: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
const backgroundColorStrategy = {
|
|
||||||
id: "background-colors",
|
|
||||||
match: /\x03(\d{2}),(\d{0,2}|[A-Za-z ]{0,10})$/,
|
|
||||||
search(term, callback, match) {
|
|
||||||
term = term.toLowerCase();
|
|
||||||
const matchingColorCodes = constants.colorCodeMap
|
|
||||||
.filter((i) => fuzzy.test(term, i[0]) || fuzzy.test(term, i[1]))
|
|
||||||
.map((pair) => {
|
|
||||||
if (fuzzy.test(term, pair[1])) {
|
|
||||||
return [
|
|
||||||
pair[0],
|
|
||||||
fuzzy.match(term, pair[1], {
|
|
||||||
pre: "<b>",
|
|
||||||
post: "</b>",
|
|
||||||
}).rendered,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return pair;
|
|
||||||
})
|
|
||||||
.map((pair) => pair.concat(match[1])); // Needed to pass fg color to `template`...
|
|
||||||
|
|
||||||
callback(matchingColorCodes);
|
|
||||||
},
|
|
||||||
template(value) {
|
|
||||||
return `<span class="irc-fg${parseInt(value[2], 10)} irc-bg irc-bg${parseInt(
|
|
||||||
value[0],
|
|
||||||
10
|
|
||||||
)}">${value[1]}</span>`;
|
|
||||||
},
|
|
||||||
replace(value) {
|
|
||||||
return "\x03$1," + value[0];
|
|
||||||
},
|
|
||||||
index: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
function enableAutocomplete(input,) {
|
|
||||||
let tabCount = 0;
|
|
||||||
let lastMatch = "";
|
|
||||||
let currentMatches = [];
|
|
||||||
|
|
||||||
input.addEventListener("input", (e) => {
|
|
||||||
if (e.detail === "autocomplete") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tabCount = 0;
|
|
||||||
currentMatches = [];
|
|
||||||
lastMatch = "";
|
|
||||||
});
|
|
||||||
|
|
||||||
Mousetrap(input).bind(
|
|
||||||
"tab",
|
|
||||||
(e) => {
|
|
||||||
if (store.state.isAutoCompleting) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const text = input.value;
|
|
||||||
|
|
||||||
if (tabCount === 0) {
|
|
||||||
lastMatch = text
|
|
||||||
.substring(0, input.selectionStart)
|
|
||||||
.split(/\s/)
|
|
||||||
.pop();
|
|
||||||
|
|
||||||
if (lastMatch.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentMatches = completeNicks(lastMatch, false);
|
|
||||||
|
|
||||||
if (currentMatches.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const position = input.selectionStart - lastMatch.length;
|
|
||||||
const newMatch = replaceNick(
|
|
||||||
currentMatches[tabCount % currentMatches.length],
|
|
||||||
position
|
|
||||||
);
|
|
||||||
const remainder = text.substr(input.selectionStart);
|
|
||||||
|
|
||||||
input.value = text.substr(0, position) + newMatch + remainder;
|
|
||||||
input.selectionStart -= remainder.length;
|
|
||||||
input.selectionEnd = input.selectionStart;
|
|
||||||
|
|
||||||
// Propagate change to Vue model
|
|
||||||
input.dispatchEvent(
|
|
||||||
new CustomEvent("input", {
|
|
||||||
detail: "autocomplete",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
lastMatch = newMatch;
|
|
||||||
tabCount++;
|
|
||||||
},
|
|
||||||
"keydown"
|
|
||||||
);
|
|
||||||
|
|
||||||
const editor = new CustomTextarea(input);
|
|
||||||
const textcomplete = new Textcomplete(editor, {
|
|
||||||
dropdown: {
|
|
||||||
className: "textcomplete-menu",
|
|
||||||
placement: "top",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
textcomplete.register([
|
|
||||||
emojiStrategy,
|
|
||||||
nicksStrategy,
|
|
||||||
chanStrategy,
|
|
||||||
commandStrategy,
|
|
||||||
foregroundColorStrategy,
|
|
||||||
backgroundColorStrategy,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Activate the first item by default
|
|
||||||
// https://github.com/yuku-t/textcomplete/issues/93
|
|
||||||
textcomplete.on("rendered", () => {
|
|
||||||
if (textcomplete.dropdown.items.length > 0) {
|
|
||||||
textcomplete.dropdown.items[0].activate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
textcomplete.on("show", () => {
|
|
||||||
store.commit("isAutoCompleting", true);
|
|
||||||
});
|
|
||||||
|
|
||||||
textcomplete.on("hidden", () => {
|
|
||||||
store.commit("isAutoCompleting", false);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
hide() {
|
|
||||||
textcomplete.hide();
|
|
||||||
},
|
|
||||||
destroy() {
|
|
||||||
textcomplete.destroy();
|
|
||||||
store.commit("isAutoCompleting", false);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceNick(original, position = 1) {
|
|
||||||
// If no postfix specified, return autocompleted nick as-is
|
|
||||||
if (!store.state.settings.nickPostfix) {
|
|
||||||
return original;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is whitespace in the input already, append space to nick
|
|
||||||
if (position > 0 && /\s/.test(store.state.activeChannel.channel.pendingMessage)) {
|
|
||||||
return original + " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// If nick is first in the input, append specified postfix
|
|
||||||
return original + store.state.settings.nickPostfix;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fuzzyGrep(term, array) {
|
|
||||||
const results = fuzzy.filter(term, array, {
|
|
||||||
pre: "<b>",
|
|
||||||
post: "</b>",
|
|
||||||
});
|
|
||||||
return results.map((el) => [el.string, el.original]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function rawNicks() {
|
|
||||||
if (store.state.activeChannel.channel.users.length > 0) {
|
|
||||||
const users = store.state.activeChannel.channel.users.slice();
|
|
||||||
|
|
||||||
return users.sort((a, b) => b.lastMessage - a.lastMessage).map((u) => u.nick);
|
|
||||||
}
|
|
||||||
|
|
||||||
const me = store.state.activeChannel.network.nick;
|
|
||||||
const otherUser = store.state.activeChannel.channel.name;
|
|
||||||
|
|
||||||
// If this is a query, add their name to autocomplete
|
|
||||||
if (me !== otherUser && store.state.activeChannel.channel.type === "query") {
|
|
||||||
return [otherUser, me];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return our own name by default for anything that isn't a channel or query
|
|
||||||
return [me];
|
|
||||||
}
|
|
||||||
|
|
||||||
function completeNicks(word, isFuzzy) {
|
|
||||||
const users = rawNicks();
|
|
||||||
word = word.toLowerCase();
|
|
||||||
|
|
||||||
if (isFuzzy) {
|
|
||||||
return fuzzyGrep(word, users);
|
|
||||||
}
|
|
||||||
|
|
||||||
return users.filter((w) => !w.toLowerCase().indexOf(word));
|
|
||||||
}
|
|
||||||
|
|
||||||
function completeCommands(word) {
|
|
||||||
const words = constants.commands.slice();
|
|
||||||
|
|
||||||
return fuzzyGrep(word, words);
|
|
||||||
}
|
|
||||||
|
|
||||||
function completeChans(word) {
|
|
||||||
const words = [];
|
|
||||||
|
|
||||||
for (const channel of store.state.activeChannel.network.channels) {
|
|
||||||
// Push all channels that start with the same CHANTYPE
|
|
||||||
if (channel.type === "channel" && channel.name[0] === word[0]) {
|
|
||||||
words.push(channel.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fuzzyGrep(word, words);
|
|
||||||
}
|
|
||||||
*/
|
|
@ -170,10 +170,12 @@ export function generateChannelContextMenu($root, channel, network) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function generateUserContextMenu($root, channel, network, user) {
|
export function generateUserContextMenu($root, channel, network, user) {
|
||||||
const currentChannelUser = channel.users.find((u) => u.nick === network.nick) || {};
|
const currentChannelUser = channel
|
||||||
|
? channel.users.find((u) => u.nick === network.nick) || {}
|
||||||
|
: {};
|
||||||
|
|
||||||
const whois = () => {
|
const whois = () => {
|
||||||
const chan = $root.$store.getters.findChannelOnCurrentNetwork(user.nick);
|
const chan = network.channels.find((c) => c.name === user.nick);
|
||||||
|
|
||||||
if (chan) {
|
if (chan) {
|
||||||
$root.switchToChannel(chan);
|
$root.switchToChannel(chan);
|
||||||
|
@ -87,6 +87,9 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
|||||||
|
|
||||||
const parts = channelParts.concat(linkParts).concat(emojiParts).concat(nameParts);
|
const parts = channelParts.concat(linkParts).concat(emojiParts).concat(nameParts);
|
||||||
|
|
||||||
|
// The channel the message belongs to might not exist if the user isn't joined to it.
|
||||||
|
const messageChannel = message ? message.channel : null;
|
||||||
|
|
||||||
// Merge the styling information with the channels / URLs / nicks / text objects and
|
// Merge the styling information with the channels / URLs / nicks / text objects and
|
||||||
// generate HTML strings with the resulting fragments
|
// generate HTML strings with the resulting fragments
|
||||||
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
return merge(parts, styleFragments, cleanText).map((textPart) => {
|
||||||
@ -184,6 +187,8 @@ function parse(createElement, text, message = undefined, network = undefined) {
|
|||||||
user: {
|
user: {
|
||||||
nick: textPart.nick,
|
nick: textPart.nick,
|
||||||
},
|
},
|
||||||
|
channel: messageChannel,
|
||||||
|
network,
|
||||||
},
|
},
|
||||||
attrs: {
|
attrs: {
|
||||||
dir: "auto",
|
dir: "auto",
|
||||||
|
@ -17,7 +17,23 @@ function detectDesktopNotificationState() {
|
|||||||
return "blocked";
|
return "blocked";
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
let store = null;
|
||||||
|
|
||||||
|
const setMessageNetworkChannel = (message) => {
|
||||||
|
const channelAndNetwork = store.getters.findChannelOnNetwork(
|
||||||
|
message.networkUuid,
|
||||||
|
message.channelName
|
||||||
|
);
|
||||||
|
|
||||||
|
if (channelAndNetwork) {
|
||||||
|
message.network = channelAndNetwork.network;
|
||||||
|
message.channel = channelAndNetwork.channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
};
|
||||||
|
|
||||||
|
store = new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
appLoaded: false,
|
appLoaded: false,
|
||||||
activeChannel: null,
|
activeChannel: null,
|
||||||
@ -118,10 +134,24 @@ const store = new Vuex.Store({
|
|||||||
state.messageSearchInProgress = value;
|
state.messageSearchInProgress = value;
|
||||||
},
|
},
|
||||||
messageSearchResults(state, value) {
|
messageSearchResults(state, value) {
|
||||||
|
if (value) {
|
||||||
|
// Set the search results and add networks and channels to messages
|
||||||
|
state.messageSearchResults = {
|
||||||
|
...value,
|
||||||
|
...value.results.map(setMessageNetworkChannel),
|
||||||
|
};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
state.messageSearchResults = value;
|
state.messageSearchResults = value;
|
||||||
},
|
},
|
||||||
addMessageSearchResults(state, value) {
|
addMessageSearchResults(state, value) {
|
||||||
value.results = [...state.messageSearchResults.results, ...value.results];
|
// Append the search results and add networks and channels to new messages
|
||||||
|
value.results = [
|
||||||
|
...state.messageSearchResults.results,
|
||||||
|
...value.results.map(setMessageNetworkChannel),
|
||||||
|
];
|
||||||
|
|
||||||
state.messageSearchResults = value;
|
state.messageSearchResults = value;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -208,7 +208,7 @@ class MessageStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let select =
|
let select =
|
||||||
'SELECT msg, type, time, channel FROM messages WHERE type = "message" AND (json_extract(msg, "$.text") LIKE ?';
|
'SELECT msg, type, time, network, channel FROM messages WHERE type = "message" AND (json_extract(msg, "$.text") LIKE ?';
|
||||||
const params = [`%${query.searchTerm}%`];
|
const params = [`%${query.searchTerm}%`];
|
||||||
|
|
||||||
if (query.searchNicks) {
|
if (query.searchNicks) {
|
||||||
@ -245,7 +245,7 @@ class MessageStorage {
|
|||||||
target: query.channelName,
|
target: query.channelName,
|
||||||
networkUuid: query.networkUuid,
|
networkUuid: query.networkUuid,
|
||||||
offset: query.offset,
|
offset: query.offset,
|
||||||
results: parseRowsToMessages(query.offset, rows),
|
results: parseSearchRowsToMessages(query.offset, rows),
|
||||||
};
|
};
|
||||||
resolve(response);
|
resolve(response);
|
||||||
}
|
}
|
||||||
@ -260,13 +260,15 @@ class MessageStorage {
|
|||||||
|
|
||||||
module.exports = MessageStorage;
|
module.exports = MessageStorage;
|
||||||
|
|
||||||
function parseRowsToMessages(id, rows) {
|
function parseSearchRowsToMessages(id, rows) {
|
||||||
const messages = [];
|
const messages = [];
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const msg = JSON.parse(row.msg);
|
const msg = JSON.parse(row.msg);
|
||||||
msg.time = row.time;
|
msg.time = row.time;
|
||||||
msg.type = row.type;
|
msg.type = row.type;
|
||||||
|
msg.networkUuid = row.network;
|
||||||
|
msg.channelName = row.channel;
|
||||||
msg.id = id;
|
msg.id = id;
|
||||||
messages.push(new Msg(msg));
|
messages.push(new Msg(msg));
|
||||||
id += 1;
|
id += 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user