Skip to content

Commit

Permalink
Merge pull request #165 from JsSucks/emotes-and-databases
Browse files Browse the repository at this point in the history
Emotes and databases
  • Loading branch information
Jiiks authored Mar 10, 2018
2 parents 27aa21a + b0fcedd commit ad522dd
Show file tree
Hide file tree
Showing 12 changed files with 346 additions and 26 deletions.
33 changes: 33 additions & 0 deletions client/src/builtin/Autocomplete.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* BetterDiscord Autocomplete Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

<template>
<div class="bd-autocomplete">
<div class="bd-autocomplete-inner">
<div class="bd-autocompleteRow">
<div class="bd-autocompleteSelector">
<div class="bd-autocompleteTitle">
Emotes Matching:
<strong>Kappa</strong>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import EmoteModule from '../../../builtin/Emotemodule.js';
export default {
props: ['title', 'emotes'],
beforeMount() {
console.log(EmoteModule);
}
}
</script>
54 changes: 39 additions & 15 deletions client/src/builtin/EmoteModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import { Events } from 'modules';
import { FileUtils } from 'common';
import { Events, Globals } from 'modules';
import { DOM, VueInjector } from 'ui';
import EditedTimeStamp from './EditedTimeStamp.vue';
import EmoteComponent from './EmoteComponent.vue';

import TwitchEmotes from '../data/twitch_emotes.json';

let emotes = null;

export default class {

static observe() {
Events.on('ui:mutable:.markup', markup => {
this.injectEmotes(markup);
});
static async observe() {
const dataPath = Globals.getObject('paths').find(path => path.id === 'data').path;
try {
emotes = await FileUtils.readJsonFromFile(dataPath + '/emotes.json');
Events.on('ui:mutable:.markup',
markup => {
if (!emotes) return;
this.injectEmotes(markup);
});
} catch (err) {
console.log(err);
}
}

static injectEmotes(node) {
Expand Down Expand Up @@ -84,11 +89,30 @@ export default class {
}

static isEmote(word) {
if (!emotes) return null;
const name = word.replace(/:/g, '');
if (TwitchEmotes.hasOwnProperty(name)) {
const src = `https://static-cdn.jtvnw.net/emoticons/v1/${TwitchEmotes[name]}/1.0`;
return { name, src };
}
return null;
const emote = emotes.find(emote => emote.id === name);
if (!emote) return null;
let { id, value } = emote;
if (value.id) value = value.id;
const uri = emote.type === 2 ? 'https://cdn.betterttv.net/emote/:id/1x' : emote.type === 1 ? 'https://cdn.frankerfacez.com/emoticon/:id/1' : 'https://static-cdn.jtvnw.net/emoticons/v1/:id/1.0';
return { name, src: uri.replace(':id', value) };
}

static filterTest() {
const re = new RegExp('Kappa', 'i');
const filtered = emotes.filter(emote => re.test(emote.id));
return filtered.slice(0, 10);
}

static filter(regex, limit) {
let index = 0;
return emotes.filter(emote => {
if (index >= limit) return false;
if (regex.test(emote.id)) {
index++;
return true;
}
});
}
}
1 change: 1 addition & 0 deletions client/src/data/emotes.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions client/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import BdCss from './styles/index.scss';
import { Events, CssEditor, Globals, ExtModuleManager, PluginManager, ThemeManager, ModuleManager, WebpackModules, Settings, Database } from 'modules';
import { ClientLogger as Logger, ClientIPC } from 'common';
import { EmoteModule } from 'builtin';
const ignoreExternal = false;
const ignoreExternal = true;

class BetterDiscord {

Expand All @@ -31,7 +31,6 @@ class BetterDiscord {
window.bdlogs = Logger;
window.emotes = EmoteModule;
window.dom = DOM;
EmoteModule.observe();

DOM.injectStyle(BdCss, 'bdmain');
Events.on('global-ready', this.globalReady.bind(this));
Expand All @@ -52,6 +51,7 @@ class BetterDiscord {
Modals.showContentManagerErrors();
Events.emit('ready');
Events.emit('discord-ready');
EmoteModule.observe();
} catch (err) {
Logger.err('main', ['FAILED TO LOAD!', err]);
}
Expand Down
76 changes: 76 additions & 0 deletions client/src/styles/partials/generic/autocomplete.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
.bd-autocomplete {
border-radius: 5px 5px 0 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
z-index: 3;
bottom: 100%;
left: 0;
position: absolute;
right: 0;
background-color: #2f3136;

.bd-autocomplete-inner {
padding-bottom: 8px;
white-space: nowrap;

.bd-autocompleteRow {
padding: 0 8px;
font-size: 14px;
line-height: 16px;

.bd-autocompleteSelector {
border-radius: 3px;
padding: 8px;

&.bd-selectable {
cursor: pointer;
}

&.bd-selected {
background-color: #36393f;
}

.bd-autocompleteTitle {
color: #72767d;
padding: 4px 0;
text-transform: uppercase;
font-weight: 600;
line-height: 16px;
font-size: 12px;

strong {
color: #fff;
text-transform: none;
font-weight: 500;
}
}

.bd-autocompleteField {
display: flex;
flex: 1 1 auto;
color: #f6f6f7;
min-height: 16px;
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
flex-direction: row;
flex-wrap: nowrap;
-webkit-box-pack: start;
justify-content: flex-start;
-webkit-box-align: center;
align-items: center;

img {
min-width: 16px;
width: 16px;
}

div {
margin-left: 8px;
color: #f6f6f7;
}
}
}
}
}
}
1 change: 1 addition & 0 deletions client/src/styles/partials/generic/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
@import './drawers.scss';
@import './preformatted.scss';
@import './refreshbtn.scss';
@import './autocomplete.scss';
21 changes: 20 additions & 1 deletion client/src/ui/automanip.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Reflection from './reflection';
import DOM from './dom';
import VueInjector from './vueinjector';
import EditedTimeStamp from './components/common/EditedTimestamp.vue';
import Autocomplete from './components/common/Autocomplete.vue';

class TempApi {
static get currentGuildId() {
Expand Down Expand Up @@ -42,6 +43,7 @@ export default class extends EventListener {

constructor() {
super();
window.injectAc = this.injectAutocomplete;
const messageFilter = function (m) {
return m.addedNodes && m.addedNodes.length && m.addedNodes[0].classList && m.addedNodes[0].classList.contains('message-group');
}
Expand Down Expand Up @@ -75,7 +77,8 @@ export default class extends EventListener {
{ id: 'server-switch', callback: this.manipAll },
{ id: 'channel-switch', callback: this.manipAll },
{ id: 'discord:MESSAGE_CREATE', callback: this.markupInjector },
{ id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector }
{ id: 'discord:MESSAGE_UPDATE', callback: this.markupInjector },
{ id: 'gkh:keyup', callback: this.injectAutocomplete }
];
}

Expand Down Expand Up @@ -195,4 +198,20 @@ export default class extends EventListener {
get appMount() {
return document.getElementById('app-mount');
}

injectAutocomplete(e) {
if (document.querySelector('.bd-autocomplete')) return;
if (!e.target.closest('[class*=channelTextArea]')) return;
const root = document.createElement('span');
const parent = document.querySelector('[class*="channelTextArea"] > [class*="inner"]');
if (!parent) return;
parent.append(root);
VueInjector.inject(
root,
DOM.createElement('span'),
{ Autocomplete },
`<Autocomplete initial="${e.target.value}"/>`,
true
);
}
}
1 change: 1 addition & 0 deletions client/src/ui/bdui.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default class {
server: TempApi.currentGuild,
channel: TempApi.currentChannel
};
window.addEventListener('keyup', e => Events.emit('gkh:keyup', e));
this.autoManip = new AutoManip();
const defer = setInterval(() => {
if (!this.profilePopupModule) return;
Expand Down
128 changes: 128 additions & 0 deletions client/src/ui/components/common/Autocomplete.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/**
* BetterDiscord Autocomplete Component
* Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks
* All rights reserved.
* https://betterdiscord.net
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

<template>
<div class="bd-autocomplete">
<div v-if="emotes && emotes.length" class="bd-autocomplete-inner">
<div class="bd-autocompleteRow">
<div class="bd-autocompleteSelector">
<div class="bd-autocompleteTitle">
Emotes Matching:
<strong>{{title}}</strong>
</div>
</div>
</div>
<div v-for="(emote, index) in emotes" class="bd-autocompleteRow" :key="emote.id">
<div class="bd-autocompleteSelector bd-selectable" :class="{'bd-selected': index === selectedIndex}" @mouseover="() => { selected = emote.id }" @click="() => inject(emote)">
<div class="bd-autocompleteField">
<img :src="getEmoteSrc(emote)"/>
<div>{{emote.id}}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { EmoteModule } from 'builtin';
import { Events } from 'modules';
import { DOM } from 'ui';
export default {
data() {
return {
emotes: [],
title: '',
selIndex: 0,
selected: '',
open: false,
selectedIndex: 0,
sterm: ''
}
},
props: ['initial'],
beforeMount() {
// this.emotes = EmoteModule.filter(new RegExp(this.initial, 'i'), 10);
// this.open = this.emotes.length;
},
created() {
window.addEventListener('keydown', this.prevents);
const ta = document.querySelector('.chat textarea');
ta.addEventListener('keydown', this.setCaret);
ta.addEventListener('keyup', this.searchEmotes);
},
destroyed() {
window.removeEventListener('keydown', this.prevents);
const ta = document.querySelector('.chat textarea');
ta.removeEventListener('keydown', this.setCaret);
ta.removeEventListener('keyup', this.searchEmotes);
},
methods: {
prevents(e) {
if (!this.open) return;
if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp' && e.key !== 'Tab') return;
e.stopPropagation();
e.preventDefault();
},
setCaret(e) {
this.caret = e.target.selectionEnd;
},
getEmoteSrc(emote) {
let { id, value } = emote;
if (value.id) value = value.id;
const uri = emote.type === 2 ? 'https://cdn.betterttv.net/emote/:id/1x' : emote.type === 1 ? 'https://cdn.frankerfacez.com/emoticon/:id/1' : 'https://static-cdn.jtvnw.net/emoticons/v1/:id/1.0';
return uri.replace(':id', value);
},
searchEmotes(e) {
if (e.key === 'ArrowDown' && this.open && this.caret) {
this.selectedIndex = (this.selectedIndex + 1) >= 10 ? 0 : this.selectedIndex + 1;
return;
} else if (e.key === 'ArrowUp' && this.open && this.caret) {
this.selectedIndex = (this.selectedIndex - 1) < 0 ? 9 : this.selectedIndex - 1;
return;
}
if (e.key === 'Tab' && this.open && this.caret) {
const selected = this.emotes[this.selectedIndex];
if (!selected) return;
this.inject(selected);
return;
}
const se = e.target.selectionEnd;
this.sterm = e.target.value.substr(0, se).split(' ').slice(-1).pop();
if (this.sterm.length < 3) {
this.emotes = [];
this.selected = '';
this.selectedIndex = 0;
return;
}
this.title = this.sterm;
this.emotes = EmoteModule.filter(new RegExp(this.sterm, ''), 10);
this.open = this.emotes.length;
},
inject(emote) {
const ta = document.querySelector('.chat textarea');
if (!ta) return;
const currentText = document.querySelector('.chat textarea').value;
const se = ta.selectionEnd;
const split = currentText.substr(0, se).split(' ');
split.pop();
split.push(`:${emote.id}:`);
const join = split.join(' ');
const rest = currentText.substr(se, currentText.length);
DOM.manip.setText(join + ' ' + rest, false);
this.emotes = [];
this.open = false;
this.selectedIndex = 0;
this.selected = '';
ta.selectionEnd = ta.selectionStart = se + `:${emote.id}:`.length - this.title.length;
}
}
}
</script>
Loading

0 comments on commit ad522dd

Please sign in to comment.