client: improve emoji picker search
All checks were successful
ci/woodpecker/push/lint-backend Pipeline was successful
ci/woodpecker/push/lint-client Pipeline was successful
ci/woodpecker/push/lint-foundkey-js Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
All checks were successful
ci/woodpecker/push/lint-backend Pipeline was successful
ci/woodpecker/push/lint-client Pipeline was successful
ci/woodpecker/push/lint-foundkey-js Pipeline was successful
ci/woodpecker/push/build Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
Reviewed-on: #101 Changelog: Added
This commit is contained in:
commit
5e320e49ab
4 changed files with 54 additions and 133 deletions
|
@ -987,6 +987,10 @@ _serverDisconnectedBehavior:
|
|||
reload: "Automatically reload"
|
||||
dialog: "Show warning dialog"
|
||||
quiet: "Show unobtrusive warning"
|
||||
maxCustomEmojiPicker: "Maximum suggested custom emoji in picker"
|
||||
maxCustomEmojiPickerDescription: "0 for unlimited"
|
||||
maxUnicodeEmojiPicker: "Maximum suggested unicode emoji in picker"
|
||||
maxUnicodeEmojiPickerDescription: "0 for unlimited"
|
||||
_channel:
|
||||
create: "Create channel"
|
||||
edit: "Edit channel"
|
||||
|
|
|
@ -112,6 +112,8 @@ const {
|
|||
reactionPickerSize,
|
||||
reactionPickerWidth,
|
||||
reactionPickerHeight,
|
||||
maxCustomEmojiPicker,
|
||||
maxUnicodeEmojiPicker,
|
||||
disableShowingAnimatedImages,
|
||||
recentlyUsedEmojis,
|
||||
} = defaultStore.reactiveState;
|
||||
|
@ -126,145 +128,41 @@ const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]);
|
|||
const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
|
||||
const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
|
||||
|
||||
watch(q, () => {
|
||||
if (emojis.value) emojis.value.scrollTop = 0;
|
||||
function emojiSearch<Type>(src: Type[], max: number, query: string): Type[] {
|
||||
// discount fuzzy matching pattern
|
||||
const re = new RegExp(query.split(' ').join('.*'), 'i');
|
||||
const match = (str: string): boolean => str && re.test(str);
|
||||
const matches = src.filter(emoji =>
|
||||
match(emoji.name)
|
||||
|| emoji.aliases?.some(match) // custom emoji
|
||||
|| emoji.keywords?.some(match) // unicode emoji
|
||||
);
|
||||
// TODO: sort matches by distance to query
|
||||
if (max <= 0 || matches.length < max) return matches;
|
||||
return matches.slice(0, max);
|
||||
}
|
||||
|
||||
if (q.value == null || q.value === '') {
|
||||
let queryTimeoutId = -1;
|
||||
const queryCallback = (query) => {
|
||||
if (emojis.value) emojis.value.scrollTop = 0;
|
||||
searchResultCustom.value = emojiSearch(instance.emojis, maxCustomEmojiPicker.value, query);
|
||||
searchResultUnicode.value = emojiSearch(emojilist, maxUnicodeEmojiPicker.value, query);
|
||||
queryTimeoutId = -1;
|
||||
}
|
||||
watch(q, () => {
|
||||
if(queryTimeoutId >= 0) {
|
||||
clearTimeout(queryTimeoutId);
|
||||
queryTimeoutId = -1;
|
||||
}
|
||||
|
||||
const query = q.value;
|
||||
if (query == null || query === '') {
|
||||
searchResultCustom.value = [];
|
||||
searchResultUnicode.value = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const newQ = q.value.replace(/:/g, '').toLowerCase();
|
||||
|
||||
const searchCustom = () => {
|
||||
const max = 8;
|
||||
const emojis = customEmojis;
|
||||
const matches = new Set<Misskey.entities.CustomEmoji>();
|
||||
|
||||
const exactMatch = emojis.find(emoji => emoji.name === newQ);
|
||||
if (exactMatch) matches.add(exactMatch);
|
||||
|
||||
if (newQ.includes(' ')) { // AND検索
|
||||
const keywords = newQ.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(newQ)) {
|
||||
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(newQ))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.name.includes(newQ)) {
|
||||
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(newQ))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
const searchUnicode = () => {
|
||||
const max = 8;
|
||||
const emojis = emojilist;
|
||||
const matches = new Set<UnicodeEmojiDef>();
|
||||
|
||||
const exactMatch = emojis.find(emoji => emoji.name === newQ);
|
||||
if (exactMatch) matches.add(exactMatch);
|
||||
|
||||
if (newQ.includes(' ')) { // AND検索
|
||||
const keywords = newQ.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(newQ)) {
|
||||
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(newQ))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
if (matches.size >= max) return matches;
|
||||
|
||||
for (const emoji of emojis) {
|
||||
if (emoji.name.includes(newQ)) {
|
||||
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(newQ))) {
|
||||
matches.add(emoji);
|
||||
if (matches.size >= max) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
searchResultCustom.value = Array.from(searchCustom());
|
||||
searchResultUnicode.value = Array.from(searchUnicode());
|
||||
queryTimeoutId = setTimeout(queryCallback, 300, query);
|
||||
});
|
||||
|
||||
function focus() {
|
||||
|
|
|
@ -35,6 +35,15 @@
|
|||
<option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option>
|
||||
<option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
|
||||
</FormSelect>
|
||||
|
||||
<FormRange v-model="maxCustomEmojiPicker" :min="0" :max="24" :step="1" class="_formBlock">
|
||||
<template #label>{{ i18n.ts.maxCustomEmojiPicker }}</template>
|
||||
<template #caption>{{ i18n.ts.maxCustomEmojiPickerDescription }}</template>
|
||||
</FormRange>
|
||||
<FormRange v-model="maxUnicodeEmojiPicker" :min="0" :max="24" :step="1" class="_formBlock">
|
||||
<template #label>{{ i18n.ts.maxUnicodeEmojiPicker }}</template>
|
||||
<template #caption>{{ i18n.ts.maxUnicodeEmojiPickerDescription }}</template>
|
||||
</FormRange>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
|
@ -124,6 +133,8 @@ async function reloadAsk() {
|
|||
|
||||
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
|
||||
const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
|
||||
const maxCustomEmojiPicker = computed(defaultStore.makeGetterSetter('maxCustomEmojiPicker'));
|
||||
const maxUnicodeEmojiPicker = computed(defaultStore.makeGetterSetter('maxUnicodeEmojiPicker'));
|
||||
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
|
||||
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
|
||||
const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
|
||||
|
|
|
@ -108,6 +108,14 @@ export const defaultStore = markRaw(new Storage('base', {
|
|||
where: 'device',
|
||||
default: 'quiet' as 'quiet' | 'reload' | 'dialog',
|
||||
},
|
||||
maxCustomEmojiPicker: {
|
||||
where: 'device',
|
||||
default: 10,
|
||||
},
|
||||
maxUnicodeEmojiPicker: {
|
||||
where: 'device',
|
||||
default: 10,
|
||||
},
|
||||
nsfw: {
|
||||
where: 'device',
|
||||
default: 'respect' as 'respect' | 'force' | 'ignore',
|
||||
|
|
Loading…
Reference in a new issue