Skip to content

Commit

Permalink
絵文字検索
Browse files Browse the repository at this point in the history
  • Loading branch information
mei23 committed Nov 26, 2020
1 parent c8bc491 commit 15c5202
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 3 deletions.
1 change: 1 addition & 0 deletions locales/en-US.yml
Expand Up @@ -658,6 +658,7 @@ common/views/components/reaction-picker.vue:
choose-reaction: "Send a reaction"
common/views/components/emoji-picker.vue:
pinned: "Keep picker open"
search: "Search Emoji"
recent-emoji: "Recent Emoji"
custom-emoji: "Custom Emoji"
remote-emoji: "Remote Custom Emoji"
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Expand Up @@ -718,6 +718,7 @@ common/views/components/reaction-picker.vue:

common/views/components/emoji-picker.vue:
pinned: "ピッカーを開いたままにする"
search: "絵文字を検索"
recent-emoji: "最近使った絵文字"
custom-emoji: "カスタム絵文字"
remote-emoji: "リモートカスタム絵文字"
Expand Down
165 changes: 165 additions & 0 deletions src/client/app/common/views/components/emoji-picker.vue
@@ -1,5 +1,6 @@
<template>
<div class="prlncendiewqqkrevzeruhndoakghvtx">
<!-- タブのボタン -->
<header>
<button v-for="category in categories"
:title="category.text"
Expand All @@ -11,10 +12,27 @@
</button>
</header>
<div class="emojis">
<!-- ピン留め -->
<header v-if="!reaction" class="menu">
<ui-switch v-model="pinned">{{ $t('pinned') }}</ui-switch>
</header>
<!-- *カテゴリの追加分 -->
<template v-if="categories[0].isActive">
<!-- 検索 -->
<ui-input v-model="q" :autofocus="true" style="margin: 0.6em;">
<span>{{ $t('search') }}</span>
</ui-input>
<div class="list" v-if="searchResults.length > 0">
<button v-for="(emoji, i) in (searchResults || [])"
:title="emoji.sources ? emoji.sources.map(x => `${x.name}@${x.host}`).join(',\n') : emoji.name"
@click="chosen(emoji)"
:key="i"
>
<mk-emoji v-if="emoji.char != null" :emoji="emoji.char" :local="emoji.local"/>
<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
</button>
</div>
<!-- 最近使った -->
<header class="category"><fa :icon="faHistory" fixed-width/> {{ $t('recent-emoji') }}</header>
<div class="list">
<button v-for="(emoji, i) in ($store.state.device.recentEmojis || [])"
Expand All @@ -28,7 +46,9 @@
</div>
</template>

<!-- メイン -->
<header class="category"><fa :icon="categories.find(x => x.isActive).icon" fixed-width/> {{ categories.find(x => x.isActive).text }}</header>
<!-- メイン *以外 -->
<template v-if="categories.find(x => x.isActive).name">
<div class="list">
<button v-for="emoji in emojilist.filter(e => e.category === categories.find(x => x.isActive).name)"
Expand All @@ -40,6 +60,7 @@
</button>
</div>
</template>
<!-- メイン * -->
<template v-else>
<div v-for="(key, i) in Object.keys(customEmojis)" :key="i">
<header class="sub">{{ key || $t('no-category') }}</header>
Expand All @@ -54,6 +75,7 @@
</div>
</div>

<!-- リモート -->
<header class="category" v-if="includeRemote"><fa :icon="faGlobe" fixed-width/> {{ $t('remote-emoji') }}</header>
<div class="list" v-if="remoteEmojis != null">
<button v-for="emoji in remoteEmojis"
Expand Down Expand Up @@ -106,6 +128,8 @@ export default Vue.extend({
getStaticImageUrl,
customEmojis: {},
remoteEmojis: null,
q: null,
searchResults: [],
faGlobe, faHistory,
categories: [{
text: this.$t('custom-emoji'),
Expand Down Expand Up @@ -160,6 +184,147 @@ export default Vue.extend({
}
},
watch: {
q() {
if (this.q == null || this.q === '') {
this.searchResults = [];
return;
}
const q = this.q.replace(/:/g, '');
const searchCustom = () => {
const max = 8;
const emojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
const matches = new Set();
const exactMatch = emojis.find(e => e.name === q);
if (exactMatch) matches.add(exactMatch);
if (q.includes(' ')) { // AND検索
const keywords = q.split(' ');
// 名前にキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
// 名前またはエイリアスにキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
} else {
for (const emoji of emojis) {
if (emoji.name.startsWith(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.aliases.some(alias => alias.startsWith(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.name.includes(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.aliases.some(alias => alias.includes(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
}
return matches;
};
const searchUnicode = () => {
const max = 8;
const emojis = this.emojilist;
const matches = new Set();
const exactMatch = emojis.find(e => e.name === q);
if (exactMatch) matches.add(exactMatch);
if (q.includes(' ')) { // AND検索
const keywords = q.split(' ');
// 名前にキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
// 名前またはエイリアスにキーワードが含まれている
for (const emoji of emojis) {
if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.keywords.some(alias => alias.includes(keyword)))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
} else {
for (const emoji of emojis) {
if (emoji.name.startsWith(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.keywords.some(keyword => keyword.startsWith(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.name.includes(q)) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const emoji of emojis) {
if (emoji.keywords.some(keyword => keyword.includes(q))) {
matches.add(emoji);
if (matches.size >= max) break;
}
}
}
return matches;
};
const searchResultCustom = Array.from(searchCustom());
const searchResultUnicode = Array.from(searchUnicode());
this.searchResults = searchResultCustom.concat(searchResultUnicode);
}
},
created() {
let local = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
local = groupBy(local, (x: any) => x.category || '');
Expand Down
10 changes: 7 additions & 3 deletions src/client/app/desktop/views/components/emoji-picker-dialog.vue
Expand Up @@ -63,9 +63,13 @@ export default Vue.extend({
methods: {
onMousedown(e) {
e.preventDefault();
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
return false;
if (!contains(this.$el, e.target) && (this.$el != e.target)) {
e.preventDefault();
this.close();
return false;
} else {
return true;
}
},
chosen(args: { emoji: string, close: boolean }) {
Expand Down

0 comments on commit 15c5202

Please sign in to comment.