improved emoji suggestions open method

This commit is contained in:
supertiger 2019-03-16 17:57:44 +00:00
parent 7ba62da699
commit 41ce5ae36a
5 changed files with 206 additions and 101 deletions

View file

@ -3,74 +3,72 @@
<div class="change-log">
<span class="news-title">Changes in this release</span>
<div class="change">
<div class="date">{{changelog.date}}</div>
<div class="changes-title">{{changelog.title}}</div>
<div class="information">
<div v-if="changelog.new">
<strong>What's new?</strong><br>
<ul>
<li v-for="(wnew, index) in changelog.new" :key="index">{{wnew}}</li>
</ul>
</div>
<div v-if="changelog.fix">
<strong>Issues fixed</strong><br>
<ul>
<li v-for="(wfix, index) in changelog.fix" :key="index">{{wfix}}</li>
</ul>
</div>
<div v-if="changelog.next">
<strong>Up next</strong><br>
<ul>
<li v-for="(wnext, index) in changelog.next" :key="index">{{wnext}}</li>
</ul>
</div>
<div v-if="changelog.msg">
{{changelog.msg}}
</div>
<div class="change" v-for="(change, index) in changelog" :key="change.title">
<div :class="`heading ${index === 0 ? 'latest': ''}`">
<div class="date">{{change.date}}</div>
<div class="changes-title">{{change.title}}</div>
</div>
<div class="information">
<div v-if="change.new">
<strong>What's new?</strong>
<br>
<ul>
<li v-for="(wnew, index) in change.new" :key="index">{{wnew}}</li>
</ul>
</div>
</div>
<div v-if="change.fix">
<strong>Issues fixed</strong>
<br>
<ul>
<li v-for="(wfix, index) in change.fix" :key="index">{{wfix}}</li>
</ul>
</div>
<div v-if="change.next">
<strong>Up next</strong>
<br>
<ul>
<li v-for="(wnext, index) in change.next" :key="index">{{wnext}}</li>
</ul>
</div>
<div v-if="change.msg">{{change.msg}}</div>
</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</li>
<li>Typing indicator</li>
<li>Sending files</li>
<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>
<script>
import Spinner from '@/components/Spinner.vue'
import ChangeLog from '@/components/ChangeLog.vue'
import changelog from '@/utils/changelog.js'
import Spinner from "@/components/Spinner.vue";
import changelog from "@/utils/changelog.js";
export default {
components: {},
data() {
return {
changelog: changelog[0]
}
changelog: changelog
};
}
}
};
</script>
<style scoped>
.news {
display: flex;
flex: 1;
margin:20px;
margin: 20px;
color: white;
overflow: auto;
}
@ -83,21 +81,35 @@ export default {
padding-bottom: 10px;
border-bottom: solid 1px white;
}
.todo-list{
.todo-list {
flex: 1;
margin-left: 10px;
background: rgba(0, 0, 0, 0.137);
padding: 20px;
}
.change-log{
.change {
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: solid 1px white;
}
.heading{
padding: 10px;
background: rgba(0, 0, 0, 0.555);
margin-bottom: 10px;
}
.heading.latest {
background: rgba(38, 139, 255, 0.87);
}
.change-log {
background: rgba(0, 0, 0, 0.137);
padding: 20px;
flex: 1;
overflow-y: auto;
}
.plan-list{
.plan-list {
color: white;
}
.date{
.date {
text-align: left;
margin-right: 50px;
color: rgba(255, 255, 255, 0.692);
@ -114,12 +126,11 @@ export default {
.news {
flex-direction: column;
}
.todo-list{
.todo-list {
margin-left: 0;
}
.change-log {
margin-bottom: 20px;
}
}
</style>

View file

@ -9,13 +9,13 @@
<span v-else>{{channelName}}</span>
</div>
</div>
<div class="loading" v-if="selectedChannelID && !messages[selectedChannelID]">
<div class="loading" v-if="selectedChannelID && !selectedChannelMessages">
<spinner/>
</div>
<div v-else-if="selectedChannelID" class="message-logs" @wheel="invertScroll">
<div class="scroll">
<message
v-for="(msg, index) in messages[selectedChannelID]"
v-for="(msg, index) in selectedChannelMessages"
:key="index"
:date="msg.created"
:admin="msg.creator.admin"
@ -29,11 +29,12 @@
<uploadsQueue v-if="uploadQueue !== undefined" :queue="uploadQueue"/>
</div>
</div>
<news v-if="!selectedChannelID && !messages[selectedChannelID]"/>
<news v-if="!selectedChannelID "/>
<div class="chat-input-area" v-if="selectedChannelID">
<div class="emoji-suggestion-outer" v-if="showEmojiSuggestions">
<emoji-suggestions :emojiArray="emojiSuggestionsArray"/>
</div>
<div class="message-area">
<input type="file" ref="sendFileBrowse" @change="attachmentChange" class="hidden">
<div class="attachment-button" @click="attachmentButton">
@ -44,8 +45,8 @@
rows="1"
ref="input-box"
placeholder="Message"
@keydown="chatInput"
@keyup="delayedResize"
@keydown="keyDown"
@keyup="keyUp"
@change="resize"
@input="onInput"
v-model="message"
@ -131,6 +132,7 @@ export default {
if (this.message == "") return;
if (this.message.length > 5000) return;
this.showEmojiSuggestions = false;
clearInterval(this.postTimerID);
this.postTimerID = null;
this.messageLength = 0;
@ -150,6 +152,10 @@ export default {
});
this.message = "";
let input = this.$refs["input-box"];
input.style.height = "1em";
this.$store.dispatch("updateChannelLastMessage", this.selectedChannelID);
const { ok, error, result } = await messagesService.post(
this.selectedChannelID,
@ -191,21 +197,52 @@ export default {
input.style.height = `calc(${input.scrollHeight}px - 1em)`;
}
},
delayedResize(event) {
this.resize(event);
emojiSwitchKey(event) {
if (!this.showEmojiSuggestions) return;
const keyCode = event.keyCode;
if (keyCode == 38) {
//up
bus.$emit("emojiSuggestions:up");
event.preventDefault();
return;
}
if (keyCode == 40) {
//down
bus.$emit("emojiSuggestions:down");
event.preventDefault();
return;
}
},
showEmojiPopout() {
const shortcode = this.message.split(" ").pop();
if (!shortcode || !shortcode.startsWith(":") || shortcode.endsWith(":") || shortcode.length < 3)
return (this.showEmojiSuggestions = false);
const searchArr = emojiParser.searchEmoji(shortcode.slice(1, -1));
if (searchArr.length <= 0) return (this.showEmojiSuggestions = false);
this.showEmojiSuggestions = true;
this.emojiSuggestionsArray = searchArr;
GetWordByPos(str, pos) {
let left = str.substr(0, pos);
let right = str.substr(pos);
left = left.replace(/^.+ /g, "");
right = right.replace(/ .+$/g, "");
return left + right;
},
showEmojiPopout(event) {
if (event.keyCode == 38 || event.keyCode == 40) return; // up/down
const cursorPosition = event.target.selectionStart;
const cursorWord = this.GetWordByPos(this.message, cursorPosition)
const cursorLetter = this.message.substring(cursorPosition - 1, cursorPosition)
if (cursorLetter.trim() == "" || cursorWord.endsWith(":"))
return this.showEmojiSuggestions = false;
if (cursorWord.startsWith(":") && cursorWord.length >= 3) {
const searchArr = emojiParser.searchEmoji(cursorWord.slice(1, -1));
if (searchArr.length <= 0) return (this.showEmojiSuggestions = false);
this.emojiSuggestionsArray = searchArr;
this.showEmojiSuggestions = true;
}
},
async onInput(event) {
this.showEmojiPopout();
this.delayedResize(event);
this.resize(event);
this.messageLength = this.message.length;
const value = event.target.value.trim();
if (value && this.postTimerID == null) {
@ -213,9 +250,13 @@ export default {
await typingService.post(this.selectedChannelID);
}
},
chatInput(event) {
this.delayedResize(event);
keyUp(event) {
this.resize(event);
this.showEmojiPopout(event);
},
keyDown(event) {
this.resize(event);
this.emojiSwitchKey(event);
// when enter is press
if (event.keyCode == 13) {
// and the shift key is not held
@ -331,8 +372,9 @@ export default {
channel() {
return this.$store.getters.channels[this.selectedChannelID];
},
messages() {
return this.$store.getters.messages;
selectedChannelMessages() {
const selectedChannel = this.$store.getters.selectedChannelID;
return this.$store.getters.messages[selectedChannel];
},
selectedChannelID() {
return this.$store.getters.selectedChannelID;

View file

@ -1,27 +1,75 @@
<template>
<div class="emoji-suggetions-list">
<div class="emoji" v-for="emoji in $props.emojiArray" :key="emoji.hexcode">
<div class="preview">{{emoji.unicode}}</div>
<div
v-for="(emoji, index) in $props.emojiArray.slice(0,10)"
:class="{emoji: true, selected: index === selectedIndex}"
@mouseover="hoverEvent"
:key="emoji.hexcode"
>
<div class="preview" v-html="emojiParser(emoji.unicode)"></div>
<div class="short-code">:{{emoji.shortcodes[0]}}:</div>
</div>
</div>
</template>
<script>
import { bus } from "@/main";
import emojiParser from "@/utils/emojiParser.js";
export default {
props: ['emojiArray'],
methods: {},
computed: {}
props: ["emojiArray"],
data() {
return {
selectedIndex: 0
};
},
methods: {
emojiParser(emoji) {
return emojiParser.replaceEmojis(emoji);
},
hoverEvent(event) {
const emoji = event.target.closest(".emoji");
const parent = event.target.parentElement.children;
if (!emoji || !emoji) return;
const index = [...parent].findIndex(el => el === emoji);
if (index >= 0) this.selectedIndex = index;
},
KeySwitch(key) {
if (key == "up") {
if (this.selectedIndex == 0)
return (this.selectedIndex =
this.$props.emojiArray.slice(0, 10).length - 1);
this.selectedIndex--;
}
if (key == "down") {
if (
this.selectedIndex ==
this.$props.emojiArray.slice(0, 10).length - 1
)
return (this.selectedIndex = 0);
this.selectedIndex++;
}
},
},
mounted() {
bus.$on("emojiSuggestions:up", () => this.KeySwitch("up"));
bus.$on("emojiSuggestions:down", () => this.KeySwitch("down"));
},
watch: {
emojiArray() {
this.selectedIndex = 0;
}
}
};
</script>
<style scoped>
.emoji-suggetions-list{
.selected {
background: rgba(66, 66, 66, 0.89);
}
.emoji-suggetions-list {
position: absolute;
bottom: 0;
left: 70px;
@ -36,24 +84,27 @@ export default {
user-select: none;
cursor: default;
}
.emoji-suggetions-list:hover{
.emoji-suggetions-list:hover {
background: rgba(32, 32, 32, 0.966);
}
.preview{
.preview {
margin-right: 5px;
}
.name{
.name {
flex: 1;
}
.short-code {
margin-right: 10px;
}
.emoji{
.emoji {
display: flex;
padding: 5px;
align-content: center;
align-items: center;
}
.emoji.selected {
@media (max-height: 441px) {
.emoji-suggetions-list {
max-height: 150px;
}
}
</style>

View file

@ -1,3 +1,4 @@
import twemoji from "twemoji";
import emojis from "emojibase-data/en/compact.json";
import matchSorter from "match-sorter";
import {
@ -15,17 +16,15 @@ export default {
return x
});
},
replaceEmojis: (string) => {
return twemoji.parse(string,
function (icon, options, variant) {
if (!icon) return string;
return require("twemoji/2/svg/" + icon + ".svg")
})
},
searchEmoji: (shortCode) => {
let array = []
for (let index = 0; index < emojis.length; index++) {
const element = emojis[index];
for (let i = 0; i < element.shortcodes.length; i++) {
const el2 = element.shortcodes[i];
if (el2.includes(shortCode)) array.push(element);
}
}
return matchSorter(emojis, shortCode, {keys: ['shortcodes']});
}
}

View file

@ -1,15 +1,11 @@
import futoji from 'futoji'
import twemoji from 'twemoji'
import emojiParser from '@/utils/emojiParser';
export default (message) => {
message = twemoji.parse(escapeHtml(message),
function (icon, options, variant) {
if (!icon) return message;
return require("twemoji/2/svg/" + icon + ".svg")
})
futoji.addTransformer({
name: 'bold-and-italic',
@ -58,7 +54,13 @@ export default (message) => {
recursive: false,
transformer: text => `<code>${text}</code>`,
})
return futoji.format(message);
message = futoji.format(escapeHtml(message));
message = emojiParser.replaceEmojis(message);
return message;
}
/**