mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-29 03:28:29 +01:00
parent
888dcd2559
commit
bef2534fa8
5 changed files with 369 additions and 154 deletions
|
@ -542,6 +542,7 @@ pluginInstallWarn: "信頼できないプラグインはインストールしな
|
||||||
deck: "デッキ"
|
deck: "デッキ"
|
||||||
undeck: "デッキ解除"
|
undeck: "デッキ解除"
|
||||||
useBlurEffectForModal: "モーダルにぼかし効果を使用"
|
useBlurEffectForModal: "モーダルにぼかし効果を使用"
|
||||||
|
useFullReactionPicker: "フル機能リアクションピッカーを使用"
|
||||||
generateAccessToken: "アクセストークンの発行"
|
generateAccessToken: "アクセストークンの発行"
|
||||||
permission: "権限"
|
permission: "権限"
|
||||||
enableAll: "全て有効にする"
|
enableAll: "全て有効にする"
|
||||||
|
|
|
@ -1,62 +1,94 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
|
<MkModal ref="modal" :src="src" @click="$refs.modal.close()" @closed="$emit('closed')">
|
||||||
<div class="omfetrab _popup">
|
<div class="omfetrab _popup">
|
||||||
<header>
|
<input ref="search" class="search" v-model.trim="q" :placeholder="$t('search')" @paste.stop="paste" @keyup.enter="done()" autofocus>
|
||||||
<button v-for="(category, i) in categories"
|
|
||||||
class="_button"
|
|
||||||
@click="go(category)"
|
|
||||||
:class="{ active: category.isActive }"
|
|
||||||
:key="i"
|
|
||||||
>
|
|
||||||
<Fa :icon="category.icon" fixed-width/>
|
|
||||||
</button>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="emojis">
|
<div class="emojis">
|
||||||
<template v-if="categories[0].isActive">
|
<section class="result">
|
||||||
<header class="category"><Fa :icon="faHistory" fixed-width/> {{ $t('recentUsed') }}</header>
|
<div v-if="searchResultCustom.length > 0">
|
||||||
<div class="list">
|
<button v-for="emoji in searchResultCustom"
|
||||||
<button v-for="emoji in ($store.state.device.recentEmojis || [])"
|
|
||||||
class="_button"
|
class="_button"
|
||||||
:title="emoji.name"
|
:title="emoji.name"
|
||||||
@click="chosen(emoji)"
|
@click="chosen(emoji, $event)"
|
||||||
:key="emoji"
|
:key="emoji"
|
||||||
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
|
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
|
||||||
<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="searchResultUnicode.length > 0">
|
||||||
<header class="category"><Fa :icon="faAsterisk" fixed-width/> {{ $t('customEmojis') }}</header>
|
<button v-for="emoji in searchResultUnicode"
|
||||||
</template>
|
|
||||||
|
|
||||||
<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)"
|
|
||||||
class="_button"
|
class="_button"
|
||||||
:title="emoji.name"
|
:title="emoji.name"
|
||||||
@click="chosen(emoji)"
|
@click="chosen(emoji, $event)"
|
||||||
|
:key="emoji.name"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<MkEmoji :emoji="emoji.char"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="index">
|
||||||
|
<section>
|
||||||
|
<div>
|
||||||
|
<button v-for="emoji in reactions || $store.state.settings.reactions"
|
||||||
|
class="_button"
|
||||||
|
:title="emoji.name"
|
||||||
|
@click="chosen(emoji, $event)"
|
||||||
|
:key="emoji"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<MkEmoji :emoji="emoji.startsWith(':') ? null : emoji" :name="emoji.startsWith(':') ? emoji.substr(1, emoji.length - 2) : null" :normal="true"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<header class="_acrylic"><Fa :icon="faHistory" fixed-width/> {{ $t('recentUsed') }}</header>
|
||||||
|
<div>
|
||||||
|
<button v-for="emoji in ($store.state.device.recentEmojis || [])"
|
||||||
|
class="_button"
|
||||||
|
:title="emoji.name"
|
||||||
|
@click="chosen(emoji, $event)"
|
||||||
|
:key="emoji"
|
||||||
|
>
|
||||||
|
<MkEmoji v-if="emoji.char != null" :emoji="emoji.char"/>
|
||||||
|
<img v-else :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="arrow"><Fa :icon="faChevronDown"/></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section v-for="category in customEmojiCategories" :key="'custom:' + category" class="custom">
|
||||||
|
<header class="_acrylic" v-appear="() => visibleCategories[category] = true">{{ category || $t('other') }}</header>
|
||||||
|
<div v-if="visibleCategories[category]">
|
||||||
|
<button v-for="emoji in customEmojis.filter(e => e.category === category)"
|
||||||
|
class="_button"
|
||||||
|
:title="emoji.name"
|
||||||
|
@click="chosen(emoji, $event)"
|
||||||
|
:key="emoji.name"
|
||||||
|
>
|
||||||
|
<img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section v-for="category in categories" :key="category.name" class="unicode">
|
||||||
|
<header class="_acrylic" v-appear="() => category.isActive = true"><Fa :icon="category.icon" fixed-width/> {{ category.name }}</header>
|
||||||
|
<div v-if="category.isActive">
|
||||||
|
<button v-for="emoji in emojilist.filter(e => e.category === category.name)"
|
||||||
|
class="_button"
|
||||||
|
:title="emoji.name"
|
||||||
|
@click="chosen(emoji, $event)"
|
||||||
:key="emoji.name"
|
:key="emoji.name"
|
||||||
>
|
>
|
||||||
<MkEmoji :emoji="emoji.char"/>
|
<MkEmoji :emoji="emoji.char"/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</section>
|
||||||
<template v-else>
|
|
||||||
<div v-for="(key, i) in Object.keys(customEmojis)" :key="i">
|
|
||||||
<header class="sub" v-if="key">{{ key }}</header>
|
|
||||||
<div class="list">
|
|
||||||
<button v-for="emoji in customEmojis[key]"
|
|
||||||
class="_button"
|
|
||||||
:title="emoji.name"
|
|
||||||
@click="chosen(emoji)"
|
|
||||||
:key="emoji.name"
|
|
||||||
>
|
|
||||||
<img :src="$store.state.device.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url"/>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkModal>
|
</MkModal>
|
||||||
|
@ -66,10 +98,11 @@
|
||||||
import { defineComponent, markRaw } from 'vue';
|
import { defineComponent, markRaw } from 'vue';
|
||||||
import { emojilist } from '../../misc/emojilist';
|
import { emojilist } from '../../misc/emojilist';
|
||||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||||
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faHistory, faUser } from '@fortawesome/free-solid-svg-icons';
|
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faHistory, faUser, faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
|
import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
|
||||||
import { groupByX } from '../../prelude/array';
|
|
||||||
import MkModal from '@/components/ui/modal.vue';
|
import MkModal from '@/components/ui/modal.vue';
|
||||||
|
import Particle from '@/components/particle.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
|
@ -80,6 +113,9 @@ export default defineComponent({
|
||||||
src: {
|
src: {
|
||||||
required: false
|
required: false
|
||||||
},
|
},
|
||||||
|
reactions: {
|
||||||
|
required: false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['done', 'closed'],
|
emits: ['done', 'closed'],
|
||||||
|
@ -88,12 +124,14 @@ export default defineComponent({
|
||||||
return {
|
return {
|
||||||
emojilist: markRaw(emojilist),
|
emojilist: markRaw(emojilist),
|
||||||
getStaticImageUrl,
|
getStaticImageUrl,
|
||||||
customEmojis: {},
|
customEmojiCategories: this.$store.getters['instance/emojiCategories'],
|
||||||
faGlobe, faHistory,
|
customEmojis: this.$store.state.instance.meta.emojis,
|
||||||
|
visibleCategories: {},
|
||||||
|
q: null,
|
||||||
|
searchResultCustom: [],
|
||||||
|
searchResultUnicode: [],
|
||||||
|
faGlobe, faHistory, faChevronDown,
|
||||||
categories: [{
|
categories: [{
|
||||||
icon: faAsterisk,
|
|
||||||
isActive: true
|
|
||||||
}, {
|
|
||||||
name: 'face',
|
name: 'face',
|
||||||
icon: faLaugh,
|
icon: faLaugh,
|
||||||
isActive: false
|
isActive: false
|
||||||
|
@ -134,38 +172,149 @@ export default defineComponent({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
watch: {
|
||||||
let local = this.$store.state.instance.meta.emojis;
|
q() {
|
||||||
local = groupByX(local, (x: any) => x.category || '');
|
if (this.q == null || this.q === '') {
|
||||||
this.customEmojis = markRaw(local);
|
this.searchResultCustom = [];
|
||||||
|
this.searchResultUnicode = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const q = this.q.replace(/:/g, '');
|
||||||
|
|
||||||
|
const searchCustom = () => {
|
||||||
|
const max = 8;
|
||||||
|
const emojis = this.customEmojis;
|
||||||
|
const matches = new Set();
|
||||||
|
|
||||||
|
const exactMatch = emojis.find(e => e.name === q);
|
||||||
|
if (exactMatch) matches.add(exactMatch);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.searchResultCustom = Array.from(searchCustom());
|
||||||
|
this.searchResultUnicode = Array.from(searchUnicode());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$refs.search.focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
go(category: any) {
|
chosen(emoji: any, ev) {
|
||||||
this.goCategory(category.name);
|
if (ev) {
|
||||||
},
|
const el = ev.currentTarget || ev.target;
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
goCategory(name: string) {
|
const x = rect.left + (el.clientWidth / 2);
|
||||||
let matched = false;
|
const y = rect.top + (el.clientHeight / 2);
|
||||||
for (const c of this.categories) {
|
os.popup(Particle, { x, y }, {}, 'end');
|
||||||
c.isActive = c.name === name;
|
|
||||||
if (c.isActive) {
|
|
||||||
matched = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!matched) {
|
|
||||||
this.categories[0].isActive = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
chosen(emoji: any) {
|
const getKey = (emoji: any) => typeof emoji === 'string' ? emoji : emoji.char || `:${emoji.name}:`;
|
||||||
const getKey = (emoji: any) => emoji.char || `:${emoji.name}:`;
|
this.$emit('done', getKey(emoji));
|
||||||
|
this.$refs.modal.close();
|
||||||
|
|
||||||
|
// 最近使った絵文字更新
|
||||||
let recents = this.$store.state.device.recentEmojis || [];
|
let recents = this.$store.state.device.recentEmojis || [];
|
||||||
recents = recents.filter((e: any) => getKey(e) !== getKey(emoji));
|
recents = recents.filter((e: any) => getKey(e) !== getKey(emoji));
|
||||||
recents.unshift(emoji)
|
recents.unshift(emoji)
|
||||||
this.$store.commit('device/set', { key: 'recentEmojis', value: recents.splice(0, 16) });
|
this.$store.commit('device/set', { key: 'recentEmojis', value: recents.splice(0, 16) });
|
||||||
this.$emit('done', getKey(emoji));
|
},
|
||||||
this.$refs.modal.close();
|
|
||||||
|
paste(event) {
|
||||||
|
const paste = (event.clipboardData || window.clipboardData).getData('text');
|
||||||
|
if (this.done(paste)) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
done(query) {
|
||||||
|
if (query == null) query = this.q;
|
||||||
|
if (query == null) return;
|
||||||
|
const q = query.replace(/:/g, '');
|
||||||
|
const exactMatchCustom = this.customEmojis.find(e => e.name === q);
|
||||||
|
if (exactMatchCustom) {
|
||||||
|
this.chosen(exactMatchCustom);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const exactMatchUnicode = this.emojilist.find(e => e.name === q);
|
||||||
|
if (exactMatchUnicode) {
|
||||||
|
this.chosen(exactMatchUnicode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -174,85 +323,108 @@ export default defineComponent({
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.omfetrab {
|
.omfetrab {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
|
contain: content;
|
||||||
|
|
||||||
> header {
|
> .search {
|
||||||
display: flex;
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
> button {
|
box-sizing: border-box;
|
||||||
flex: 1;
|
font-size: 1em;
|
||||||
padding: 10px 0;
|
outline: none;
|
||||||
font-size: 16px;
|
border: none;
|
||||||
transition: color 0.2s ease;
|
background: transparent;
|
||||||
|
color: var(--fg);
|
||||||
&:hover {
|
|
||||||
color: var(--fgHighlighted);
|
|
||||||
transition: color 0s;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: var(--accent);
|
|
||||||
transition: color 0s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
> .emojis {
|
> .emojis {
|
||||||
height: 300px;
|
$height: 300px;
|
||||||
|
|
||||||
|
height: $height;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
> header.category {
|
> .index {
|
||||||
position: sticky;
|
min-height: $height;
|
||||||
top: 0;
|
position: relative;
|
||||||
left: 0;
|
border-bottom: solid 1px var(--divider);
|
||||||
z-index: 1;
|
|
||||||
padding: 8px;
|
> .arrow {
|
||||||
background: var(--panel);
|
position: absolute;
|
||||||
font-size: 12px;
|
bottom: 0;
|
||||||
}
|
left: 0;
|
||||||
|
|
||||||
header.sub {
|
|
||||||
padding: 4px 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.list {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
|
||||||
gap: 4px;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
> button {
|
|
||||||
position: relative;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
padding: 16px 0;
|
||||||
|
text-align: center;
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:before {
|
section {
|
||||||
content: '';
|
> header {
|
||||||
display: block;
|
position: sticky;
|
||||||
width: 1px;
|
top: 0;
|
||||||
height: 0;
|
left: 0;
|
||||||
padding-bottom: 100%;
|
z-index: 1;
|
||||||
}
|
padding: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: solid 2px var(--focus);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
width: 1px;
|
||||||
|
height: 0;
|
||||||
|
padding-bottom: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
> * {
|
||||||
|
transform: scale(1.2);
|
||||||
|
transition: transform 0s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
|
||||||
> * {
|
> * {
|
||||||
transform: scale(1.2);
|
position: absolute;
|
||||||
transition: transform 0s;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
font-size: 28px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> * {
|
&.result {
|
||||||
position: absolute;
|
border-bottom: solid 1px var(--divider);
|
||||||
top: 0;
|
}
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
&.unicode {
|
||||||
height: 100%;
|
min-height: 384px;
|
||||||
object-fit: contain;
|
}
|
||||||
font-size: 28px;
|
|
||||||
transition: transform 0.2s ease;
|
&.custom {
|
||||||
pointer-events: none;
|
min-height: 64px;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -498,20 +498,36 @@ export default defineComponent({
|
||||||
react(viaKeyboard = false) {
|
react(viaKeyboard = false) {
|
||||||
pleaseLogin();
|
pleaseLogin();
|
||||||
this.blur();
|
this.blur();
|
||||||
os.popup(import('@/components/reaction-picker.vue'), {
|
if (this.$store.state.device.useFullReactionPicker) {
|
||||||
showFocus: viaKeyboard,
|
os.popup(import('@/components/emoji-picker.vue'), {
|
||||||
src: this.$refs.reactButton,
|
src: this.$refs.reactButton,
|
||||||
}, {
|
}, {
|
||||||
done: reaction => {
|
done: reaction => {
|
||||||
if (reaction) {
|
if (reaction) {
|
||||||
os.api('notes/reactions/create', {
|
os.api('notes/reactions/create', {
|
||||||
noteId: this.appearNote.id,
|
noteId: this.appearNote.id,
|
||||||
reaction: reaction
|
reaction: reaction
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.focus();
|
this.focus();
|
||||||
},
|
},
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
|
} else {
|
||||||
|
os.popup(import('@/components/reaction-picker.vue'), {
|
||||||
|
showFocus: viaKeyboard,
|
||||||
|
src: this.$refs.reactButton,
|
||||||
|
}, {
|
||||||
|
done: reaction => {
|
||||||
|
if (reaction) {
|
||||||
|
os.api('notes/reactions/create', {
|
||||||
|
noteId: this.appearNote.id,
|
||||||
|
reaction: reaction
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.focus();
|
||||||
|
},
|
||||||
|
}, 'closed');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
reactDirectly(reaction) {
|
reactDirectly(reaction) {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
{{ $t('reaction') }}<template #desc>{{ $t('reactionSettingDescription') }} <button class="_textButton" @click="chooseEmoji">{{ $t('chooseEmoji') }}</button></template>
|
{{ $t('reaction') }}<template #desc>{{ $t('reactionSettingDescription') }} <button class="_textButton" @click="chooseEmoji">{{ $t('chooseEmoji') }}</button></template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkButton inline @click="setDefault"><Fa :icon="faUndo"/> {{ $t('default') }}</MkButton>
|
<MkButton inline @click="setDefault"><Fa :icon="faUndo"/> {{ $t('default') }}</MkButton>
|
||||||
|
<MkSwitch v-model:value="useFullReactionPicker">{{ $t('useFullReactionPicker') }}</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
<div class="_footer">
|
<div class="_footer">
|
||||||
<MkButton @click="save()" primary inline :disabled="!changed"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
|
<MkButton @click="save()" primary inline :disabled="!changed"><Fa :icon="faSave"/> {{ $t('save') }}</MkButton>
|
||||||
|
@ -22,6 +23,7 @@ import { faLaugh, faSave, faEye } from '@fortawesome/free-regular-svg-icons';
|
||||||
import { faUndo } from '@fortawesome/free-solid-svg-icons';
|
import { faUndo } from '@fortawesome/free-solid-svg-icons';
|
||||||
import MkInput from '@/components/ui/input.vue';
|
import MkInput from '@/components/ui/input.vue';
|
||||||
import MkButton from '@/components/ui/button.vue';
|
import MkButton from '@/components/ui/button.vue';
|
||||||
|
import MkSwitch from '@/components/ui/switch.vue';
|
||||||
import { emojiRegexWithCustom } from '../../../misc/emoji-regex';
|
import { emojiRegexWithCustom } from '../../../misc/emoji-regex';
|
||||||
import { defaultSettings } from '@/store';
|
import { defaultSettings } from '@/store';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
|
@ -30,6 +32,7 @@ export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
MkInput,
|
MkInput,
|
||||||
MkButton,
|
MkButton,
|
||||||
|
MkSwitch,
|
||||||
},
|
},
|
||||||
|
|
||||||
emits: ['info'],
|
emits: ['info'],
|
||||||
|
@ -50,6 +53,11 @@ export default defineComponent({
|
||||||
splited(): any {
|
splited(): any {
|
||||||
return this.reactions.match(emojiRegexWithCustom);
|
return this.reactions.match(emojiRegexWithCustom);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
useFullReactionPicker: {
|
||||||
|
get() { return this.$store.state.device.useFullReactionPicker; },
|
||||||
|
set(value) { this.$store.commit('device/set', { key: 'useFullReactionPicker', value: value }); }
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -72,11 +80,18 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
preview(ev) {
|
preview(ev) {
|
||||||
os.popup(import('@/components/reaction-picker.vue'), {
|
if (this.$store.state.device.useFullReactionPicker) {
|
||||||
reactions: this.splited,
|
os.popup(import('@/components/emoji-picker.vue'), {
|
||||||
showFocus: false,
|
reactions: this.splited,
|
||||||
src: ev.currentTarget || ev.target,
|
src: ev.currentTarget || ev.target,
|
||||||
}, {}, 'closed');
|
}, {}, 'closed');
|
||||||
|
} else {
|
||||||
|
os.popup(import('@/components/reaction-picker.vue'), {
|
||||||
|
reactions: this.splited,
|
||||||
|
showFocus: false,
|
||||||
|
src: ev.currentTarget || ev.target,
|
||||||
|
}, {}, 'closed');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setDefault() {
|
setDefault() {
|
||||||
|
|
|
@ -76,6 +76,7 @@ export const defaultDeviceSettings = {
|
||||||
disablePagesScript: false,
|
disablePagesScript: false,
|
||||||
enableInfiniteScroll: true,
|
enableInfiniteScroll: true,
|
||||||
useBlurEffectForModal: true,
|
useBlurEffectForModal: true,
|
||||||
|
useFullReactionPicker: false,
|
||||||
sidebarDisplay: 'full', // full, icon, hide
|
sidebarDisplay: 'full', // full, icon, hide
|
||||||
instanceTicker: 'remote', // none, remote, always
|
instanceTicker: 'remote', // none, remote, always
|
||||||
roomGraphicsQuality: 'medium',
|
roomGraphicsQuality: 'medium',
|
||||||
|
@ -182,6 +183,16 @@ export const store = createStore({
|
||||||
meta: null
|
meta: null
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
emojiCategories: state => {
|
||||||
|
const categories = new Set();
|
||||||
|
for (const emoji of state.meta.emojis) {
|
||||||
|
categories.add(emoji.category);
|
||||||
|
}
|
||||||
|
return Array.from(categories);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
set(state, meta) {
|
set(state, meta) {
|
||||||
state.meta = meta;
|
state.meta = meta;
|
||||||
|
|
Loading…
Reference in a new issue