Merge remote-tracking branch 'misskey-dev/develop' into io
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { UnicodeEmojiDef } from './emojilist.js';
|
||||
|
||||
export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple): boolean {
|
||||
export function checkReactionPermissions(me: Misskey.entities.MeDetailed, note: Misskey.entities.Note, emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef): boolean {
|
||||
if ('char' in emoji) return true; // UnicodeEmojiDefなら常にリアクション可能
|
||||
|
||||
emoji = emoji as Misskey.entities.EmojiSimple;
|
||||
const roleIdsThatCanBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [];
|
||||
const roleIdsThatCanNotBeUsedThisEmojiAsReaction = emoji.roleIdsThatCanNotBeUsedThisEmojiAsReaction ?? [];
|
||||
|
||||
|
@@ -2,14 +2,18 @@ import { unisonReload } from '@/scripts/unison-reload.js';
|
||||
import * as os from '@/os.js';
|
||||
import { miLocalStorage } from '@/local-storage.js';
|
||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||
import { fetchInstance } from '@/instance.js';
|
||||
|
||||
export async function clearCache() {
|
||||
os.waiting();
|
||||
miLocalStorage.removeItem('instance');
|
||||
miLocalStorage.removeItem('instanceCachedAt');
|
||||
miLocalStorage.removeItem('locale');
|
||||
miLocalStorage.removeItem('localeVersion');
|
||||
miLocalStorage.removeItem('theme');
|
||||
miLocalStorage.removeItem('emojis');
|
||||
miLocalStorage.removeItem('lastEmojisFetchedAt');
|
||||
await fetchInstance(true);
|
||||
await fetchCustomEmojis(true);
|
||||
unisonReload();
|
||||
}
|
||||
|
@@ -20,6 +20,10 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({
|
||||
category: unicodeEmojiCategories[x[2]],
|
||||
}));
|
||||
|
||||
export const unicodeEmojisMap = new Map<string, UnicodeEmojiDef>(
|
||||
emojilist.map(x => [x.char, x])
|
||||
);
|
||||
|
||||
const _indexByChar = new Map<string, number>();
|
||||
const _charGroupByCategory = new Map<string, string[]>();
|
||||
for (let i = 0; i < emojilist.length; i++) {
|
||||
|
101
packages/frontend/src/scripts/search-emoji.ts
Normal file
101
packages/frontend/src/scripts/search-emoji.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
export type EmojiDef = {
|
||||
emoji: string;
|
||||
name: string;
|
||||
url: string;
|
||||
aliasOf?: string;
|
||||
} | {
|
||||
emoji: string;
|
||||
name: string;
|
||||
aliasOf?: string;
|
||||
isCustomEmoji?: true;
|
||||
};
|
||||
type EmojiScore = { emoji: EmojiDef, score: number };
|
||||
|
||||
export function searchEmoji(query: string | null, emojiDb: EmojiDef[], max = 30): EmojiDef[] {
|
||||
if (!query) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const matched = new Map<string, EmojiScore>();
|
||||
// 完全一致(エイリアスなし)
|
||||
emojiDb.some(x => {
|
||||
if (x.name === query && !x.aliasOf) {
|
||||
matched.set(x.name, { emoji: x, score: query.length + 3 });
|
||||
}
|
||||
return matched.size === max;
|
||||
});
|
||||
|
||||
// 完全一致(エイリアス込み)
|
||||
if (matched.size < max) {
|
||||
emojiDb.some(x => {
|
||||
if (x.name === query && !matched.has(x.aliasOf ?? x.name)) {
|
||||
matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length + 2 });
|
||||
}
|
||||
return matched.size === max;
|
||||
});
|
||||
}
|
||||
|
||||
// 前方一致(エイリアスなし)
|
||||
if (matched.size < max) {
|
||||
emojiDb.some(x => {
|
||||
if (x.name.startsWith(query) && !x.aliasOf && !matched.has(x.name)) {
|
||||
matched.set(x.name, { emoji: x, score: query.length + 1 });
|
||||
}
|
||||
return matched.size === max;
|
||||
});
|
||||
}
|
||||
|
||||
// 前方一致(エイリアス込み)
|
||||
if (matched.size < max) {
|
||||
emojiDb.some(x => {
|
||||
if (x.name.startsWith(query) && !matched.has(x.aliasOf ?? x.name)) {
|
||||
matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length });
|
||||
}
|
||||
return matched.size === max;
|
||||
});
|
||||
}
|
||||
|
||||
// 部分一致(エイリアス込み)
|
||||
if (matched.size < max) {
|
||||
emojiDb.some(x => {
|
||||
if (x.name.includes(query) && !matched.has(x.aliasOf ?? x.name)) {
|
||||
matched.set(x.aliasOf ?? x.name, { emoji: x, score: query.length - 1 });
|
||||
}
|
||||
return matched.size === max;
|
||||
});
|
||||
}
|
||||
|
||||
// 簡易あいまい検索(3文字以上)
|
||||
if (matched.size < max && query.length > 3) {
|
||||
const queryChars = [...query];
|
||||
const hitEmojis = new Map<string, EmojiScore>();
|
||||
|
||||
for (const x of emojiDb) {
|
||||
// 文字列の位置を進めながら、クエリの文字を順番に探す
|
||||
|
||||
let pos = 0;
|
||||
let hit = 0;
|
||||
for (const c of queryChars) {
|
||||
pos = x.name.indexOf(c, pos);
|
||||
if (pos <= -1) break;
|
||||
hit++;
|
||||
}
|
||||
|
||||
// 半分以上の文字が含まれていればヒットとする
|
||||
if (hit > Math.ceil(queryChars.length / 2) && hit - 2 > (matched.get(x.aliasOf ?? x.name)?.score ?? 0)) {
|
||||
hitEmojis.set(x.aliasOf ?? x.name, { emoji: x, score: hit - 2 });
|
||||
}
|
||||
}
|
||||
|
||||
// ヒットしたものを全部追加すると雑多になるので、先頭の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);
|
||||
}
|
@@ -188,7 +188,7 @@ export async function loadAudio(url: string, options?: { useCache?: boolean; })
|
||||
*/
|
||||
export function playMisskeySfx(operationType: OperationType) {
|
||||
const sound = defaultStore.state[`sound_${operationType}`];
|
||||
if (sound.type == null || !canPlay || !navigator.userActivation.hasBeenActive) return;
|
||||
if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return;
|
||||
|
||||
canPlay = false;
|
||||
playMisskeySfxFile(sound).finally(() => {
|
||||
|
Reference in New Issue
Block a user