Feat: アバターデコレーションの検索機能

This commit is contained in:
mattyatea 2024-01-11 07:03:14 +09:00
parent be2626ea0d
commit 5320e6db77

View file

@ -28,7 +28,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton> <MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton>
</div> </div>
<MkInput v-model="q" :placeholder="i18n.ts.search"/>
<div v-if="searchResult.length > 0" :class="$style.decorations">
<span> {{ i18n.ts.searchResult }}</span><br>
<XDecoration
v-for="avatarDecoration in searchResult"
:key="avatarDecoration.name"
:decoration="avatarDecoration"
@click="openDecoration(avatarDecoration)"
/>
</div>
<div v-for="category in categories"> <div v-for="category in categories">
<MkFoldableSection :defaultOpen="false"> <MkFoldableSection :defaultOpen="false">
<template #header> {{ (category !== '') ? category : i18n.ts.other }}</template> <template #header> {{ (category !== '') ? category : i18n.ts.other }}</template>
@ -51,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, defineAsyncComponent, computed } from 'vue'; import { ref, defineAsyncComponent, computed, watch } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import XDecoration from './avatar-decoration.decoration.vue'; import XDecoration from './avatar-decoration.decoration.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
@ -62,12 +71,62 @@ import { signinRequired } from '@/account.js';
import MkInfo from '@/components/MkInfo.vue'; import MkInfo from '@/components/MkInfo.vue';
import { definePageMetadata } from '@/scripts/page-metadata.js'; import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkInput from '@/components/MkInput.vue';
const $i = signinRequired(); const $i = signinRequired();
const searchResult = ref([]);
const loading = ref(true); const loading = ref(true);
const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse & { category:string }>([]); const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse & { category:string }>([]);
const q = ref<string>('');
watch(() => q.value, () => {
const searchCustom = () => {
const max = 100;
const matches = new Set();
const decos = avatarDecorations.value;
const exactMatch = decos.find(avatarDecoration => avatarDecoration.name === q.value);
if (exactMatch) matches.add(exactMatch);
if (decos.includes(' ')) { // AND
const keywords = q.value.split(' ');
//
for (const deco of decos) {
if (keywords.every(keyword => deco.name.includes(keyword))) {
matches.add(deco);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
//
for (const deco of decos) {
if (keywords.every(keyword => deco.name.includes(keyword))) {
matches.add(deco);
if (matches.size >= max) break;
}
}
} else {
for (const deco of decos) {
if (deco.name.startsWith(q.value)) {
matches.add(deco);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
for (const deco of decos) {
if (deco.name.includes(q.value)) {
matches.add(deco);
if (matches.size >= max) break;
}
}
if (matches.size >= max) return matches;
}
return matches;
};
searchResult.value = Array.from(searchCustom());
});
misskeyApi('get-avatar-decorations').then(_avatarDecorations => { misskeyApi('get-avatar-decorations').then(_avatarDecorations => {
avatarDecorations.value = _avatarDecorations; avatarDecorations.value = _avatarDecorations;
loading.value = false; loading.value = false;