Skip to content

Commit

Permalink
feat(frontend): リモート絵文字のインポート時に詳細を確認できるように (#15344)
Browse files Browse the repository at this point in the history
* feat(frontend): リモート絵文字のインポート時に詳細を確認できるように

* 追加対応

* MkInput -> MkKeyValue
  • Loading branch information
samunohito authored Jan 26, 2025
1 parent e94c697 commit f4bca47
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
(Based on https://github.com/Otaku-Social/maniakey/pull/14)
- Enhance: AiScriptの拡張API関数において引数の型チェックをより厳格に
- Enhance: クエリパラメータでuiを一時的に変更できるように #15240
- Enhance: リモート絵文字のインポート時に詳細を確認できるように #15336
- Fix: 画面サイズが変わった際にナビゲーションバーが自動で折りたたまれない問題を修正
- Fix: サーバー情報メニューに区切り線が不足していたのを修正
- Fix: ノートがログインしているユーザーしか見れない場合にログインダイアログを閉じるとその後の動線がなくなる問題を修正
Expand Down
4 changes: 4 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10635,6 +10635,10 @@ export interface Locale extends ILocale {
"logNothing": string;
};
"_remote": {
/**
* 選択行の詳細
*/
"selectionRowDetail": string;
/**
* 選択行をインポート
*/
Expand Down
1 change: 1 addition & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2837,6 +2837,7 @@ _customEmojisManager:
failureLogNothing: "失敗ログはありません。"
logNothing: "ログはありません。"
_remote:
selectionRowDetail: "選択行の詳細"
importSelectionRows: "選択行をインポート"
importSelectionRangesRows: "選択範囲の行をインポート"
importEmojisButton: "チェックされた絵文字をインポート"
Expand Down
132 changes: 132 additions & 0 deletions packages/frontend/src/components/MkRemoteEmojiEditDialog.vue
Original file line number Diff line number Diff line change
@@ -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>
48 changes: 46 additions & 2 deletions packages/frontend/src/pages/admin/custom-emojis-manager.remote.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,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"
Expand Down Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -178,12 +189,37 @@ 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' },
],
cells: {
contextMenuFactory: (col, row, value, context) => {
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,
Expand All @@ -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);
Expand All @@ -229,6 +266,7 @@ async function onSearchRequest() {
function onQueryResetButtonClicked() {
queryName.value = null;
queryHost.value = null;
queryLicense.value = null;
queryUri.value = null;
queryPublicUrl.value = null;
}
Expand Down Expand Up @@ -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',
Expand All @@ -330,6 +369,7 @@ async function refreshCustomEmojis() {
id: it.id,
url: it.publicUrl,
name: it.name,
license: it.license,
host: it.host!,
}));
}
Expand All @@ -356,6 +396,10 @@ onMounted(async () => {
grid-column: 2 / 3;
}

.col3 {
grid-column: 3 / 4;
}

.root {
padding: 16px;
}
Expand All @@ -366,7 +410,7 @@ onMounted(async () => {

.searchArea {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
}

Expand Down
18 changes: 18 additions & 0 deletions packages/frontend/src/pages/custom-emojis-manager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand All @@ -169,6 +183,10 @@ const remoteMenu = (emoji, ev: MouseEvent) => {
os.popupMenu([{
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',
Expand Down

1 comment on commit f4bca47

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chromatic detects changes. Please review the changes on Chromatic.

Please sign in to comment.