mirror of
https://github.com/danbulant/Nertivia-Client
synced 2026-06-16 21:11:16 +00:00
emoji panel added
This commit is contained in:
parent
ec37b3ac92
commit
a9276faf4b
6 changed files with 249 additions and 25 deletions
|
|
@ -4,7 +4,7 @@
|
|||
<span class="news-title">Changes in this release</span>
|
||||
|
||||
<div class="change" v-for="(change, index) in changelog" :key="change.title">
|
||||
<div :class="`heading ${index === 0 ? 'latest': ''}`">
|
||||
<div class="heading" :style="change.headColor ? `background-color: ${change.headColor}` : ``">
|
||||
<div class="date">{{change.date}}</div>
|
||||
<div class="changes-title">{{change.title}}</div>
|
||||
</div>
|
||||
|
|
@ -34,19 +34,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="todo-list">
|
||||
<span class="news-title">Planned Features</span>
|
||||
<p>Features that are coming soon:</p>
|
||||
<ul class="plan-list">
|
||||
<li>Online, Offline status(Done)</li>
|
||||
<li>Profile picture (done)</li>
|
||||
<li>Typing indicator (done)</li>
|
||||
<li>Sending files (done)</li>
|
||||
<li>Custom emojis</li>
|
||||
<li>Guilds</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -96,6 +83,7 @@ export default {
|
|||
padding: 10px;
|
||||
background: rgba(0, 0, 0, 0.555);
|
||||
margin-bottom: 10px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.heading.latest {
|
||||
background: rgba(38, 139, 255, 0.87);
|
||||
|
|
|
|||
|
|
@ -31,8 +31,10 @@
|
|||
</div>
|
||||
<news v-if="!selectedChannelID "/>
|
||||
<div class="chat-input-area" v-if="selectedChannelID">
|
||||
<div class="emoji-suggestion-outer" v-if="emojiArray">
|
||||
<emoji-suggestions :emojiArray="emojiArray"/>
|
||||
|
||||
<div style="position: relative;" >
|
||||
<emoji-suggestions v-if="emojiArray" :emojiArray="emojiArray"/>
|
||||
<emoji-panel v-if="showEmojiPanel"/>
|
||||
</div>
|
||||
|
||||
<div class="message-area">
|
||||
|
|
@ -52,10 +54,14 @@
|
|||
v-model="message"
|
||||
@paste="onPaste"
|
||||
></textarea>
|
||||
<button
|
||||
class="emojis-button"
|
||||
@click="showEmojiPanel = !showEmojiPanel">
|
||||
<i class="material-icons">face</i>
|
||||
</button>
|
||||
<button
|
||||
:class="{'send-button': true, 'error-send-button': messageLength > 5000}"
|
||||
@click="sendMessage"
|
||||
>
|
||||
@click="sendMessage">
|
||||
<i class="material-icons">send</i>
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -85,6 +91,7 @@ import Spinner from "@/components/Spinner.vue";
|
|||
import TypingStatus from "@/components/app/TypingStatus.vue";
|
||||
import uploadsQueue from "@/components/app/uploadsQueue.vue";
|
||||
import emojiSuggestions from "@/components/app/emojiSuggestions.vue";
|
||||
import emojiPanel from "@/components/app/emojiPanel.vue";
|
||||
import emojiParser from "@/utils/emojiParser.js";
|
||||
|
||||
export default {
|
||||
|
|
@ -94,7 +101,8 @@ export default {
|
|||
News,
|
||||
TypingStatus,
|
||||
uploadsQueue,
|
||||
emojiSuggestions
|
||||
emojiSuggestions,
|
||||
emojiPanel
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -103,7 +111,8 @@ export default {
|
|||
postTimerID: null,
|
||||
getTimerID: null,
|
||||
typing: false,
|
||||
whosTyping: ""
|
||||
whosTyping: "",
|
||||
showEmojiPanel: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -267,7 +276,12 @@ export default {
|
|||
const end = cursorPosition;
|
||||
|
||||
this.message = this.message.substring(0, start) + emojiShortCode + this.message.substring(end);
|
||||
return (this.$store.dispatch('setEmojiArray', null));
|
||||
this.$store.dispatch('setEmojiArray', null);
|
||||
},
|
||||
enterEmojiPanel(shortcode){
|
||||
const target = this.$refs["input-box"];
|
||||
target.focus();
|
||||
document.execCommand('insertText', false, `:${shortcode}:`);
|
||||
},
|
||||
keyDown(event) {
|
||||
this.resize(event);
|
||||
|
|
@ -353,6 +367,7 @@ export default {
|
|||
};
|
||||
bus.$on("newMessage", this.hideTypingStatus);
|
||||
bus.$on("emojiSuggestions:Selected", this.enterEmojiSuggestion)
|
||||
bus.$on("emojiPanel:Selected", this.enterEmojiPanel)
|
||||
//dismiss notification on focus
|
||||
window.onfocus = () => {
|
||||
bus.$emit("title:change", "Nertivia");
|
||||
|
|
@ -370,6 +385,7 @@ export default {
|
|||
beforeDestroy() {
|
||||
bus.$off("newMessage", this.hideTypingStatus);
|
||||
bus.$off("emojiSuggestions:Selected", this.enterEmojiSuggestion)
|
||||
bus.$on("emojiPanel:Selected", this.enterEmojiPanel)
|
||||
delete this.$options.sockets.typingStatus;
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -442,9 +458,6 @@ export default {
|
|||
display: flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.emoji-suggestion-outer {
|
||||
position: relative;
|
||||
}
|
||||
.show-menu-button {
|
||||
display: inline-block;
|
||||
margin: auto;
|
||||
|
|
@ -506,6 +519,7 @@ export default {
|
|||
cursor: default;
|
||||
user-select: none;
|
||||
transition: 0.3s;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.attachment-button:hover {
|
||||
background: rgba(0, 0, 0, 0.322);
|
||||
|
|
@ -553,6 +567,7 @@ export default {
|
|||
overflow: hidden;
|
||||
max-height: 30vh;
|
||||
overflow-y: auto;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.chat-input:hover {
|
||||
|
|
@ -576,6 +591,8 @@ export default {
|
|||
transition: 0.3s;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
border-radius: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
.send-button .material-icons {
|
||||
margin: auto;
|
||||
|
|
@ -590,6 +607,29 @@ export default {
|
|||
.error-send-button:hover {
|
||||
background-color: rgba(255, 0, 0, 0.294);
|
||||
}
|
||||
.emojis-button{
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
background: rgba(0, 0, 0, 0.274);
|
||||
border: none;
|
||||
outline: none;
|
||||
margin-left: 2px;
|
||||
|
||||
min-height: 40px;
|
||||
width: 50px;
|
||||
transition: 0.3s;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
border-radius: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.emojis-button .material-icons {
|
||||
margin: auto;
|
||||
}
|
||||
.emojis-button:hover {
|
||||
background: rgba(0, 0, 0, 0.514);
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.show-menu-button {
|
||||
display: block;
|
||||
|
|
|
|||
123
src/components/app/emojiPanel.vue
Normal file
123
src/components/app/emojiPanel.vue
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
<template>
|
||||
<div class="emoji-panel">
|
||||
<div class="emoji-panel-inner">
|
||||
<div class="emojis-list">
|
||||
<div class="category" v-for="(group, index) in groups" :key="group">
|
||||
<div class="category-name">{{group}}</div>
|
||||
<div class="list">
|
||||
<div
|
||||
class="emoji-item"
|
||||
v-for="emojiSorted in emojiByGroup(index)"
|
||||
:key="emojiSorted.shortcodes[0]"
|
||||
@click="clickEvent(emojiSorted.shortcodes[0])">
|
||||
<img class="panel emoji" v-lazyload :data-url="parseEmojiPath(emojiSorted.unicode)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="triangle"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { bus } from "@/main";
|
||||
import emojiParser from "@/utils/emojiParser.js";
|
||||
import lazyLoad from "@/directives/LazyLoad.js"
|
||||
|
||||
import emojis from "emojibase-data/en/compact.json";
|
||||
import { groups } from "emojibase-data/meta/groups.json";
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
lazyload: lazyLoad
|
||||
},
|
||||
methods: {
|
||||
emojiByGroup(index) {
|
||||
index = parseInt(index);
|
||||
return emojis.filter(_emoji => _emoji.group === index);
|
||||
},
|
||||
parseEmojiPath(emoji) {
|
||||
return emojiParser.GetEmojiPath(emoji);
|
||||
},
|
||||
clickEvent(shortcode) {
|
||||
bus.$emit('emojiPanel:Selected', shortcode)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
groups() {
|
||||
return groups;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.emoji-panel {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
right: 20px;
|
||||
width: 370px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.emoji-panel-inner{
|
||||
background: rgba(32, 32, 32, 0.87);
|
||||
transition: 0.3s;
|
||||
border-radius: 5px;
|
||||
padding: 3px;
|
||||
}
|
||||
.emoji-panel-inner:hover {
|
||||
background: rgb(32, 32, 32);
|
||||
}
|
||||
.emojis-list {
|
||||
color: white;
|
||||
padding: 5px;
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
height: 300px;
|
||||
overflow-y: auto;
|
||||
transition: 0.32s;
|
||||
}
|
||||
|
||||
.category {
|
||||
}
|
||||
.category-name {
|
||||
padding: 10px;
|
||||
}
|
||||
.list {
|
||||
}
|
||||
.emoji-item {
|
||||
background: rgba(59, 59, 59, 0.521);
|
||||
transition: 0.3s;
|
||||
display: inline-block;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
border-radius: 5px;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
|
||||
}
|
||||
.emoji-item:hover {
|
||||
background: rgb(59, 59, 59);
|
||||
}
|
||||
.triangle {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 10px solid transparent;
|
||||
border-right: 10px solid transparent;
|
||||
border-top: 15px solid rgba(32, 32, 32, 0.87);
|
||||
|
||||
align-self: flex-end;
|
||||
margin-right: 70px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
img.panel.emoji {
|
||||
margin-left: 3px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
</style>
|
||||
49
src/directives/LazyLoad.js
Normal file
49
src/directives/LazyLoad.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
export default {
|
||||
inserted: el => {
|
||||
function loadImage() {
|
||||
const imageElement = el
|
||||
if (imageElement) {
|
||||
imageElement.addEventListener("load", () => {
|
||||
setTimeout(() => el.classList.add("loaded"), 100);
|
||||
});
|
||||
imageElement.addEventListener("error", () => console.log("error"));
|
||||
imageElement.src = imageElement.dataset.url;
|
||||
}
|
||||
}
|
||||
|
||||
function handleIntersect(entries, observer) {
|
||||
entries.forEach(entry => {
|
||||
if (!entry.isIntersecting) {
|
||||
return;
|
||||
} else {
|
||||
loadImage();
|
||||
observer.unobserve(el);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function createObserver() {
|
||||
const options = {
|
||||
// circumstances under which the observer's callback is invoked
|
||||
root: null, // defaults to the browser viewport if not specified or if null
|
||||
threshold: "0" // the degree of intersection between the target element and its root (0 - 1)
|
||||
// threshold of 1.0 means that when 100% of the target is visible within
|
||||
//the element specified by the root option, the callback is invoked
|
||||
};
|
||||
|
||||
// Whether you're using the viewport or some other element as the root,the API works the same way,
|
||||
// executing a callback function you provide whenever the visibility of the target element changes
|
||||
// so that it crosses desired amounts of intersection with the root
|
||||
|
||||
const observer = new IntersectionObserver(handleIntersect, options);
|
||||
|
||||
observer.observe(el); // target element to watch
|
||||
}
|
||||
|
||||
if (!window["IntersectionObserver"]) {
|
||||
loadImage();
|
||||
} else {
|
||||
createObserver();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,8 +1,24 @@
|
|||
const config = [
|
||||
{
|
||||
title: 'Emojis :D',
|
||||
shortTitle: 'Emojis',
|
||||
date: '20/03/2019',
|
||||
headColor: "rgba(17, 153, 69, 0.87)",
|
||||
new: [
|
||||
'Emoji suggestions in chat when typing in any emoji :ok_hand:',
|
||||
'Emoji picker',
|
||||
'Removed planned features from changelog'
|
||||
],
|
||||
next: [
|
||||
'make tabs in emoji panel',
|
||||
'Custom emojis for freeeee!',
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Upload anything!',
|
||||
shortTitle: 'Upload anything!',
|
||||
date: '08/03/2019',
|
||||
headColor: "rgba(38, 139, 255, 0.87)",
|
||||
new: [
|
||||
'You can now upload any kind of files to friends. (Google drive required)',
|
||||
'Shift + enter should expand the text area.',
|
||||
|
|
|
|||
|
|
@ -23,7 +23,15 @@ export default {
|
|||
return require("twemoji/2/svg/" + icon + ".svg")
|
||||
})
|
||||
},
|
||||
|
||||
GetEmojiPath: (string) => {
|
||||
let emojiPath;
|
||||
twemoji.parse(string,
|
||||
function (icon, options, variant) {
|
||||
if (!icon) return string;
|
||||
emojiPath = require("twemoji/2/svg/" + icon + ".svg")
|
||||
})
|
||||
return emojiPath;
|
||||
},
|
||||
searchEmoji: (shortCode) => {
|
||||
return matchSorter(emojis, shortCode, {keys: ['shortcodes']});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue