Initial autocomplete and all emotes
This commit is contained in:
parent
27aa21a47a
commit
523d226b91
|
@ -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>
|
|
@ -7,18 +7,18 @@
|
|||
* 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() {
|
||||
static async observe() {
|
||||
const dataPath = Globals.getObject('paths').find(path => path.id === 'data').path;
|
||||
emotes = await FileUtils.readJsonFromFile(dataPath + '/emotes.json');
|
||||
window.emotee = emotes;
|
||||
Events.on('ui:mutable:.markup', markup => {
|
||||
this.injectEmotes(markup);
|
||||
});
|
||||
|
@ -84,11 +84,19 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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 {
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,3 +8,4 @@
|
|||
@import './drawers.scss';
|
||||
@import './preformatted.scss';
|
||||
@import './refreshbtn.scss';
|
||||
@import './autocomplete.scss';
|
||||
|
|
|
@ -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() {
|
||||
|
@ -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');
|
||||
}
|
||||
|
@ -195,4 +197,18 @@ export default class extends EventListener {
|
|||
get appMount() {
|
||||
return document.getElementById('app-mount');
|
||||
}
|
||||
|
||||
injectAutocomplete() {
|
||||
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/>`,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* 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>Kappa</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="emote in emotes" class="bd-autocompleteRow">
|
||||
<div class="bd-autocompleteSelector">
|
||||
<div class="bd-autocompleteField">
|
||||
<img :src="getEmoteSrc(emote)"/>
|
||||
<div>{{emote.id}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { EmoteModule } from 'builtin';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
emotes: []
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.emotes = EmoteModule.filterTest();
|
||||
},
|
||||
methods: {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -12,13 +12,16 @@ import Vue from './vue';
|
|||
|
||||
export default class {
|
||||
|
||||
static inject(root, bdnode, components, template, replaceRoot) {
|
||||
static inject(root, bdnode, components, template, replaceRoot, data) {
|
||||
if(!replaceRoot) bdnode.appendTo(root);
|
||||
|
||||
return new Vue({
|
||||
el: replaceRoot ? root : bdnode.element,
|
||||
components,
|
||||
template
|
||||
template,
|
||||
data: {
|
||||
data
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,7 @@ const dummyArgs = {
|
|||
{ 'id': 'modules', 'path': __modulePath }
|
||||
]
|
||||
};
|
||||
|
||||
const dbInstance = new Database(dummyArgs.paths.find(path => path.id === 'data').path + '/storage');
|
||||
|
||||
const dbInstance = new Database(dummyArgs.paths.find(path => path.id === 'data').path);
|
||||
console.log(dummyArgs);
|
||||
|
||||
|
||||
|
|
|
@ -15,23 +15,41 @@ class Database {
|
|||
constructor(dbPath) {
|
||||
this.exec = this.exec.bind(this);
|
||||
this.update = this.update.bind(this);
|
||||
this.db = new Datastore({ filename: dbPath, autoload: true });
|
||||
this.init(dbPath);
|
||||
//this.db.emotes.loadDatabase();
|
||||
//this.db = new Datastore({ filename: dbPath, autoload: true });
|
||||
}
|
||||
|
||||
async init(dbPath) {
|
||||
this.db = {
|
||||
storage: new Datastore({ filename: `${dbPath}/storage`, autoload: true })
|
||||
};
|
||||
}
|
||||
|
||||
async update(cmd) {
|
||||
const db = cmd.db ? this.db[cmd.db] : this.db.storage;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.update(cmd.args, cmd.data, { upsert: true }, (err, docs) => {
|
||||
db.update(cmd.args, cmd.data, { upsert: true }, (err, docs) => {
|
||||
if (err) return reject(err);
|
||||
this.db.persistence.compactDatafile();
|
||||
db.persistence.compactDatafile();
|
||||
resolve(docs);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async find(cmd) {
|
||||
console.log('FIND', cmd);
|
||||
async loadDatabase(db) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.db.find(cmd.args, (err, docs) => {
|
||||
db.loadDatabase();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
async find(cmd) {
|
||||
const db = cmd.db ? this.db[cmd.db] : this.db.storage;
|
||||
let args = cmd.args;
|
||||
if (cmd.regex) args = { [cmd.regex.param]: new RegExp(cmd.regex.source, cmd.regex.flags) };
|
||||
return new Promise((resolve, reject) => {
|
||||
db.find(args, (err, docs) => {
|
||||
if (err) return reject(err);
|
||||
resolve(docs);
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue