mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 07:24:13 +00:00 
			
		
		
		
	絵文字のオートコンプリート強化の対応 (#12365)
* 前方一致・部分一致でなくても近似値でヒットするように * fix CHANGELOG.md * for of に変更 --------- Co-authored-by: osamu <46447427+sam-osamu@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									b15f293b82
								
							
						
					
					
						commit
						ded328fb43
					
				
					 2 changed files with 78 additions and 23 deletions
				
			
		| 
						 | 
				
			
			@ -19,6 +19,7 @@
 | 
			
		|||
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
 | 
			
		||||
 | 
			
		||||
### Client
 | 
			
		||||
- Enhance: 絵文字のオートコンプリート機能強化 #12364
 | 
			
		||||
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
 | 
			
		||||
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -242,29 +242,7 @@ function exec() {
 | 
			
		|||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const matched: EmojiDef[] = [];
 | 
			
		||||
		const max = 30;
 | 
			
		||||
 | 
			
		||||
		emojiDb.value.some(x => {
 | 
			
		||||
			if (x.name.startsWith(props.q ?? '') && !x.aliasOf && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
 | 
			
		||||
			return matched.length === max;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		if (matched.length < max) {
 | 
			
		||||
			emojiDb.value.some(x => {
 | 
			
		||||
				if (x.name.startsWith(props.q ?? '') && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
 | 
			
		||||
				return matched.length === max;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (matched.length < max) {
 | 
			
		||||
			emojiDb.value.some(x => {
 | 
			
		||||
				if (x.name.includes(props.q ?? '') && !matched.some(y => y.emoji === x.emoji)) matched.push(x);
 | 
			
		||||
				return matched.length === max;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		emojis.value = matched;
 | 
			
		||||
		emojis.value = emojiAutoComplete(props.q, emojiDb.value);
 | 
			
		||||
	} else if (props.type === 'mfmTag') {
 | 
			
		||||
		if (!props.q || props.q === '') {
 | 
			
		||||
			mfmTags.value = MFM_TAGS;
 | 
			
		||||
| 
						 | 
				
			
			@ -275,6 +253,82 @@ function exec() {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type EmojiScore = { emoji: EmojiDef, score: number };
 | 
			
		||||
 | 
			
		||||
function emojiAutoComplete(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] {
 | 
			
		||||
	if (!query) {
 | 
			
		||||
		return [];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const matched = new Map<string, EmojiScore>();
 | 
			
		||||
 | 
			
		||||
	// 前方一致(エイリアスなし)
 | 
			
		||||
	emojiDb.some(x => {
 | 
			
		||||
		if (x.name.startsWith(query) && !x.aliasOf) {
 | 
			
		||||
			matched.set(x.name, { emoji: x, score: query.length });
 | 
			
		||||
		}
 | 
			
		||||
		return matched.size === max;
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// 前方一致(エイリアス込み)
 | 
			
		||||
	if (matched.size < max) {
 | 
			
		||||
		emojiDb.some(x => {
 | 
			
		||||
			if (x.name.startsWith(query)) {
 | 
			
		||||
				matched.set(x.name, { emoji: x, score: query.length });
 | 
			
		||||
			}
 | 
			
		||||
			return matched.size === max;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 部分一致(エイリアス込み)
 | 
			
		||||
	if (matched.size < max) {
 | 
			
		||||
		emojiDb.some(x => {
 | 
			
		||||
			if (x.name.includes(query)) {
 | 
			
		||||
				matched.set(x.name, { emoji: x, score: query.length });
 | 
			
		||||
			}
 | 
			
		||||
			return matched.size === max;
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 簡易あいまい検索
 | 
			
		||||
	if (matched.size < max) {
 | 
			
		||||
		const queryChars = [...query];
 | 
			
		||||
		const hitEmojis = new Map<string, EmojiScore>();
 | 
			
		||||
 | 
			
		||||
		for (const x of emojiDb) {
 | 
			
		||||
			// クエリ文字列の1文字単位で絵文字名にヒットするかを見る
 | 
			
		||||
			// ただし、過剰に検出されるのを防ぐためクエリ文字列に登場する順番で絵文字名を走査する
 | 
			
		||||
 | 
			
		||||
			let queryCharHitPos = 0;
 | 
			
		||||
			let queryCharHitCount = 0;
 | 
			
		||||
			for (let idx = 0; idx < queryChars.length; idx++) {
 | 
			
		||||
				queryCharHitPos = x.name.indexOf(queryChars[idx], queryCharHitPos);
 | 
			
		||||
				if (queryCharHitPos <= -1) {
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				queryCharHitCount++;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// ヒット数が少なすぎると検索結果が汚れるので調節する
 | 
			
		||||
			if (queryCharHitCount > 2) {
 | 
			
		||||
				hitEmojis.set(x.name, { emoji: x, score: queryCharHitCount });
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// ヒットしたものを全部追加すると雑多になるので、先頭の6件程度だけにしておく(6件=オートコンプリートのポップアップのサイズ分)
 | 
			
		||||
		[...hitEmojis.values()]
 | 
			
		||||
			.sort((x, y) => y.score - x.score)
 | 
			
		||||
			.slice(0, 6)
 | 
			
		||||
			.forEach(it => matched.set(it.emoji.name, it));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return [...matched.values()]
 | 
			
		||||
		.sort((x, y) => y.score - x.score)
 | 
			
		||||
		.slice(0, max)
 | 
			
		||||
		.map(it => it.emoji);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onMousedown(event: Event) {
 | 
			
		||||
	if (!contains(rootEl.value, event.target) && (rootEl.value !== event.target)) props.close();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue