Feat(frontend): リアクション・ノート内絵文字・/about#emojisで絵文字詳細が見られるように (#12984)
* リアクション・ノート内絵文字・/about#emojisで絵文字詳細が見られるように * update CHANGELOG.md * fix locale & type errors * fix locale etc * fix * fix type * lint fixes * lint fixes(2)
This commit is contained in:
parent
d246f6c360
commit
19fe32bd7e
5 changed files with 146 additions and 14 deletions
|
@ -20,6 +20,7 @@
|
|||
|
||||
### Client
|
||||
- Feat: 新しいゲームを追加
|
||||
- Feat: 絵文字の詳細ダイアログを追加
|
||||
- Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように
|
||||
- Enhance: チャンネルノートのピン留めをノートのメニューからできるように
|
||||
- Enhance: 管理者の場合はAPI tokenの発行画面で管理機能に関する権限を付与できるように
|
||||
|
|
102
packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
Normal file
102
packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
Normal file
|
@ -0,0 +1,102 @@
|
|||
<!--
|
||||
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
|
||||
<template #header>:{{ emoji.name }}:</template>
|
||||
<template #default>
|
||||
<MkSpacer>
|
||||
<div style="display: flex; flex-direction: column; gap: 1em;">
|
||||
<div :class="$style.emojiImgWrapper">
|
||||
<MkCustomEmoji :name="emoji.name" :normal="true" style="height: 100%;"></MkCustomEmoji>
|
||||
</div>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.name }}</template>
|
||||
<template #value>{{ emoji.name }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.tags }}</template>
|
||||
<template #value>
|
||||
<div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
|
||||
<div v-else :class="$style.aliases">
|
||||
<span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
|
||||
{{ alias }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.category }}</template>
|
||||
<template #value>{{ emoji.category ?? i18n.ts.none }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.sensitive }}</template>
|
||||
<template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.localOnly }}</template>
|
||||
<template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue>
|
||||
<template #key>{{ i18n.ts.license }}</template>
|
||||
<template #value>{{ emoji.license ?? i18n.ts.none }}</template>
|
||||
</MkKeyValue>
|
||||
<MkKeyValue :copy="emoji.url">
|
||||
<template #key>{{ i18n.ts.emojiUrl }}</template>
|
||||
<template #value>
|
||||
<a :href="emoji.url" target="_blank">{{ emoji.url }}</a>
|
||||
</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</template>
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { defineProps, shallowRef } from 'vue';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
const props = defineProps<{
|
||||
emoji: Misskey.entities.EmojiDetailed,
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
dialogEl.value!.close();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.emojiImgWrapper {
|
||||
max-width: 100%;
|
||||
height: 40cqh;
|
||||
background-image: repeating-linear-gradient(45deg, transparent, transparent 8px, var(--X5) 8px, var(--X5) 14px);
|
||||
border-radius: var(--radius);
|
||||
margin: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.aliases {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
.alias {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
background-color: var(--X5);
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
</style>
|
|
@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
class="_button"
|
||||
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
|
||||
@click="toggleReaction()"
|
||||
@contextmenu.prevent.stop="menu"
|
||||
>
|
||||
<MkReactionIcon :class="defaultStore.state.limitWidthOfReaction ? $style.limitWidth : ''" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
|
||||
<span :class="$style.count">{{ count }}</span>
|
||||
|
@ -21,6 +22,7 @@ import { computed, inject, onMounted, shallowRef, watch } from 'vue';
|
|||
import * as Misskey from 'misskey-js';
|
||||
import XDetails from '@/components/MkReactionsViewer.details.vue';
|
||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||
import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
|
||||
import { useTooltip } from '@/scripts/use-tooltip.js';
|
||||
|
@ -98,6 +100,22 @@ async function toggleReaction() {
|
|||
}
|
||||
}
|
||||
|
||||
async function menu(ev) {
|
||||
if (!canToggle.value) return;
|
||||
if (!props.reaction.includes(":")) return;
|
||||
os.popupMenu([{
|
||||
text: i18n.ts.info,
|
||||
icon: 'ti ti-info-circle',
|
||||
action: async () => {
|
||||
os.popup(MkCustomEmojiDetailedDialog, {
|
||||
emoji: await misskeyApiGet('emoji', {
|
||||
name: props.reaction.replace(/:/g, '').replace(/@\./, ''),
|
||||
}),
|
||||
});
|
||||
},
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
function anime() {
|
||||
if (document.hidden) return;
|
||||
if (!defaultStore.state.animation) return;
|
||||
|
|
|
@ -24,9 +24,11 @@ import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js'
|
|||
import { defaultStore } from '@/store.js';
|
||||
import { customEmojisMap } from '@/custom-emojis.js';
|
||||
import * as os from '@/os.js';
|
||||
import { misskeyApiGet } from '@/scripts/misskey-api.js';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
name: string;
|
||||
|
@ -93,7 +95,19 @@ function onClick(ev: MouseEvent) {
|
|||
react(`:${props.name}:`);
|
||||
sound.playMisskeySfx('reaction');
|
||||
},
|
||||
}] : [])], ev.currentTarget ?? ev.target);
|
||||
}] : []), {
|
||||
text: i18n.ts.info,
|
||||
icon: 'ti ti-info-circle',
|
||||
action: async () => {
|
||||
os.popup(MkCustomEmojiDetailedDialog, {
|
||||
emoji: await misskeyApiGet('emoji', {
|
||||
name: customEmojiName.value,
|
||||
}),
|
||||
}, {
|
||||
anchor: ev.target,
|
||||
});
|
||||
},
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -14,19 +14,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as os from '@/os.js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { misskeyApiGet } from '@/scripts/misskey-api.js';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
emoji: {
|
||||
name: string;
|
||||
aliases: string[];
|
||||
category: string;
|
||||
url: string;
|
||||
};
|
||||
emoji: Misskey.entities.EmojiSimple;
|
||||
}>();
|
||||
|
||||
function menu(ev) {
|
||||
|
@ -43,12 +39,13 @@ function menu(ev) {
|
|||
}, {
|
||||
text: i18n.ts.info,
|
||||
icon: 'ti ti-info-circle',
|
||||
action: () => {
|
||||
misskeyApiGet('emoji', { name: props.emoji.name }).then(res => {
|
||||
os.alert({
|
||||
type: 'info',
|
||||
text: `Name: ${res.name}\nAliases: ${res.aliases.join(' ')}\nCategory: ${res.category}\nisSensitive: ${res.isSensitive}\nlocalOnly: ${res.localOnly}\nLicense: ${res.license}\nURL: ${res.url}`,
|
||||
});
|
||||
action: async () => {
|
||||
os.popup(MkCustomEmojiDetailedDialog, {
|
||||
emoji: await misskeyApiGet('emoji', {
|
||||
name: props.emoji.name,
|
||||
})
|
||||
}, {
|
||||
anchor: ev.target,
|
||||
});
|
||||
},
|
||||
}], ev.currentTarget ?? ev.target);
|
||||
|
|
Loading…
Reference in a new issue