eicon picker
This commit is contained in:
parent
cf1f23a19e
commit
2e807adae2
|
@ -1,13 +1,14 @@
|
|||
# Changelog
|
||||
|
||||
## 1.23.0
|
||||
## Canary
|
||||
* Cleaned up top menu
|
||||
* Profile Helper now only shows up if you have anything to fix; otherwise the profile helper can be found in the Settings menu
|
||||
* Post Ads and Ad Editor have been merged together
|
||||
|
||||
## 1.23.0
|
||||
* Improved text editor
|
||||
* eicon picker (courtesy of @Xariah Dailstone)
|
||||
* color picker
|
||||
* unicode emoji picker
|
||||
* Added [privacy statement](PRIVACY.md)
|
||||
|
||||
## 1.22.0
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
<template>
|
||||
<div class="eicon-selector-ui">
|
||||
<div v-if="!store || refreshing" class="d-flex align-items-center loading">
|
||||
<strong>Loading...</strong>
|
||||
<div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
|
||||
</div>
|
||||
<div v-else tabindex="0">
|
||||
<div>
|
||||
<div class="search-bar">
|
||||
<input type="text" class="form-control search" id="search" v-model="search" ref="search" placeholder="Search eicons..." @click.prevent.stop="setFocus()" @mousedown.prevent.stop @mouseup.prevent.stop />
|
||||
<div class="btn-group search-buttons">
|
||||
<div class="btn expressions" @click.prevent.stop="runSearch('category:expressions')" aria-label="Expressions">
|
||||
<i class="fas fa-theater-masks"></i>
|
||||
</div>
|
||||
|
||||
<div class="btn sexual" @click.prevent.stop="runSearch('category:sexual')" aria-label="Sexual">
|
||||
<i class="fas fa-heart"></i>
|
||||
</div>
|
||||
|
||||
<div class="btn bubbles" @click.prevent.stop="runSearch('category:bubbles')" aria-label="Speech bubbles">
|
||||
<i class="fas fa-comment"></i>
|
||||
</div>
|
||||
|
||||
<div class="btn actions" @click.prevent.stop="runSearch('category:symbols')" aria-label="Symbols">
|
||||
<i class="fas fa-icons"></i>
|
||||
</div>
|
||||
|
||||
<div class="btn memes" @click.prevent.stop="runSearch('category:memes')" aria-label="Memes">
|
||||
<i class="fas fa-poo"></i>
|
||||
</div>
|
||||
|
||||
<div class="btn random" @click.prevent.stop="runSearch('category:random')" aria-label="Random">
|
||||
<i class="fas fa-random"></i>
|
||||
</div>
|
||||
|
||||
<div class="btn refresh" @click.prevent.stop="refreshIcons()" aria-label="Refresh eicons">
|
||||
<i class="fas fa-sync"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="courtesy">
|
||||
Courtesy of <a href="https://xariah.net/eicons">xariah.net</a>
|
||||
</div>
|
||||
|
||||
<div class="upload">
|
||||
<a href="https://www.f-list.net/icons.php">Upload eicons</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="carousel slide w-100 results">
|
||||
<div class="carousel-inner w-100" role="listbox">
|
||||
<div class="carousel-item" v-for="eicon in results" role="img" :aria-label="eicon">
|
||||
<img class="eicon" :alt="eicon" :src="'https://static.f-list.net/images/eicon/' + eicon + '.gif'" :title="eicon" @click.prevent.stop="selectIcon(eicon)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
import { Component, Hook, Prop, Watch } from '@f-list/vue-ts';
|
||||
import Vue from 'vue';
|
||||
import { EIconStore } from '../learn/eicon/store';
|
||||
|
||||
@Component
|
||||
export default class EIconSelector extends Vue {
|
||||
@Prop
|
||||
readonly onSelect?: (eicon: string) => void;
|
||||
|
||||
store: EIconStore | undefined;
|
||||
results: string[] = [];
|
||||
|
||||
search: string = '';
|
||||
|
||||
refreshing = false;
|
||||
|
||||
searchUpdateDebounce = _.debounce(() => this.runSearch(), 200, { maxWait: 1000 });
|
||||
|
||||
@Hook('mounted')
|
||||
async mounted(): Promise<void> {
|
||||
this.store = await EIconStore.getSharedStore();
|
||||
this.runSearch('');
|
||||
}
|
||||
|
||||
@Watch('search')
|
||||
searchUpdate() {
|
||||
this.searchUpdateDebounce();
|
||||
}
|
||||
|
||||
runSearch(search?: string) {
|
||||
if (search) {
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
const s = this.search.toLowerCase().trim();
|
||||
|
||||
if (s.substring(0, 9) === 'category:') {
|
||||
const category = s.substring(9).trim();
|
||||
|
||||
if (category === 'random') {
|
||||
this.results = _.map(this.store?.random(100), (e) => e.eicon);
|
||||
} else {
|
||||
this.results = this.getCategoryResults(category);
|
||||
}
|
||||
} else {
|
||||
if (s.length === 0) {
|
||||
this.results = _.map(this.store?.random(100), (e) => e.eicon);
|
||||
} else {
|
||||
this.results = _.map(_.take(this.store?.search(s), 100), (e) => e.eicon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getCategoryResults(category: string): string[] {
|
||||
switch(category) {
|
||||
case 'expressions':
|
||||
return ['coolemoji', 'coughing emoji', 'flushedemoji', 'eyerollemoji', 'laughingemoji', 'grinning emoji', 'party emoji', 'pensiveemoji', 'lipbite emoji', 'nauseous emoji', 'angryemoji', 'facialemoji', 'clapemoji', 'heart eyes', 'kissing heart', 'cowboy emoji', 'cowemoji', 'eggplantemoji', 'peachemoji', 'melting emoji', 'poopemoji', 'thinkingemoji', 'triumphemoji', 'uwuemoji', 'voremoji', 'skullemoji', 'smugemoji', 'heartflooshed', 'fluttersorry', 'snake emoji', 'horseeyes', 'thehorse', 'catblob', 'catblobangery', 'splashemoji', 'tonguemoji', 'blobhugs', 'lickscreen', 'eyes emoji', 'nerdmeme', 'horsepls', 'e62pog', 'thirstytwi', 'bangfingerbang', 'chefs kiss', 'excuse me', 'psychopath', 'ashemote3', 'whentheohitsright', 'caradrinkreact', 'lip_bite', 'twittersob'];
|
||||
|
||||
case 'symbols':
|
||||
return ['loveslove', 'pimpdcash', 'pls stop', 'gender-female', 'gender-male', 'gendershemale', 'gender-cuntboy', 'gender-mherm', 'gender-transgender', 'usflag', 'europeflag', 'lgbt', 'transflag', 'yaoilove', 'sunnyuhsuperlove', 'discovered', 'thbun', 'cuckquean', 'goldendicegmgolddicegif', 'pentagramo', 'sexsymbol', 'idnd1', 'instagram', 'twitterlogo', 'snapchaticon', 'tiktok', 'uber', 'google', 'suitclubs', 'suitdiamonds', 'suithearts', 'suitspades'];
|
||||
|
||||
case 'bubbles':
|
||||
return ['takemetohornyjail', 'notcashmoney', 'lickme', 'iacs', 'imahugeslut', 'fuckyouasshole', 'bubblecute', 'pat my head', 'chorse', 'knotslutbubble', 'toofuckinghot', 'pbmr', 'imabimbo', 'dicefuck', 'ciaig', 'horseslut', 'fatdick', 'tomboypussy', 'breakthesubs', 'fuckingnya', 'iltclion', 'suckfuckobey', 'shemale', 'breedmaster', 'imastepfordwife', 'prier ahegao', 'buttslutbb', 'notgayoranything', 'onlyfans', 'horsecockneed', 'crimes', 'breed143', 'nagagross', 'willrim', 'muskslut', '4lewdbubble', 'thatslewd', 'hypnosiss', 'imahypnoslut', 'sheepsass2', 'imahugeslut', 'ratedmilf', 'notahealslut', 'ratedstud', 'ratedslut', 'xarcuminme', '5lewdbubble', 'xarcumonme', 'choke me', 'iamgoingtopunchyou', 'snapmychoker', 'rude1', 'fuckbun', 'iamindanger', 'fuckingelves', 'slutmug', 'helpicantstopsuckingcocks', 'talkpooltoy', 'thatskindahot', 'simpbait',];
|
||||
|
||||
case 'sexual':
|
||||
return ['kissspink', 'paytonkiss', 'coralbutt4', 'slavefidget', 'capstrip', 'pinkundress', 'jhab1', 'caninelover', 'pole', 'rorobutt2', 'fingerlick', 'lapgrind', 'jackthighs', 'a condom', 'wolf abs', 'musclefuck2', 'verobutt3', 'bumsqueeze', 'realahegao4', 'influencerhater', 'assfucker', 'gagged2', 'ballsack3', 'fingering wolf', 'sloppy01', 'sybian', 'femboibate1', 'floppyhorsecock', 'blackshem1', 'fingersucc', 'vullylick', 'freyasuckfingers', 'cmontakeit', 'jessi flash', 'poju-butt', 'cheegrope2', 'patr1', 'ahega01 2', 'handjob1nuke', 'harmanfingers', 'rorysheath2', 'hermione1', '2buttw1', 'dropsqueeze', 'lixlove', 'bbctitjob6', 'appreciativetease', 'bimbolick', 'subj3', 'salivashare', 'ballsworship3', 'wolfsknot2', 'gaykiss', 'slurpkiss', 'absbulge', 'cockiss', 'horsedick11', 'knot1', 'g4ebulge', 'blackadamrough', 'flaunt', 'cummiefj', 'lovetosuck', 'worship', 'hopelessly in love', 'knotts', 'cockloveeee', 'donglove', 'woowyknotjob', 'cummz', 'every drop', 'edgyoops', 'orccummies2', 'oralcreampie100px', 'horseoral9a', 'swallowit', 'sinahegao', 'gayicon2', 'slut4', 'hossspurties2', 'cumringgag', 'jillbimbogiffell2', 'artistry01'];
|
||||
|
||||
case 'memes':
|
||||
return ['guncock', 'michaelguns', 'wegotabadass', 'gonnabang', 'flirting101', 'monkeymeme', 'monkeymeme2', 'horsenoises', 'nyancat', 'gayb', 'fortasshole', 'dickletsign', 'sausageface', 'siren0', 'apologize to god', 'jabbalick', 'zeldawink', 'whatislove', 'surprisemothafucka', 'females', 'thanksihateit'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
selectIcon(eicon: string): void {
|
||||
if (this.onSelect) {
|
||||
this.onSelect(eicon);
|
||||
}
|
||||
}
|
||||
|
||||
async refreshIcons(): Promise<void> {
|
||||
this.refreshing = true;
|
||||
|
||||
await this.store?.update();
|
||||
await this.runSearch();
|
||||
|
||||
this.refreshing = false;
|
||||
}
|
||||
|
||||
setFocus(): void {
|
||||
(this.$refs['search'] as any).focus();
|
||||
(this.$refs['search'] as any).select();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.eicon-selector-ui {
|
||||
.loading {
|
||||
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
|
||||
.search {
|
||||
flex: 1;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.search-buttons {
|
||||
margin-left: -1px;
|
||||
|
||||
.btn {
|
||||
border-bottom: 1px solid var(--secondary);
|
||||
}
|
||||
|
||||
.expressions {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.refresh {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.courtesy {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
font-size: 9px;
|
||||
right: 10px;
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
.upload {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
font-size: 9px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.results {
|
||||
max-height: 200px;
|
||||
overflow: hidden;
|
||||
margin-top: 5px;
|
||||
|
||||
.carousel-inner {
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
|
||||
.carousel-item {
|
||||
display: table-cell;
|
||||
border: solid 1px transparent !important;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--secondary) !important;
|
||||
border: solid 1px var(--gray-dark) !important;
|
||||
}
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
max-height: 75px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -16,6 +16,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="popover popover-top eicon-selector" v-show="eiconPopupVisible" v-on-clickaway="dismissEIconSelector">
|
||||
<div class="popover-body">
|
||||
<EIconSelector :onSelect="onSelectEIcon" ref="eIconSelector"></EIconSelector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group toolbar-buttons" style="flex-wrap:wrap">
|
||||
<div v-if="!!characterName" class="character-btn">
|
||||
<icon :character="characterName"></icon>
|
||||
|
@ -59,10 +65,12 @@
|
|||
import {defaultButtons, EditorButton, EditorSelection} from './editor';
|
||||
import {BBCodeParser} from './parser';
|
||||
import {default as IconView} from './IconView.vue';
|
||||
import {default as EIconSelector} from './EIconSelector.vue';
|
||||
|
||||
@Component({
|
||||
components: {
|
||||
'icon': IconView
|
||||
'icon': IconView,
|
||||
'EIconSelector': EIconSelector
|
||||
},
|
||||
mixins: [ clickaway ]
|
||||
})
|
||||
|
@ -96,6 +104,7 @@
|
|||
|
||||
buttonColors = ['red', 'orange', 'yellow', 'green', 'cyan', 'purple', 'blue', 'pink', 'black', 'brown', 'white', 'gray'];
|
||||
colorPopupVisible = false;
|
||||
eiconPopupVisible = false;
|
||||
|
||||
preview = false;
|
||||
previewWarnings: ReadonlyArray<string> = [];
|
||||
|
@ -176,7 +185,7 @@
|
|||
for(let i = 0, l = this.extras.length; i < l; i++)
|
||||
buttons.push(this.extras[i]);
|
||||
|
||||
const colorButtonIndex = _.findIndex(buttons, (b) => b.icon === 'fa-eye-dropper')!;
|
||||
const colorButtonIndex = _.findIndex(buttons, (b) => b.tag === 'color');
|
||||
|
||||
if (this.colorPopupVisible) {
|
||||
const colorButton = _.cloneDeep(buttons[colorButtonIndex]);
|
||||
|
@ -185,11 +194,26 @@
|
|||
buttons[colorButtonIndex] = colorButton;
|
||||
}
|
||||
|
||||
const eiconButtonIndex = _.findIndex(buttons, (b) => b.tag === 'eicon');
|
||||
|
||||
if (this.eiconPopupVisible) {
|
||||
const eiconButton = _.cloneDeep(buttons[eiconButtonIndex]);
|
||||
eiconButton.outerClass = 'toggled';
|
||||
|
||||
buttons[eiconButtonIndex] = eiconButton;
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
getColorButton(): EditorButton {
|
||||
return _.find(this.buttons, (b) => b.icon === 'fa-eye-dropper')!;
|
||||
getButtonByTag(tag: string): EditorButton {
|
||||
const btn = _.find(this.buttons, (b) => b.tag === tag);
|
||||
|
||||
if (!btn) {
|
||||
throw new Error('Unknown button');
|
||||
}
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
@Watch('value')
|
||||
|
@ -228,16 +252,16 @@
|
|||
this.element.setSelectionRange(start, end);
|
||||
}
|
||||
|
||||
applyText(startText: string, endText: string): void {
|
||||
applyText(startText: string, endText: string, withInject?: string): void {
|
||||
const selection = this.getSelection();
|
||||
if(selection.length > 0) {
|
||||
const replacement = startText + selection.text + endText;
|
||||
const replacement = startText + (withInject || selection.text) + endText;
|
||||
this.text = this.replaceSelection(replacement);
|
||||
this.setSelection(selection.start, selection.start + replacement.length);
|
||||
} else {
|
||||
const start = this.text.substr(0, selection.start) + startText;
|
||||
const end = endText + this.text.substr(selection.start);
|
||||
this.text = start + end;
|
||||
this.text = start + (withInject || '') + end;
|
||||
this.$nextTick(() => this.setSelection(start.length));
|
||||
}
|
||||
this.$emit('input', this.text);
|
||||
|
@ -248,27 +272,47 @@
|
|||
}
|
||||
|
||||
colorApply(btnColor: string): void {
|
||||
const button = this.getColorButton();
|
||||
const button = this.getButtonByTag('color');
|
||||
|
||||
this.applyButtonEffect(button, btnColor);
|
||||
|
||||
this.colorPopupVisible = false;
|
||||
}
|
||||
|
||||
dismissEIconSelector(): void {
|
||||
this.eiconPopupVisible = false;
|
||||
}
|
||||
|
||||
onSelectEIcon(eiconId: string): void {
|
||||
this.eiconApply(eiconId);
|
||||
}
|
||||
|
||||
eiconApply(eiconId: string): void {
|
||||
const button = this.getButtonByTag('eicon');
|
||||
|
||||
this.applyButtonEffect(button, undefined, eiconId);
|
||||
|
||||
this.eiconPopupVisible = false;
|
||||
}
|
||||
|
||||
apply(button: EditorButton): void {
|
||||
if (button.tag === 'color') {
|
||||
this.colorPopupVisible = !this.colorPopupVisible;
|
||||
return;
|
||||
} else if (button.tag === 'eicon') {
|
||||
this.eiconPopupVisible = !this.eiconPopupVisible;
|
||||
|
||||
} else if (button.tag === 'emoji') {
|
||||
if (this.eiconPopupVisible) {
|
||||
setTimeout(() => (this.$refs.eIconSelector as any).setFocus(), 100);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.applyButtonEffect(button);
|
||||
}
|
||||
|
||||
applyButtonEffect(button: EditorButton, withArgument?: string): void {
|
||||
applyButtonEffect(button: EditorButton, withArgument?: string, withInject?: string): void {
|
||||
// Allow emitted variations for custom buttons.
|
||||
this.$once('insert', (startText: string, endText: string) => this.applyText(startText, endText));
|
||||
// noinspection TypeScriptValidateTypes
|
||||
|
@ -285,7 +329,7 @@
|
|||
const sbl = button.startText ? button.startText.length : 0;
|
||||
|
||||
if(this.text.length + sbl + ebl > this.maxlength) return;
|
||||
this.applyText(button.startText || '', button.endText || '');
|
||||
this.applyText(button.startText || '', button.endText || '', withInject);
|
||||
this.lastInput = Date.now();
|
||||
}
|
||||
|
||||
|
@ -402,6 +446,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
.eicon-selector {
|
||||
width: 550px;
|
||||
max-width: 550px;
|
||||
top: -169px;
|
||||
left: 0;
|
||||
line-height: 1;
|
||||
z-index: 1000;
|
||||
background-color: var(--input-bg);
|
||||
min-height: 170px;
|
||||
}
|
||||
|
||||
.color-selector {
|
||||
max-width: 145px;
|
||||
top: -57px;
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</div>
|
||||
</a>
|
||||
|
||||
<a href="#" @click.prevent="showAddPmPartner()" class="new-conversation" :class="{ glowing: conversations.privateConversations.length === 0 }">Open Conversation</a>
|
||||
<a href="#" @click.prevent="showAddPmPartner()" class="new-conversation" :class="{ glowing: conversations.privateConversations.length === 0 && privateCanGlow }">Open Conversation</a>
|
||||
</div>
|
||||
|
||||
<a href="#" @click.prevent="showChannels()" class="btn"><span class="fas fa-list"></span>
|
||||
|
@ -86,7 +86,7 @@
|
|||
</span>
|
||||
</a>
|
||||
|
||||
<a href="#" @click.prevent="showChannels()" class="join-channel" :class="{ glowing: conversations.channelConversations.length === 0 }">Join Channel</a>
|
||||
<a href="#" @click.prevent="showChannels()" class="join-channel" :class="{ glowing: conversations.channelConversations.length === 0 && channelCanGlow }">Join Channel</a>
|
||||
</div>
|
||||
</sidebar>
|
||||
<div style="display:flex;flex-direction:column;flex:1;min-width:0">
|
||||
|
@ -136,7 +136,7 @@
|
|||
</template>/me
|
||||
|
||||
<script lang="ts">
|
||||
import {Component, Hook} from '@f-list/vue-ts';
|
||||
import { Component, Hook, Watch } from '@f-list/vue-ts';
|
||||
|
||||
import Sortable from 'sortablejs';
|
||||
|
||||
|
@ -200,6 +200,26 @@
|
|||
focusListener!: () => void;
|
||||
blurListener!: () => void;
|
||||
|
||||
channelConversations = core.conversations.channelConversations
|
||||
privateConversations = core.conversations.privateConversations
|
||||
|
||||
privateCanGlow = !this.channelConversations?.length;
|
||||
channelCanGlow = !this.privateConversations?.length;
|
||||
|
||||
@Watch('conversations.channelConversations')
|
||||
channelConversationsChange() {
|
||||
if (this.conversations.channelConversations?.length) {
|
||||
this.channelCanGlow = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Watch('conversations.privateConversations')
|
||||
privateConversationsChange() {
|
||||
if (this.conversations.privateConversations?.length) {
|
||||
this.privateCanGlow = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Hook('mounted')
|
||||
mounted(): void {
|
||||
this.keydownListener = (e: KeyboardEvent) => this.onKeyDown(e);
|
||||
|
|
|
@ -110,4 +110,8 @@
|
|||
.statusEditor .bbcode-toolbar .color-selector {
|
||||
left: 58px !important;
|
||||
}
|
||||
|
||||
.statusEditor .bbcode-toolbar .eicon-selector {
|
||||
top: 30px !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -93,6 +93,11 @@
|
|||
<style lang="scss">
|
||||
.ad-list .bbcode-toolbar .color-selector {
|
||||
left: 58px !important;
|
||||
top: 30px !important;
|
||||
}
|
||||
|
||||
.ad-list .bbcode-toolbar .eicon-selector {
|
||||
top: 30px !important;
|
||||
}
|
||||
|
||||
.w-100 {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
import * as electron from 'electron';
|
||||
const app = electron.app;
|
||||
import * as remote from '@electron/remote';
|
||||
import log from 'electron-log'; //tslint:disable-line:match-default-export-name
|
||||
import * as fs from 'fs';
|
||||
|
@ -41,9 +39,11 @@ export class EIconStore {
|
|||
this.asOfTimestamp = data?.asOfTimestamp || 0;
|
||||
this.lookup = _.fromPairs(_.map(this.records, (r) => [r.eicon, r]));
|
||||
|
||||
this.resortList();
|
||||
log.info('eicons.loaded.local', { records: this.records.length, asOfTimestamp: this.asOfTimestamp });
|
||||
|
||||
log.info('eicons.loaded', { records: this.records.length, asOfTimestamp: this.asOfTimestamp });
|
||||
await this.update();
|
||||
|
||||
log.info('eicons.loaded.update.remote', { records: this.records.length, asOfTimestamp: this.asOfTimestamp });
|
||||
} catch (err) {
|
||||
try {
|
||||
await this.downloadAll();
|
||||
|
@ -54,7 +54,7 @@ export class EIconStore {
|
|||
}
|
||||
|
||||
protected getStoreFilename(): string {
|
||||
const baseDir = app.getPath('userData');
|
||||
const baseDir = remote.app.getPath('userData');
|
||||
const settingsDir = path.join(baseDir, 'data');
|
||||
|
||||
return path.join(settingsDir, 'eicons.json');
|
||||
|
@ -78,7 +78,7 @@ export class EIconStore {
|
|||
}
|
||||
|
||||
async update(): Promise<void> {
|
||||
log.info('eicons.update');
|
||||
log.info('eicons.update', { asOf: this.asOfTimestamp });
|
||||
|
||||
const changes = await this.updater.fetchUpdates(this.asOfTimestamp);
|
||||
|
||||
|
@ -92,6 +92,8 @@ export class EIconStore {
|
|||
|
||||
this.asOfTimestamp = changes.asOfTimestamp;
|
||||
|
||||
log.info('eicons.update.processed', { removals: removals.length, additions: additions.length, asOf: this.asOfTimestamp });
|
||||
|
||||
if (changes.recordUpdates.length > 0) {
|
||||
await this.save();
|
||||
}
|
||||
|
@ -131,7 +133,7 @@ export class EIconStore {
|
|||
}
|
||||
|
||||
search(searchString: string): EIconRecord[] {
|
||||
const lcSearch = searchString.toLowerCase();
|
||||
const lcSearch = searchString.trim().toLowerCase();
|
||||
const found = _.filter(this.records, (r) => r.eicon.indexOf(lcSearch) >= 0);
|
||||
|
||||
return found.sort((a, b) => {
|
||||
|
@ -146,4 +148,22 @@ export class EIconStore {
|
|||
return a.eicon.localeCompare(b.eicon);
|
||||
});
|
||||
}
|
||||
|
||||
random(count: number): EIconRecord[] {
|
||||
return _.sampleSize(this.records, count);
|
||||
}
|
||||
|
||||
private static sharedStore: EIconStore | undefined;
|
||||
|
||||
static async getSharedStore(): Promise<EIconStore> {
|
||||
if (!EIconStore.sharedStore) {
|
||||
EIconStore.sharedStore = new EIconStore();
|
||||
|
||||
await EIconStore.sharedStore.load();
|
||||
|
||||
setInterval(() => EIconStore.sharedStore!.update(), 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
return EIconStore.sharedStore;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ export class EIconUpdater {
|
|||
const result = await Axios.get(EIconUpdater.FULL_DATA_URL);
|
||||
const lines = _.split(result.data, '\n');
|
||||
|
||||
const records = _.map(_.filter(lines, (line) => (line.trim().substr(0, 1) !== '#')), (line) => {
|
||||
const records = _.map(_.filter(lines, (line) => (line.trim().substr(0, 1) !== '#' && line.trim() !== '')), (line) => {
|
||||
const [eicon, timestamp] = _.split(line, '\t', 2);
|
||||
return { eicon: eicon.toLowerCase(), timestamp: parseInt(timestamp, 10) };
|
||||
});
|
||||
|
@ -34,7 +34,7 @@ export class EIconUpdater {
|
|||
const result = await Axios.get(`${EIconUpdater.DATA_UPDATE_URL}/${fromTimestampInSecs}`);
|
||||
const lines = _.split(result.data, '\n');
|
||||
|
||||
const recordUpdates = _.map(_.filter(lines, (line) => (line.trim().substr(0, 1) !== '#')), (line) => {
|
||||
const recordUpdates = _.map(_.filter(lines, (line) => (line.trim().substr(0, 1) !== '#' && line.trim() !== '')), (line) => {
|
||||
const [action, eicon, timestamp] = _.split(line, '\t', 3);
|
||||
return { action: action as '+' | '-', eicon: eicon.toLowerCase(), timestamp: parseInt(timestamp, 10) };
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue