summaryrefslogtreecommitdiff
path: root/packages/frontend
diff options
context:
space:
mode:
authorおさむのひと <46447427+samunohito@users.noreply.github.com>2025-01-26 14:59:03 +0900
committerGitHub <noreply@github.com>2025-01-26 05:59:03 +0000
commitf4bca4708eba50cdef4127c74a37678ac25747c8 (patch)
tree44c03c14483fb3e964c282781466ab7de1ba82b1 /packages/frontend
parentNew Crowdin updates (#15347) (diff)
downloadsharkey-f4bca4708eba50cdef4127c74a37678ac25747c8.tar.gz
sharkey-f4bca4708eba50cdef4127c74a37678ac25747c8.tar.bz2
sharkey-f4bca4708eba50cdef4127c74a37678ac25747c8.zip
feat(frontend): リモート絵文字のインポート時に詳細を確認できるように (#15344)
* feat(frontend): リモート絵文字のインポート時に詳細を確認できるように * 追加対応 * MkInput -> MkKeyValue
Diffstat (limited to 'packages/frontend')
-rw-r--r--packages/frontend/src/components/MkRemoteEmojiEditDialog.vue132
-rw-r--r--packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue48
-rw-r--r--packages/frontend/src/pages/custom-emojis-manager.vue18
3 files changed, 196 insertions, 2 deletions
diff --git a/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue b/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue
new file mode 100644
index 0000000000..873b276b3d
--- /dev/null
+++ b/packages/frontend/src/components/MkRemoteEmojiEditDialog.vue
@@ -0,0 +1,132 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkWindow
+ ref="windowEl"
+ :initialWidth="400"
+ :initialHeight="500"
+ :canResize="true"
+ @close="windowEl?.close()"
+ @closed="emit('closed')"
+>
+ <template #header>:{{ name }}:</template>
+
+ <div style="display: flex; flex-direction: column; min-height: 100%;">
+ <MkSpacer :marginMin="20" :marginMax="28" style="flex-grow: 1;">
+ <div class="_gaps_m">
+ <div v-if="imgUrl != null" :class="$style.imgs">
+ <div style="background: #000;" :class="$style.imgContainer">
+ <img :src="imgUrl" :class="$style.img" :alt="name"/>
+ </div>
+ <div style="background: #222;" :class="$style.imgContainer">
+ <img :src="imgUrl" :class="$style.img" :alt="name"/>
+ </div>
+ <div style="background: #ddd;" :class="$style.imgContainer">
+ <img :src="imgUrl" :class="$style.img" :alt="name"/>
+ </div>
+ <div style="background: #fff;" :class="$style.imgContainer">
+ <img :src="imgUrl" :class="$style.img" :alt="name"/>
+ </div>
+ </div>
+
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.id }}</template>
+ <template #value>{{ name }}</template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.host }}</template>
+ <template #value>{{ host }}</template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.license }}</template>
+ <template #value>{{ license }}</template>
+ </MkKeyValue>
+ </div>
+ </MkSpacer>
+ <div :class="$style.footer">
+ <MkButton primary rounded style="margin: 0 auto;" @click="done">
+ <i class="ti ti-plus"></i> {{ i18n.ts.import }}
+ </MkButton>
+ </div>
+ </div>
+</MkWindow>
+</template>
+
+<script lang="ts" setup>
+import { computed, ref } from 'vue';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
+import MkWindow from '@/components/MkWindow.vue';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+
+const props = defineProps<{
+ emoji: {
+ id: string,
+ name: string,
+ host: string,
+ license: string | null,
+ url: string
+ },
+}>();
+
+const emit = defineEmits<{
+ // 必要なら戻り値を増やす
+ (ev: 'done'): void,
+ (ev: 'closed'): void
+}>();
+
+const windowEl = ref<InstanceType<typeof MkWindow> | null>(null);
+
+const name = computed(() => props.emoji.name);
+const host = computed(() => props.emoji.host);
+const license = computed(() => props.emoji.license);
+const imgUrl = computed(() => props.emoji.url);
+
+async function done() {
+ await os.apiWithDialog('admin/emoji/copy', {
+ emojiId: props.emoji.id,
+ });
+
+ emit('done');
+ windowEl.value?.close();
+}
+</script>
+
+<style lang="scss" module>
+.imgs {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ justify-content: center;
+}
+
+.imgContainer {
+ padding: 8px;
+ border-radius: 6px;
+}
+
+.img {
+ display: block;
+ height: 64px;
+ width: 64px;
+ object-fit: contain;
+}
+
+.footer {
+ position: sticky;
+ z-index: 10000;
+ bottom: 0;
+ left: 0;
+ padding: 12px;
+ border-top: solid 0.5px var(--MI_THEME-divider);
+ background: var(--MI_THEME-acrylicBg);
+ -webkit-backdrop-filter: var(--MI-blur, blur(15px));
+ backdrop-filter: var(--MI-blur, blur(15px));
+}
+</style>
diff --git a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
index 9a9d2990ba..14a3b71e53 100644
--- a/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
+++ b/packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
@@ -35,6 +35,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>host</template>
</MkInput>
<MkInput
+ v-model="queryLicense"
+ type="search"
+ autocapitalize="off"
+ :class="[$style.col3, $style.row1]"
+ @enter="onSearchRequest"
+ >
+ <template #label>license</template>
+ </MkInput>
+
+ <MkInput
v-model="queryUri"
type="search"
autocapitalize="off"
@@ -115,6 +125,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { computed, onMounted, ref, useCssModule } from 'vue';
import * as Misskey from 'misskey-js';
+import MkRemoteEmojiEditDialog from '@/components/MkRemoteEmojiEditDialog.vue';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
@@ -135,7 +146,7 @@ import { deviceKind } from '@/scripts/device-kind.js';
import MkPagingButtons from '@/components/MkPagingButtons.vue';
import MkSortOrderEditor from '@/components/MkSortOrderEditor.vue';
import { SortOrder } from '@/components/MkSortOrderEditor.define.js';
-import { useLoading } from "@/components/hook/useLoading.js";
+import { useLoading } from '@/components/hook/useLoading.js';
type GridItem = {
checked: boolean;
@@ -178,6 +189,7 @@ function setupGrid(): GridSetting {
{ bindTo: 'url', icon: 'ti-icons', type: 'image', editable: false, width: 'auto' },
{ bindTo: 'name', title: 'name', type: 'text', editable: false, width: 'auto' },
{ bindTo: 'host', title: 'host', type: 'text', editable: false, width: 'auto' },
+ { bindTo: 'license', title: 'license', type: 'text', editable: false, width: 200 },
{ bindTo: 'uri', title: 'uri', type: 'text', editable: false, width: 'auto' },
{ bindTo: 'publicUrl', title: 'publicUrl', type: 'text', editable: false, width: 'auto' },
],
@@ -186,6 +198,30 @@ function setupGrid(): GridSetting {
return [
{
type: 'button',
+ text: i18n.ts._customEmojisManager._remote.selectionRowDetail,
+ icon: 'ti ti-info-circle',
+ action: async () => {
+ const target = customEmojis.value[row.index];
+ const { dispose } = os.popup(MkRemoteEmojiEditDialog, {
+ emoji: {
+ id: target.id,
+ name: target.name,
+ host: target.host!,
+ license: target.license,
+ url: target.publicUrl,
+ },
+ }, {
+ done: () => {
+ dispose();
+ },
+ closed: () => {
+ dispose();
+ },
+ });
+ },
+ },
+ {
+ type: 'button',
text: i18n.ts._customEmojisManager._remote.importSelectionRangesRows,
icon: 'ti ti-download',
action: async () => {
@@ -207,6 +243,7 @@ const currentPage = ref<number>(0);
const queryName = ref<string | null>(null);
const queryHost = ref<string | null>(null);
+const queryLicense = ref<string | null>(null);
const queryUri = ref<string | null>(null);
const queryPublicUrl = ref<string | null>(null);
const previousQuery = ref<string | undefined>(undefined);
@@ -229,6 +266,7 @@ async function onSearchRequest() {
function onQueryResetButtonClicked() {
queryName.value = null;
queryHost.value = null;
+ queryLicense.value = null;
queryUri.value = null;
queryPublicUrl.value = null;
}
@@ -306,6 +344,7 @@ async function refreshCustomEmojis() {
const query: Misskey.entities.V2AdminEmojiListRequest['query'] = {
name: emptyStrToUndefined(queryName.value),
host: emptyStrToUndefined(queryHost.value),
+ license: emptyStrToUndefined(queryLicense.value),
uri: emptyStrToUndefined(queryUri.value),
publicUrl: emptyStrToUndefined(queryPublicUrl.value),
hostType: 'remote',
@@ -330,6 +369,7 @@ async function refreshCustomEmojis() {
id: it.id,
url: it.publicUrl,
name: it.name,
+ license: it.license,
host: it.host!,
}));
}
@@ -356,6 +396,10 @@ onMounted(async () => {
grid-column: 2 / 3;
}
+.col3 {
+ grid-column: 3 / 4;
+}
+
.root {
padding: 16px;
}
@@ -366,7 +410,7 @@ onMounted(async () => {
.searchArea {
display: grid;
- grid-template-columns: 1fr 1fr;
+ grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
}
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 789a63eb90..82c6d8df4e 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -78,6 +78,7 @@ import { computed, defineAsyncComponent, ref, shallowRef } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import MkPagination from '@/components/MkPagination.vue';
+import MkRemoteEmojiEditDialog from '@/components/MkRemoteEmojiEditDialog.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import FormSplit from '@/components/form/split.vue';
import { selectFile } from '@/scripts/select-file.js';
@@ -159,6 +160,19 @@ const edit = (emoji) => {
});
};
+const detailRemoteEmoji = (emoji) => {
+ const { dispose } = os.popup(MkRemoteEmojiEditDialog, {
+ emoji: emoji,
+ }, {
+ done: () => {
+ dispose();
+ },
+ closed: () => {
+ dispose();
+ },
+ });
+};
+
const importEmoji = (emoji) => {
os.apiWithDialog('admin/emoji/copy', {
emojiId: emoji.id,
@@ -170,6 +184,10 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
type: 'label',
text: ':' + emoji.name + ':',
}, {
+ text: i18n.ts.details,
+ icon: 'ti ti-info-circle',
+ action: () => { detailRemoteEmoji(emoji); },
+ }, {
text: i18n.ts.import,
icon: 'ti ti-plus',
action: () => { importEmoji(emoji); },