mirror of
https://github.com/danbulant/Nertivia-Client
synced 2026-06-19 14:21:36 +00:00
added mentioning
This commit is contained in:
parent
5c76f628d4
commit
c1509654d8
17 changed files with 450 additions and 74 deletions
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="emoji-suggetions-list">
|
||||
<div
|
||||
v-for="(emoji, index) in $props.emojiArray.slice(0, 10)"
|
||||
v-for="(emoji, index) in $props.emojiArray"
|
||||
:key="emoji.hexcode || emoji.emojiID"
|
||||
:class="{ emojiItem: true, selected: index === emojiIndex }"
|
||||
@mouseenter="hoverEvent"
|
||||
|
|
@ -83,6 +83,7 @@ export default {
|
|||
<style scoped>
|
||||
.selected {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
}
|
||||
.emoji-suggetions-list {
|
||||
position: absolute;
|
||||
|
|
@ -92,9 +93,10 @@ export default {
|
|||
overflow-y: auto;
|
||||
z-index: 2;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
box-shadow: 0px 0px 20px 5px #000000bd;
|
||||
backdrop-filter: blur(5px);
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
transition: 0.3s;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@
|
|||
class="item material-icons"
|
||||
:class="{
|
||||
selected: currentTab == 2,
|
||||
notifyAnimation: serverNotification
|
||||
notifyAnimation: serverNotification.notification,
|
||||
mentioned: serverNotification.mentioned
|
||||
}"
|
||||
@click="switchTab(2)"
|
||||
@mouseenter="localToolTipEvent('Servers', $event)"
|
||||
|
|
@ -197,7 +198,7 @@ export default {
|
|||
serverNotification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
const notificationsFiltered = notifications.filter(e => {
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
channels[e.channelID].server_id &&
|
||||
|
|
@ -206,7 +207,11 @@ export default {
|
|||
this.currentTab !== 2)
|
||||
);
|
||||
});
|
||||
return notification;
|
||||
const mentioned = notifications.find(m => m.mentioned);
|
||||
return {
|
||||
notification: !!notificationsFiltered.length,
|
||||
mentioned: !!mentioned
|
||||
};
|
||||
},
|
||||
DMNotification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
|
|
@ -307,7 +312,12 @@ export default {
|
|||
background: #ee3e34;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mentioned:before {
|
||||
content: "@";
|
||||
margin-bottom: 10px;
|
||||
font-size: 13px;
|
||||
background: #ff6947;
|
||||
}
|
||||
.tool-tip {
|
||||
color: white;
|
||||
position: absolute;
|
||||
|
|
@ -325,4 +335,5 @@ export default {
|
|||
cursor: default;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
:timeEdited="msg.timeEdited"
|
||||
:color="msg.color"
|
||||
:isServer="isServer"
|
||||
:mentions="msg.mentions"
|
||||
/>
|
||||
|
||||
<uploadsQueue v-if="uploadQueue !== undefined" :queue="uploadQueue" />
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
</div>
|
||||
</transition>
|
||||
<emoji-suggestions v-if="emojiArray" :emojiArray="emojiArray" />
|
||||
<mentions-popout v-if="mentionsArray" :list="mentionsArray" />
|
||||
<emoji-panel v-if="showEmojiPanel" @close="showEmojiPanel = false" />
|
||||
</div>
|
||||
|
||||
|
|
@ -192,6 +193,7 @@ import { bus } from "../../main";
|
|||
import Spinner from "@/components/Spinner.vue";
|
||||
import heading from "@/components/app/MessagePanel/Heading.vue";
|
||||
import emojiSuggestions from "@/components/app/EmojiPanels/emojiSuggestions.vue";
|
||||
import mentionsPopout from "@/components/app/mentionsPopout.vue";
|
||||
import MessageLogs from "@/components/app/MessageLogs.vue";
|
||||
import emojiParser from "@/utils/emojiParser.js";
|
||||
import windowProperties from "@/utils/windowProperties";
|
||||
|
|
@ -211,7 +213,8 @@ export default {
|
|||
heading,
|
||||
EditPanel,
|
||||
MessageLogs,
|
||||
TypingStatus
|
||||
TypingStatus,
|
||||
mentionsPopout
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -283,6 +286,19 @@ export default {
|
|||
|
||||
return ("" + number).substring(add);
|
||||
},
|
||||
replaceMentions(message) {
|
||||
const regex = /@([\w\d\s_-]+):([\w\d_-]+)/g;
|
||||
|
||||
return message.replace(regex, word => {
|
||||
const [username, tag] = word.split(":");
|
||||
if (tag.length !== 4 || !username || !tag) return word;
|
||||
const member = Object.values(this.members).find(
|
||||
m => "@" + m.username === username && m.tag === tag
|
||||
);
|
||||
if (!member) return word;
|
||||
return `<@${member.uniqueID}>`;
|
||||
});
|
||||
},
|
||||
async sendMessage() {
|
||||
this.$refs["input-box"].focus();
|
||||
this.message = this.message.trim();
|
||||
|
|
@ -293,7 +309,8 @@ export default {
|
|||
clearInterval(this.postTimerID);
|
||||
this.postTimerID = null;
|
||||
|
||||
const msg = emojiParser.replaceShortcode(this.message);
|
||||
let msg = emojiParser.replaceShortcode(this.message);
|
||||
msg = this.replaceMentions(msg);
|
||||
|
||||
const tempID = this.generateNum(25);
|
||||
|
||||
|
|
@ -409,6 +426,19 @@ export default {
|
|||
}
|
||||
bus.$emit("scrollDown");
|
||||
},
|
||||
mentionsSwitchKey(event) {
|
||||
if (!this.mentionsArray) return;
|
||||
if (event.keyCode === 38) {
|
||||
bus.$emit("mentions:key", "up");
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (event.keyCode === 40) {
|
||||
bus.$emit("mentions:key", "down");
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
},
|
||||
emojiSwitchKey(event) {
|
||||
if (!this.emojiArray) return;
|
||||
|
||||
|
|
@ -438,10 +468,10 @@ export default {
|
|||
},
|
||||
showEmojiPopout(event) {
|
||||
if (event.keyCode == 38 || event.keyCode == 40) return; // up/down
|
||||
|
||||
const message = this.$refs["input-box"].value;
|
||||
const cursorPosition = event.target.selectionStart;
|
||||
const cursorWord = this.ReturnWord(this.message, cursorPosition);
|
||||
const cursorLetter = this.message.substring(
|
||||
const cursorWord = this.ReturnWord(message, cursorPosition);
|
||||
const cursorLetter = message.substring(
|
||||
cursorPosition - 1,
|
||||
cursorPosition
|
||||
);
|
||||
|
|
@ -452,11 +482,49 @@ export default {
|
|||
if (!cursorWord.startsWith(":") || cursorWord.length <= 2)
|
||||
return this.$store.dispatch("setEmojiArray", null);
|
||||
|
||||
const searchArr = emojiParser.searchEmoji(cursorWord.slice(1, -1));
|
||||
const searchArr = emojiParser.searchEmoji(
|
||||
cursorWord.slice(1, cursorWord.length)
|
||||
);
|
||||
if (searchArr.length <= 0)
|
||||
return this.$store.dispatch("setEmojiArray", null);
|
||||
|
||||
this.$store.dispatch("setEmojiArray", searchArr);
|
||||
this.$store.dispatch("setEmojiArray", searchArr.slice(0, 10));
|
||||
},
|
||||
showMentionsPopout(event) {
|
||||
if (event.keyCode == 38 || event.keyCode == 40) return; // up/down
|
||||
const message = this.$refs["input-box"].value;
|
||||
const cursorPosition = event.target.selectionStart;
|
||||
const cursorWord = this.ReturnWord(message, cursorPosition);
|
||||
|
||||
if (!cursorWord.startsWith("@")) {
|
||||
this.$store.dispatch("mentionsListModule/setMentionsArray", null);
|
||||
return;
|
||||
}
|
||||
// word without @
|
||||
const wordWithoutBegining = cursorWord
|
||||
.slice(1, cursorWord.length)
|
||||
.toLowerCase();
|
||||
|
||||
let searchedMembers = [];
|
||||
|
||||
for (let index = 0; index < this.serverMembers.length; index++) {
|
||||
const serverMember = this.serverMembers[index];
|
||||
if (serverMember.server_id != this.server.server_id) continue;
|
||||
const member = this.members[serverMember.uniqueID];
|
||||
if (
|
||||
member.username
|
||||
.toLowerCase()
|
||||
.replace(/\s/g, "")
|
||||
.includes(wordWithoutBegining)
|
||||
) {
|
||||
searchedMembers.push(member);
|
||||
}
|
||||
}
|
||||
searchedMembers = searchedMembers.slice(0, 9);
|
||||
this.$store.dispatch(
|
||||
"mentionsListModule/setMentionsArray",
|
||||
searchedMembers
|
||||
);
|
||||
},
|
||||
async onInput(event) {
|
||||
const value = event.target.value.trim();
|
||||
|
|
@ -466,7 +534,24 @@ export default {
|
|||
}
|
||||
},
|
||||
keyUp(event) {
|
||||
this.showEmojiPopout(event);
|
||||
setTimeout(() => {
|
||||
this.showMentionsPopout(event);
|
||||
this.showEmojiPopout(event);
|
||||
}, 10);
|
||||
},
|
||||
enterMention() {
|
||||
const member = this.mentionsArray[this.mentionsListIndex];
|
||||
if (!member) return;
|
||||
this.$store.dispatch("mentionsListModule/setMentionsArray", null);
|
||||
const cursorPosition = this.$refs["input-box"].selectionStart;
|
||||
const cursorWord = this.ReturnWord(this.message, cursorPosition);
|
||||
const start = cursorPosition - cursorWord.length;
|
||||
const end = cursorPosition;
|
||||
|
||||
this.message =
|
||||
this.message.substring(0, start) +
|
||||
`@${member.username}:${member.tag} ` +
|
||||
this.message.substring(end);
|
||||
},
|
||||
enterEmojiSuggestion() {
|
||||
const emoji = this.emojiArray[this.emojiIndex];
|
||||
|
|
@ -505,6 +590,7 @@ export default {
|
|||
this.$store.dispatch("settingsModule/addRecentEmoji", shortcode);
|
||||
},
|
||||
keyDown(event) {
|
||||
this.mentionsSwitchKey(event);
|
||||
this.emojiSwitchKey(event);
|
||||
// when enter is press
|
||||
if (event.keyCode == 13) {
|
||||
|
|
@ -518,6 +604,13 @@ export default {
|
|||
this.enterEmojiSuggestion();
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.mentionsArray &&
|
||||
this.mentionsArray[this.mentionsListIndex]
|
||||
) {
|
||||
this.enterMention();
|
||||
return;
|
||||
}
|
||||
if (this.editMessage) {
|
||||
return this.updateMessage();
|
||||
} else {
|
||||
|
|
@ -662,6 +755,7 @@ export default {
|
|||
|
||||
bus.$on("newMessage", this.hideTypingStatus);
|
||||
bus.$on("emojiSuggestions:Selected", this.enterEmojiSuggestion);
|
||||
bus.$on("mentions:Selected", this.enterMention);
|
||||
bus.$on("emojiPanel:Selected", this.enterEmojiPanel);
|
||||
|
||||
bus.$on("scrolledDown", scrolledDown => {
|
||||
|
|
@ -678,7 +772,8 @@ export default {
|
|||
|
||||
bus.$off("newMessage", this.hideTypingStatus);
|
||||
bus.$off("emojiSuggestions:Selected", this.enterEmojiSuggestion);
|
||||
bus.$on("emojiPanel:Selected", this.enterEmojiPanel);
|
||||
bus.$off("emojiPanel:Selected", this.enterEmojiPanel);
|
||||
bus.$off("mentions:Selected", this.enterMention);
|
||||
window.removeEventListener("focus", this.onFocus);
|
||||
window.removeEventListener("blur", this.onBlur);
|
||||
|
||||
|
|
@ -722,6 +817,14 @@ export default {
|
|||
undefined
|
||||
);
|
||||
},
|
||||
members() {
|
||||
const members = this.$store.getters["members/members"];
|
||||
return members;
|
||||
},
|
||||
serverMembers() {
|
||||
const serverMembers = this.$store.getters["servers/serverMembers"];
|
||||
return serverMembers.reverse();
|
||||
},
|
||||
serverMember() {
|
||||
return this.$store.getters["servers/serverMembers"].find(
|
||||
sm =>
|
||||
|
|
@ -783,9 +886,15 @@ export default {
|
|||
emojiArray() {
|
||||
return this.$store.getters.emojiArray;
|
||||
},
|
||||
mentionsArray() {
|
||||
return this.$store.getters["mentionsListModule/mentionsArray"];
|
||||
},
|
||||
emojiIndex() {
|
||||
return this.$store.getters.getEmojiIndex;
|
||||
},
|
||||
mentionsListIndex() {
|
||||
return this.$store.getters["mentionsListModule/getMentionIndex"];
|
||||
},
|
||||
recipients() {
|
||||
const selectedChannel = this.$store.getters.selectedChannelID;
|
||||
const channel = this.$store.getters.channels[selectedChannel];
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@
|
|||
{{ this.$props.username }}
|
||||
</div>
|
||||
<div class="date">{{ getDate }}</div>
|
||||
<div
|
||||
class="mentioned material-icons"
|
||||
v-if="mentioned"
|
||||
title="You were mentioned"
|
||||
>
|
||||
alternate_email
|
||||
</div>
|
||||
</div>
|
||||
<SimpleMarkdown
|
||||
class="content-message"
|
||||
|
|
@ -116,8 +123,8 @@
|
|||
<span class="username" @click="openUserInformation">{{
|
||||
this.$props.username
|
||||
}}</span>
|
||||
<span v-if="type === 1" class="text">has joined the server!</span>
|
||||
<span v-if="type === 2" class="text">has left the server.</span>
|
||||
<span v-if="type === 1" class="text">joined the server!</span>
|
||||
<span v-if="type === 2" class="text">left the server.</span>
|
||||
<span v-if="type === 3" class="text">has been kicked.</span>
|
||||
<span v-if="type === 4" class="text">has been banned.</span>
|
||||
<span class="date">{{ getDate }}</span>
|
||||
|
|
@ -161,7 +168,8 @@ export default {
|
|||
"channelID",
|
||||
"timeEdited",
|
||||
"color",
|
||||
"isServer"
|
||||
"isServer",
|
||||
"mentions"
|
||||
],
|
||||
components: {
|
||||
ProfilePicture,
|
||||
|
|
@ -171,7 +179,8 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
hover: false,
|
||||
isGif: false
|
||||
isGif: false,
|
||||
mentioned: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -263,14 +272,28 @@ export default {
|
|||
},
|
||||
onResize() {
|
||||
this.imageSize();
|
||||
},
|
||||
checkMentioned() {
|
||||
if (
|
||||
this.mentions &&
|
||||
this.mentions.find(u => u.uniqueID === this.user.uniqueID)
|
||||
) {
|
||||
this.mentioned = true;
|
||||
} else {
|
||||
this.mentioned = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
getWindowWidth(dimentions) {
|
||||
this.onResize(dimentions);
|
||||
},
|
||||
mentions() {
|
||||
this.checkMentioned();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.checkMentioned();
|
||||
this.isGif = this.userAvatar.endsWith(".gif");
|
||||
const files = this.files;
|
||||
if (!files || files.length === 0 || !files[0].dimensions) return undefined;
|
||||
|
|
@ -603,7 +626,24 @@ $message-color: rgba(0, 0, 0, 0.3);
|
|||
cursor: pointer;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.mentioned {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
flex-shrink: 0;
|
||||
color: white;
|
||||
background: rgba(255, 59, 59, 0.9);
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: -2px;
|
||||
padding: 2px;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
border-radius: 50%;
|
||||
cursor: default;
|
||||
}
|
||||
@media (max-width: 468px) {
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -194,48 +194,8 @@ export default {
|
|||
},
|
||||
selectedServerID() {
|
||||
return this.$store.getters["servers/selectedServerID"];
|
||||
},
|
||||
serverNotification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
channels[e.channelID].server_id &&
|
||||
(e.channelID !== this.$store.getters.selectedChannelID ||
|
||||
!document.hasFocus() ||
|
||||
this.currentTab !== 2)
|
||||
);
|
||||
});
|
||||
return notification;
|
||||
},
|
||||
DMNotification() {
|
||||
const notifications = this.$store.getters.notifications;
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
!channels[e.channelID].server_id &&
|
||||
(e.channelID !== this.$store.getters.selectedChannelID ||
|
||||
!document.hasFocus() ||
|
||||
this.currentTab !== 1)
|
||||
);
|
||||
});
|
||||
// unopened dm
|
||||
if (!notification) {
|
||||
return notifications.find(e => {
|
||||
return !channels[e.channelID];
|
||||
});
|
||||
}
|
||||
return notification;
|
||||
},
|
||||
friendRequestExists() {
|
||||
const allFriend = this.$store.getters.user.friends;
|
||||
const result = Object.keys(allFriend).map(function(key) {
|
||||
return allFriend[key];
|
||||
});
|
||||
return result.find(friend => friend.status === 1);
|
||||
}
|
||||
|
||||
},
|
||||
mounted() {
|
||||
bus.$on("server-tool-tip", this.serverToolTipEvent);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
:data-servername="serverData.name"
|
||||
:class="{
|
||||
selected: selectedServerID === serverData.server_id,
|
||||
notifyAnimation: notification
|
||||
notifyAnimation: notification.notification,
|
||||
mentioned: notification.mentioned
|
||||
}"
|
||||
@contextmenu.prevent="contextEvent"
|
||||
@mouseenter="hoverEvent"
|
||||
|
|
@ -47,7 +48,7 @@ export default {
|
|||
const notifications = this.$store.getters.notifications;
|
||||
|
||||
const channels = this.$store.getters.channels;
|
||||
const notification = notifications.find(e => {
|
||||
const filteredNotifications = notifications.filter(e => {
|
||||
return (
|
||||
channels[e.channelID] &&
|
||||
channels[e.channelID].server_id &&
|
||||
|
|
@ -56,7 +57,11 @@ export default {
|
|||
(this.selectedChannelID !== e.channelID || !document.hasFocus())
|
||||
);
|
||||
});
|
||||
return notification;
|
||||
const mentioned = filteredNotifications.find(n => n.mentioned);
|
||||
return {
|
||||
mentioned: !!mentioned,
|
||||
notification: !!filteredNotifications.length
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -125,7 +130,6 @@ export default {
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
font-size: 15px;
|
||||
position: absolute;
|
||||
z-index: 115651;
|
||||
|
|
@ -136,4 +140,10 @@ export default {
|
|||
border-radius: 50%;
|
||||
background: #ee3e34;
|
||||
}
|
||||
.mentioned:after {
|
||||
content: "@";
|
||||
margin-bottom: 10px;
|
||||
font-size: 13px;
|
||||
background: #ff6947;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
<template>
|
||||
<div class="formatted-content" v-html="markdown"></div>
|
||||
<div
|
||||
class="formatted-content"
|
||||
ref="content"
|
||||
@click="textClicked"
|
||||
v-html="markdown"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -9,6 +14,14 @@ export default {
|
|||
props: {
|
||||
message: String
|
||||
},
|
||||
methods: {
|
||||
textClicked(event) {
|
||||
if (event.target.classList[0] === "mention") {
|
||||
const id = event.target.id.split("-")[1];
|
||||
this.$store.dispatch("setUserInformationPopout", id);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
markdown: function() {
|
||||
return messageFormatter(this.message);
|
||||
|
|
@ -47,4 +60,16 @@ pre {
|
|||
.link {
|
||||
color: #68aaff;
|
||||
}
|
||||
.mention {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
color: rgb(94, 164, 255);
|
||||
font-weight: bold;
|
||||
border-radius: 7px;
|
||||
padding: 3px;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
}
|
||||
.mention:hover {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
118
src/components/app/mentionsPopout.vue
Normal file
118
src/components/app/mentionsPopout.vue
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<template>
|
||||
<div class="list">
|
||||
<div
|
||||
v-for="(member, index) in list"
|
||||
:key="member.uniqueID"
|
||||
:class="{ item: true, selected: index === mentionIndex }"
|
||||
@mouseenter="hoverEvent"
|
||||
@click="clickEvent"
|
||||
>
|
||||
<img class="avatar" :src="avatarPath + member.avatar + '?type=webp'" />
|
||||
<div class="username">{{ member.username }}</div>
|
||||
<div class="tag">:{{ member.tag }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { bus } from "@/main";
|
||||
import config from "@/config.js";
|
||||
export default {
|
||||
props: ["list"],
|
||||
data() {
|
||||
return {
|
||||
avatarPath: config.domain + "/avatars/"
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
mentionIndex() {
|
||||
return this.$store.getters["mentionsListModule/getMentionIndex"];
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
list() {
|
||||
this.changeIndex(0);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
bus.$on("mentions:key", this.KeySwitch);
|
||||
},
|
||||
destroyed() {
|
||||
bus.$off("mentions:key", this.KeySwitch);
|
||||
this.$store.dispatch("mentionsListModule/setMentionsArray", null);
|
||||
},
|
||||
methods: {
|
||||
changeIndex(index) {
|
||||
this.$store.dispatch("mentionsListModule/changeIndex", index);
|
||||
},
|
||||
hoverEvent(event) {
|
||||
const mention = event.target.closest(".item");
|
||||
const parent = event.target.parentElement.children;
|
||||
if (!mention) return;
|
||||
const index = [...parent].findIndex(el => el === mention);
|
||||
if (index >= 0) this.changeIndex(index);
|
||||
},
|
||||
KeySwitch(key) {
|
||||
if (key == "up") {
|
||||
if (this.mentionIndex == 0)
|
||||
return this.changeIndex(this.$props.list.length - 1);
|
||||
|
||||
this.changeIndex(this.mentionIndex - 1);
|
||||
}
|
||||
if (key == "down") {
|
||||
if (this.mentionIndex == this.$props.list.length - 1)
|
||||
return this.changeIndex(0);
|
||||
this.changeIndex(this.mentionIndex + 1);
|
||||
}
|
||||
},
|
||||
clickEvent() {
|
||||
bus.$emit("mentions:Selected");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.selected {
|
||||
color: white;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
.list {
|
||||
position: absolute;
|
||||
bottom: -35px;
|
||||
left: 50px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
z-index: 2;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
box-shadow: 0px 0px 20px 5px #000000bd;
|
||||
backdrop-filter: blur(5px);
|
||||
border-radius: 4px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
transition: 0.3s;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
.avatar {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
margin-right: 5px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
padding: 5px;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.tag {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
@media (max-height: 441px) {
|
||||
.list {
|
||||
max-height: 150px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -9,6 +9,7 @@ import settingsModule from "./modules/settingsModule";
|
|||
import uploadFilesModule from "./modules/uploadFilesModule";
|
||||
import popoutsModule from "./modules/popoutsModule/popoutsModule.js";
|
||||
import emojiSuggestionModule from "./modules/emojiSuggestionModule";
|
||||
import mentionsListModule from "./modules/mentionsListModule";
|
||||
import serversModule from "./modules/serversModule";
|
||||
import membersModule from "./modules/membersModule";
|
||||
Vue.use(Vuex);
|
||||
|
|
@ -24,6 +25,7 @@ export const store = new Vuex.Store({
|
|||
uploadFilesModule,
|
||||
popoutsModule,
|
||||
emojiSuggestionModule,
|
||||
mentionsListModule,
|
||||
servers: serversModule,
|
||||
members: membersModule
|
||||
},
|
||||
|
|
|
|||
41
src/store/modules/mentionsListModule.js
Normal file
41
src/store/modules/mentionsListModule.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import Vue from "vue";
|
||||
|
||||
const state = {
|
||||
array: null,
|
||||
index: 0
|
||||
};
|
||||
|
||||
const getters = {
|
||||
mentionsArray(state) {
|
||||
return state.array;
|
||||
},
|
||||
getMentionIndex(state) {
|
||||
return state.index;
|
||||
}
|
||||
};
|
||||
|
||||
const actions = {
|
||||
setMentionsArray(context, array) {
|
||||
context.commit("setMentionsArray", array);
|
||||
},
|
||||
changeIndex(context, index) {
|
||||
context.commit("changeIndex", index);
|
||||
}
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
setMentionsArray(state, array) {
|
||||
Vue.set(state, "array", array);
|
||||
},
|
||||
changeIndex(state, index) {
|
||||
Vue.set(state, "index", index);
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
};
|
||||
|
|
@ -16,7 +16,7 @@ const actions = {
|
|||
context.commit("addAllNotifications", notifications);
|
||||
},
|
||||
messageCreatedNotification(context, notification) {
|
||||
const { channelID, lastMessageID, sender } = notification;
|
||||
const { channelID, lastMessageID, sender, mentioned } = notification;
|
||||
const currentTab = context.rootGetters.currentTab;
|
||||
|
||||
// dont display a notification if the channel is selected.
|
||||
|
|
@ -33,12 +33,12 @@ const actions = {
|
|||
if (find) {
|
||||
return context.commit("messageCreatedNotification", {
|
||||
exists: true,
|
||||
notification: { channelID, lastMessageID, sender }
|
||||
notification: { channelID, lastMessageID, sender, mentioned }
|
||||
});
|
||||
}
|
||||
context.commit("messageCreatedNotification", {
|
||||
exists: false,
|
||||
notification: { channelID, lastMessageID, sender, count: 1 }
|
||||
notification: { channelID, lastMessageID, sender, count: 1, mentioned }
|
||||
});
|
||||
},
|
||||
dismissNotification(context, channelID) {
|
||||
|
|
@ -66,6 +66,13 @@ const mutations = {
|
|||
if (state.notifications[i].channelID === notification.channelID) {
|
||||
const count = state.notifications[i].count;
|
||||
Vue.set(state.notifications[i], "count", count + 1);
|
||||
if (!state.notifications[i].mentioned) {
|
||||
Vue.set(
|
||||
state.notifications[i],
|
||||
"mentioned",
|
||||
notification.mentioned
|
||||
);
|
||||
}
|
||||
Vue.set(
|
||||
state.notifications[i],
|
||||
"lastMessageID",
|
||||
|
|
|
|||
|
|
@ -217,10 +217,14 @@ const actions = {
|
|||
);
|
||||
desktopNotification();
|
||||
}
|
||||
|
||||
const notification = {
|
||||
channelID: data.message.channelID,
|
||||
lastMessageID: data.message.messageID,
|
||||
sender: data.message.creator
|
||||
sender: data.message.creator,
|
||||
mentioned: !!data.message.mentions.find(
|
||||
m => m.uniqueID === context.rootState.user.user.uniqueID
|
||||
)
|
||||
};
|
||||
context.dispatch("messageCreatedNotification", notification);
|
||||
function desktopNotification() {
|
||||
|
|
@ -496,9 +500,16 @@ const actions = {
|
|||
context.dispatch("servers/addMemberRole", { role_id, uniqueID, server_id });
|
||||
},
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
["socket_serverMember:removeRole"](context, { role_id, uniqueID, server_id }) {
|
||||
["socket_serverMember:removeRole"](
|
||||
context,
|
||||
{ role_id, uniqueID, server_id }
|
||||
) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
context.dispatch("servers/removeMemberRole", { role_id, uniqueID, server_id });
|
||||
context.dispatch("servers/removeMemberRole", {
|
||||
role_id,
|
||||
uniqueID,
|
||||
server_id
|
||||
});
|
||||
},
|
||||
["socket_server:updateRoles"](context, { roles }) {
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
const config = [
|
||||
{
|
||||
version: 9.8,
|
||||
title: "Wake up @Fishie!",
|
||||
shortTitle: "",
|
||||
date: "12/02/2020",
|
||||
new: [
|
||||
"Is someone not responding to you? Wake them up by mentioning them by typing '@' followed by their name in the message input box."
|
||||
],
|
||||
},
|
||||
{
|
||||
version: 9.7,
|
||||
title: "You have 99 mails!",
|
||||
|
|
|
|||
|
|
@ -54,10 +54,12 @@ export default {
|
|||
const customEmojis = store.state["settingsModule"].customEmojis;
|
||||
return [
|
||||
...matchSorter(customEmojis, shortCode, {
|
||||
keys: ["name"]
|
||||
keys: ["name"],
|
||||
threshold: matchSorter.rankings.CONTAINS
|
||||
}),
|
||||
...matchSorter(emojis, shortCode, {
|
||||
keys: ["shortcodes"]
|
||||
keys: ["shortcodes"],
|
||||
threshold: matchSorter.rankings.CONTAINS
|
||||
})
|
||||
];
|
||||
},
|
||||
|
|
|
|||
26
src/utils/markdown-rules/mentions.js
Normal file
26
src/utils/markdown-rules/mentions.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import * as SimpleMarkdown from "simple-markdown";
|
||||
import { store } from "@/store/index";
|
||||
|
||||
export default order => {
|
||||
return {
|
||||
order: order++,
|
||||
match: function(source) {
|
||||
return /^<@([\d]+)>/.exec(source);
|
||||
},
|
||||
|
||||
parse: function(capture) {
|
||||
return {
|
||||
id: capture[1],
|
||||
orig: capture[0]
|
||||
};
|
||||
},
|
||||
html: function(node) {
|
||||
const member = store.getters["members/members"][node.id];
|
||||
if (!member) return node.orig;
|
||||
return SimpleMarkdown.htmlTag("span", "@" + member.username, {
|
||||
class: "mention",
|
||||
id: "mention-" + member.uniqueID
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
@ -8,6 +8,7 @@ import strikeout from "./markdown-rules/strikeout";
|
|||
import inlineCodeblock from "./markdown-rules/inlineCodeblock";
|
||||
import link from "./markdown-rules/link";
|
||||
import customEmoji from "./markdown-rules/customEmoji";
|
||||
import mentions from "./markdown-rules/mentions";
|
||||
|
||||
let order = 0; // order the below rules as declared below rather than by the original defaultRules order:
|
||||
|
||||
|
|
@ -17,6 +18,7 @@ const rules = {
|
|||
underline: underline(order++),
|
||||
strikeout: strikeout(order++),
|
||||
link: link(order++),
|
||||
mentions: mentions(order++),
|
||||
customEmoji: customEmoji(order++),
|
||||
|
||||
strong: Object.assign({}, SimpleMarkdown.defaultRules.strong, {
|
||||
|
|
|
|||
Loading…
Reference in a new issue