emoji panel added

This commit is contained in:
supertiger 2019-03-20 21:01:33 +00:00
parent ec37b3ac92
commit a9276faf4b
6 changed files with 249 additions and 25 deletions

View file

@ -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);

View file

@ -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;

View 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>

View 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();
}
}
};

View file

@ -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.',

View file

@ -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']});
}