forked from FoundKeyGang/FoundKey
client: improve emoji picker search
Reviewed-on: FoundKeyGang/FoundKey#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"
|
reload: "Automatically reload"
|
||||||
dialog: "Show warning dialog"
|
dialog: "Show warning dialog"
|
||||||
quiet: "Show unobtrusive warning"
|
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:
|
_channel:
|
||||||
create: "Create channel"
|
create: "Create channel"
|
||||||
edit: "Edit channel"
|
edit: "Edit channel"
|
||||||
|
|
|
@ -112,6 +112,8 @@ const {
|
||||||
reactionPickerSize,
|
reactionPickerSize,
|
||||||
reactionPickerWidth,
|
reactionPickerWidth,
|
||||||
reactionPickerHeight,
|
reactionPickerHeight,
|
||||||
|
maxCustomEmojiPicker,
|
||||||
|
maxUnicodeEmojiPicker,
|
||||||
disableShowingAnimatedImages,
|
disableShowingAnimatedImages,
|
||||||
recentlyUsedEmojis,
|
recentlyUsedEmojis,
|
||||||
} = defaultStore.reactiveState;
|
} = defaultStore.reactiveState;
|
||||||
|
@ -126,145 +128,41 @@ const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]);
|
||||||
const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
|
const searchResultUnicode = ref<UnicodeEmojiDef[]>([]);
|
||||||
const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
|
const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index');
|
||||||
|
|
||||||
watch(q, () => {
|
function emojiSearch<Type>(src: Type[], max: number, query: string): Type[] {
|
||||||
if (emojis.value) emojis.value.scrollTop = 0;
|
// 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 = [];
|
searchResultCustom.value = [];
|
||||||
searchResultUnicode.value = [];
|
searchResultUnicode.value = [];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newQ = q.value.replace(/:/g, '').toLowerCase();
|
queryTimeoutId = setTimeout(queryCallback, 300, query);
|
||||||
|
|
||||||
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());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function focus() {
|
function focus() {
|
||||||
|
|
|
@ -35,6 +35,15 @@
|
||||||
<option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option>
|
<option value="dialog">{{ i18n.ts._serverDisconnectedBehavior.dialog }}</option>
|
||||||
<option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
|
<option value="quiet">{{ i18n.ts._serverDisconnectedBehavior.quiet }}</option>
|
||||||
</FormSelect>
|
</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>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
@ -124,6 +133,8 @@ async function reloadAsk() {
|
||||||
|
|
||||||
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
|
const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDeviceKind'));
|
||||||
const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
|
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 reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
|
||||||
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
|
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
|
||||||
const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
|
const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect'));
|
||||||
|
|
|
@ -108,6 +108,14 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'quiet' as 'quiet' | 'reload' | 'dialog',
|
default: 'quiet' as 'quiet' | 'reload' | 'dialog',
|
||||||
},
|
},
|
||||||
|
maxCustomEmojiPicker: {
|
||||||
|
where: 'device',
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
maxUnicodeEmojiPicker: {
|
||||||
|
where: 'device',
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
nsfw: {
|
nsfw: {
|
||||||
where: 'device',
|
where: 'device',
|
||||||
default: 'respect' as 'respect' | 'force' | 'ignore',
|
default: 'respect' as 'respect' | 'force' | 'ignore',
|
||||||
|
|
Loading…
Reference in a new issue