mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-06-25 17:10:43 +00:00
enhance(frontend): 絵文字メニューから直接絵文字パレットに追加できるように (#17420)
* enhance(frontend): 絵文字メニューから直接絵文字パレットに追加できるように
* Update Changelog
* fix lint
* Update Changelog
* enhance: 追加し直す挙動に変更
* ✌️
* fix
This commit is contained in:
parent
4ae53440b2
commit
e2bcd9c2b4
10 changed files with 176 additions and 14 deletions
|
|
@ -9,6 +9,7 @@
|
|||
### Client
|
||||
- Enhance: ユーザーページのファイルタブでスクロール位置が保持されるように
|
||||
- Enhance: ドライブページでスクロール位置が保持されるように
|
||||
- Enhance: 絵文字のメニューから直接絵文字パレットに絵文字を追加できるように
|
||||
- Fix: URLプレビューのプレイヤーをウィンドウで開いたとき、プレイヤーが読み込まれるまでの間 `Invalid URL` と表示される問題を修正
|
||||
- Fix: 一部の実績が正しく表示されない問題を修正
|
||||
- Fix: アクセストークン発行時のダイアログのタイトルが「確認コード」となっているのを修正
|
||||
|
|
|
|||
|
|
@ -1415,6 +1415,11 @@ viewRenotedChannel: "リノート先のチャンネルを見る"
|
|||
previewingTheme: "テーマのプレビュー中"
|
||||
previewingThemeRestore: "元に戻す"
|
||||
accessToken: "アクセストークン"
|
||||
chooseEmojiPalette: "絵文字パレットを選択"
|
||||
addToEmojiPalette: "絵文字パレットに追加"
|
||||
emojiPaletteAlreadyAddedConfirm: "この絵文字はすでにこの絵文字パレットに含まれています。追加しなおしますか?"
|
||||
append: "末尾に追加"
|
||||
prepend: "先頭に追加"
|
||||
|
||||
_imageEditing:
|
||||
_vars:
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import { prefer } from '@/preferences.js';
|
|||
import { DI } from '@/di.js';
|
||||
import { noteEvents } from '@/composables/use-note-capture.js';
|
||||
import { mute as muteEmoji, unmute as unmuteEmoji, checkMuted as isEmojiMuted } from '@/utility/emoji-mute.js';
|
||||
import { addToEmojiPalette } from '@/utility/emoji-palette.js';
|
||||
import { haptic } from '@/utility/haptic.js';
|
||||
|
||||
const props = defineProps<{
|
||||
|
|
@ -206,6 +207,16 @@ async function menu(ev: PointerEvent) {
|
|||
});
|
||||
}
|
||||
|
||||
if (canToggle.value) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.addToEmojiPalette,
|
||||
icon: 'ti ti-palette',
|
||||
action: () => {
|
||||
addToEmojiPalette(isLocalCustomEmoji ? `:${emojiName.value}:` : props.reaction);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ export type ItemOption<T extends OptionValue = OptionValue> = {
|
|||
type?: 'option';
|
||||
value: T;
|
||||
label: string;
|
||||
caption?: string;
|
||||
};
|
||||
|
||||
export type ItemGroup<T extends OptionValue = OptionValue> = {
|
||||
|
|
@ -177,6 +178,7 @@ function show() {
|
|||
for (const option of item.items) {
|
||||
menu.push({
|
||||
text: option.label,
|
||||
caption: option.caption,
|
||||
active: computed(() => model.value === option.value),
|
||||
action: () => {
|
||||
model.value = option.value as ModelTChecked;
|
||||
|
|
@ -186,6 +188,7 @@ function show() {
|
|||
} else {
|
||||
menu.push({
|
||||
text: item.label,
|
||||
caption: item.caption,
|
||||
active: computed(() => model.value === item.value),
|
||||
action: () => {
|
||||
model.value = item.value as ModelTChecked;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import { $i } from '@/i.js';
|
|||
import { prefer } from '@/preferences.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { makeEmojiMuteKey, mute as muteEmoji, unmute as unmuteEmoji, checkMuted as checkEmojiMuted } from '@/utility/emoji-mute';
|
||||
import { addToEmojiPalette } from '@/utility/emoji-palette.js';
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
|
|
@ -167,8 +168,20 @@ function onClick(ev: PointerEvent) {
|
|||
});
|
||||
}
|
||||
|
||||
if (isLocal.value) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.addToEmojiPalette,
|
||||
icon: 'ti ti-palette',
|
||||
action: () => {
|
||||
addToEmojiPalette(`:${props.name}:`);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (($i?.isModerator ?? $i?.isAdmin) && isLocal.value) {
|
||||
menuItems.push({
|
||||
type: 'divider',
|
||||
}, {
|
||||
text: i18n.ts.edit,
|
||||
icon: 'ti ti-pencil',
|
||||
action: async () => {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import { i18n } from '@/i18n.js';
|
|||
import { prefer } from '@/preferences.js';
|
||||
import { DI } from '@/di.js';
|
||||
import { mute as muteEmoji, unmute as unmuteEmoji, checkMuted as checkMutedEmoji } from '@/utility/emoji-mute.js';
|
||||
import { addToEmojiPalette } from '@/utility/emoji-palette.js';
|
||||
|
||||
const props = defineProps<{
|
||||
emoji: string;
|
||||
|
|
@ -94,17 +95,31 @@ function onClick(ev: PointerEvent) {
|
|||
|
||||
menuItems.push({
|
||||
type: 'divider',
|
||||
}, isMuted.value ? {
|
||||
text: i18n.ts.emojiUnmute,
|
||||
icon: 'ti ti-mood-smile',
|
||||
});
|
||||
|
||||
if (isMuted.value) {
|
||||
menuItems.push({
|
||||
text: i18n.ts.emojiUnmute,
|
||||
icon: 'ti ti-mood-smile',
|
||||
action: () => {
|
||||
unmute();
|
||||
},
|
||||
});
|
||||
} else {
|
||||
menuItems.push({
|
||||
text: i18n.ts.emojiMute,
|
||||
icon: 'ti ti-mood-off',
|
||||
action: () => {
|
||||
mute();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
menuItems.push({
|
||||
text: i18n.ts.addToEmojiPalette,
|
||||
icon: 'ti ti-palette',
|
||||
action: () => {
|
||||
unmute();
|
||||
},
|
||||
} : {
|
||||
text: i18n.ts.emojiMute,
|
||||
icon: 'ti ti-mood-off',
|
||||
action: () => {
|
||||
mute();
|
||||
addToEmojiPalette(props.emoji);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
94
packages/frontend/src/utility/emoji-palette.ts
Normal file
94
packages/frontend/src/utility/emoji-palette.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { prefer } from '@/preferences.js';
|
||||
import * as os from '@/os.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import type { MkSelectItem } from '@/components/MkSelect.vue';
|
||||
|
||||
export function chooseEmojiPalette() {
|
||||
return os.select({
|
||||
title: i18n.ts.chooseEmojiPalette,
|
||||
default: prefer.s.emojiPaletteForMain ?? prefer.s.emojiPaletteForReaction ?? prefer.s.emojiPalettes[0]?.id,
|
||||
items: prefer.s.emojiPalettes.map<MkSelectItem<string>>((palette) => {
|
||||
let caption: string | undefined = undefined;
|
||||
|
||||
if (prefer.s.emojiPaletteForMain === palette.id) {
|
||||
caption = i18n.ts._emojiPalette.paletteForMain;
|
||||
} else if (prefer.s.emojiPaletteForReaction === palette.id) {
|
||||
caption = i18n.ts._emojiPalette.paletteForReaction;
|
||||
}
|
||||
|
||||
return {
|
||||
label: palette.name || `(${i18n.ts.noName})`,
|
||||
caption,
|
||||
value: palette.id,
|
||||
};
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export async function addToEmojiPalette(emoji: string) {
|
||||
const res = await chooseEmojiPalette();
|
||||
|
||||
if (res.canceled || res.result == null) return;
|
||||
|
||||
const palette = prefer.s.emojiPalettes.find((p) => p.id === res.result);
|
||||
if (!palette) return;
|
||||
let emojis = [...palette.emojis];
|
||||
|
||||
if (!emojis.includes(emoji)) {
|
||||
emojis.push(emoji);
|
||||
prefer.commit('emojiPalettes', prefer.s.emojiPalettes.map((p) => {
|
||||
if (p.id === palette.id) {
|
||||
return {
|
||||
...p,
|
||||
emojis,
|
||||
};
|
||||
} else {
|
||||
return p;
|
||||
}
|
||||
}));
|
||||
os.success();
|
||||
} else {
|
||||
const res = await os.actions({
|
||||
type: 'warning',
|
||||
text: i18n.ts.emojiPaletteAlreadyAddedConfirm,
|
||||
actions: [{
|
||||
value: 'prepend',
|
||||
text: i18n.ts.prepend,
|
||||
}, {
|
||||
value: 'append',
|
||||
text: i18n.ts.append,
|
||||
}, {
|
||||
value: 'doNothing',
|
||||
text: i18n.ts.doNothing,
|
||||
}],
|
||||
});
|
||||
|
||||
if (res.canceled || res.result === 'doNothing') return;
|
||||
|
||||
emojis = emojis.filter((e) => e !== emoji);
|
||||
|
||||
if (res.result === 'append') {
|
||||
emojis.push(emoji);
|
||||
} else if (res.result === 'prepend') {
|
||||
emojis.unshift(emoji);
|
||||
}
|
||||
|
||||
prefer.commit('emojiPalettes', prefer.s.emojiPalettes.map((p) => {
|
||||
if (p.id === palette.id) {
|
||||
return {
|
||||
...p,
|
||||
emojis,
|
||||
};
|
||||
} else {
|
||||
return p;
|
||||
}
|
||||
}));
|
||||
|
||||
os.success();
|
||||
}
|
||||
}
|
||||
|
|
@ -22,8 +22,8 @@ class EmojiPicker {
|
|||
}
|
||||
|
||||
public init() {
|
||||
watch([prefer.r.emojiPaletteForMain, prefer.r.emojiPalettes], () => {
|
||||
this.emojisRef.value = prefer.s.emojiPaletteForMain == null ? prefer.s.emojiPalettes[0].emojis : prefer.s.emojiPalettes.find(palette => palette.id === prefer.s.emojiPaletteForMain)?.emojis ?? [];
|
||||
watch([prefer.r.emojiPaletteForMain, prefer.r.emojiPalettes], ([newId, newPalettes]) => {
|
||||
this.emojisRef.value = newId == null ? newPalettes[0].emojis : newPalettes.find(palette => palette.id === newId)?.emojis ?? [];
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ class ReactionPicker {
|
|||
}
|
||||
|
||||
public init() {
|
||||
watch([prefer.r.emojiPaletteForReaction, prefer.r.emojiPalettes], () => {
|
||||
this.reactionsRef.value = prefer.s.emojiPaletteForReaction == null ? prefer.s.emojiPalettes[0].emojis : prefer.s.emojiPalettes.find(palette => palette.id === prefer.s.emojiPaletteForReaction)?.emojis ?? [];
|
||||
watch([prefer.r.emojiPaletteForReaction, prefer.r.emojiPalettes], ([newId, newPalettes]) => {
|
||||
this.reactionsRef.value = newId == null ? newPalettes[0].emojis : newPalettes.find(palette => palette.id === newId)?.emojis ?? [];
|
||||
}, {
|
||||
immediate: true,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5675,6 +5675,26 @@ export interface Locale extends ILocale {
|
|||
* アクセストークン
|
||||
*/
|
||||
"accessToken": string;
|
||||
/**
|
||||
* 絵文字パレットを選択
|
||||
*/
|
||||
"chooseEmojiPalette": string;
|
||||
/**
|
||||
* 絵文字パレットに追加
|
||||
*/
|
||||
"addToEmojiPalette": string;
|
||||
/**
|
||||
* この絵文字はすでにこの絵文字パレットに含まれています。追加しなおしますか?
|
||||
*/
|
||||
"emojiPaletteAlreadyAddedConfirm": string;
|
||||
/**
|
||||
* 末尾に追加
|
||||
*/
|
||||
"append": string;
|
||||
/**
|
||||
* 先頭に追加
|
||||
*/
|
||||
"prepend": string;
|
||||
"_imageEditing": {
|
||||
"_vars": {
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue