Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop
This commit is contained in:
commit
52b84e7d61
|
@ -19,6 +19,7 @@
|
|||
- Feat: モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能を追加 (cherry-pick from https://github.com/TeamNijimiss/misskey/commit/e0eb5a752f6e5616d6312bb7c9790302f9dbff83)
|
||||
|
||||
### Client
|
||||
- Enhance: 絵文字のオートコンプリート機能強化 #12364
|
||||
- fix: 「設定のバックアップ」で一部の項目がバックアップに含まれていなかった問題を修正
|
||||
- Fix: ウィジェットのジョブキューにて音声の発音方法変更に追従できていなかったのを修正 #12367
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export const paramDef = {
|
|||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
publishing: { type: 'boolean', default: null, nullable: true},
|
||||
publishing: { type: 'boolean', default: null, nullable: true },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
|
|
@ -243,29 +243,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;
|
||||
|
@ -276,6 +254,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();
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<XHeader :actions="headerActions" :tabs="headerTabs"/>
|
||||
</template>
|
||||
<MkSpacer :contentMax="900">
|
||||
<MkSelect v-model="type" :class="$style.input" @update:modelValue="onChangePublishing">
|
||||
<MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems">
|
||||
<template #label>{{ i18n.ts.state }}</template>
|
||||
<option value="null">{{ i18n.ts.all }}</option>
|
||||
<option value="true">{{ i18n.ts.publishing }}</option>
|
||||
<option value="false">{{ i18n.ts.expired }}</option>
|
||||
<option value="all">{{ i18n.ts.all }}</option>
|
||||
<option value="publishing">{{ i18n.ts.publishing }}</option>
|
||||
<option value="expired">{{ i18n.ts.expired }}</option>
|
||||
</MkSelect>
|
||||
<div>
|
||||
<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
|
||||
|
@ -102,8 +102,8 @@ let ads: any[] = $ref([]);
|
|||
const localTime = new Date();
|
||||
const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
|
||||
const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
|
||||
const filterType = ref('all');
|
||||
let publishing: boolean | null = null;
|
||||
let type = ref('null');
|
||||
|
||||
os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
||||
if (adsResponse != null) {
|
||||
|
@ -121,9 +121,15 @@ os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
|||
}
|
||||
});
|
||||
|
||||
const onChangePublishing = (v) => {
|
||||
console.log(v);
|
||||
publishing = v === 'true' ? true : v === 'false' ? false : null;
|
||||
const filterItems = (v) => {
|
||||
if (v === 'publishing') {
|
||||
publishing = true;
|
||||
} else if (v === 'expired') {
|
||||
publishing = false;
|
||||
} else {
|
||||
publishing = null;
|
||||
}
|
||||
|
||||
refresh();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue