summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2024-03-01 21:17:01 +0900
committerGitHub <noreply@github.com>2024-03-01 21:17:01 +0900
commit7e706ea6693781fb8973800bb3d0c91b5ab91cdf (patch)
tree8abd91edd288284cf3cc47949e749f881cfaff8e /packages/frontend/src/scripts
parentMerge pull request #13045 from misskey-dev/develop (diff)
parentNew translations ja-jp.yml (Chinese Traditional) (#13480) (diff)
downloadsharkey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.tar.gz
sharkey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.tar.bz2
sharkey-7e706ea6693781fb8973800bb3d0c91b5ab91cdf.zip
Merge pull request #13447 from misskey-dev/develop
Release: 2024.3.0
Diffstat (limited to 'packages/frontend/src/scripts')
-rw-r--r--packages/frontend/src/scripts/autocomplete.ts4
-rw-r--r--packages/frontend/src/scripts/check-reaction-permissions.ts6
-rw-r--r--packages/frontend/src/scripts/clear-cache.ts4
-rw-r--r--packages/frontend/src/scripts/emojilist.ts9
-rw-r--r--packages/frontend/src/scripts/form.ts17
-rw-r--r--packages/frontend/src/scripts/search-emoji.ts106
-rw-r--r--packages/frontend/src/scripts/sound.ts2
7 files changed, 144 insertions, 4 deletions
diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index fe515d81a1..9fc8f7843e 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -93,9 +93,11 @@ export class Autocomplete {
return;
}
+ const afterLastMfmParam = text.split(/\$\[[a-zA-Z]+/).pop();
+
const isMention = mentionIndex !== -1;
const isHashtag = hashtagIndex !== -1;
- const isMfmParam = mfmParamIndex !== -1 && text.split(/\$\[[a-zA-Z]+/).pop()?.includes('.');
+ const isMfmParam = mfmParamIndex !== -1 && afterLastMfmParam?.includes('.') && !afterLastMfmParam?.includes(' ');
const isMfmTag = mfmTagIndex !== -1 && !isMfmParam;
const isEmoji = emojiIndex !== -1 && text.split(/:[a-z0-9_+\-]+:/).pop()!.includes(':');
diff --git a/packages/frontend/src/scripts/check-reaction-permissions.ts b/packages/frontend/src/scripts/check-reaction-permissions.ts
index c9d2a5bfc6..da704717c1 100644
--- a/packages/frontend/src/scripts/check-reaction-permissions.ts
+++ b/packages/frontend/src/scripts/check-reaction-permissions.ts
@@ -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 ?? [];
return !(emoji.localOnly && note.user.host !== me.host)
&& !(emoji.isSensitive && (note.reactionAcceptance === 'nonSensitiveOnly' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote'))
diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts
index f2db87c4fb..b20109ec72 100644
--- a/packages/frontend/src/scripts/clear-cache.ts
+++ b/packages/frontend/src/scripts/clear-cache.ts
@@ -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();
}
diff --git a/packages/frontend/src/scripts/emojilist.ts b/packages/frontend/src/scripts/emojilist.ts
index 54d45e025f..f2fbeae0ed 100644
--- a/packages/frontend/src/scripts/emojilist.ts
+++ b/packages/frontend/src/scripts/emojilist.ts
@@ -20,6 +20,10 @@ export const emojilist: UnicodeEmojiDef[] = _emojilist.map(x => ({
category: unicodeEmojiCategories[x[2]],
}));
+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++) {
@@ -35,6 +39,11 @@ for (let i = 0; i < emojilist.length; i++) {
export const emojiCharByCategory = _charGroupByCategory;
+export function getUnicodeEmoji(char: string): UnicodeEmojiDef | null {
+ // Colorize it because emojilist.json assumes that
+ return unicodeEmojisMap.get(colorizeEmoji(char)) ?? null;
+}
+
export function getEmojiName(char: string): string | null {
// Colorize it because emojilist.json assumes that
const idx = _indexByChar.get(colorizeEmoji(char));
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index 26a027f461..b0db404f28 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -12,29 +12,37 @@ export type FormItem = {
label?: string;
type: 'string';
default: string | null;
+ description?: string;
+ required?: boolean;
hidden?: boolean;
multiline?: boolean;
+ treatAsMfm?: boolean;
} | {
label?: string;
type: 'number';
default: number | null;
+ description?: string;
+ required?: boolean;
hidden?: boolean;
step?: number;
} | {
label?: string;
type: 'boolean';
default: boolean | null;
+ description?: string;
hidden?: boolean;
} | {
label?: string;
type: 'enum';
default: string | null;
+ required?: boolean;
hidden?: boolean;
enum: EnumItem[];
} | {
label?: string;
type: 'radio';
default: unknown | null;
+ required?: boolean;
hidden?: boolean;
options: {
label: string;
@@ -44,9 +52,12 @@ export type FormItem = {
label?: string;
type: 'range';
default: number | null;
- step: number;
+ description?: string;
+ required?: boolean;
+ step?: number;
min: number;
max: number;
+ textConverter?: (value: number) => string;
} | {
label?: string;
type: 'object';
@@ -57,6 +68,10 @@ export type FormItem = {
type: 'array';
default: unknown[] | null;
hidden: boolean;
+} | {
+ type: 'button';
+ content?: string;
+ action: (ev: MouseEvent, v: any) => void;
};
export type Form = Record<string, FormItem>;
diff --git a/packages/frontend/src/scripts/search-emoji.ts b/packages/frontend/src/scripts/search-emoji.ts
new file mode 100644
index 0000000000..371f69b9a7
--- /dev/null
+++ b/packages/frontend/src/scripts/search-emoji.ts
@@ -0,0 +1,106 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+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);
+}
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 9555579e0d..fcd59510df 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -126,7 +126,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) return;
+ if (sound.type == null || !canPlay || ('userActivation' in navigator && !navigator.userActivation.hasBeenActive)) return;
canPlay = false;
playMisskeySfxFile(sound).finally(() => {