summaryrefslogtreecommitdiff
path: root/packages/frontend/src/pages/admin
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2023-10-21 18:38:07 +0900
committerGitHub <noreply@github.com>2023-10-21 18:38:07 +0900
commit2c0a139da60ddf33e82353acbb985230c71c78da (patch)
treeb12d2762b332b7c885c5b210954053869618b62a /packages/frontend/src/pages/admin
parentUpdate CHANGELOG.md (diff)
downloadmisskey-2c0a139da60ddf33e82353acbb985230c71c78da.tar.gz
misskey-2c0a139da60ddf33e82353acbb985230c71c78da.tar.bz2
misskey-2c0a139da60ddf33e82353acbb985230c71c78da.zip
feat: Avatar decoration (#12096)
* wip * Update ja-JP.yml * Update profile.vue * .js * Update home.test.ts
Diffstat (limited to 'packages/frontend/src/pages/admin')
-rw-r--r--packages/frontend/src/pages/admin/avatar-decorations.vue103
-rw-r--r--packages/frontend/src/pages/admin/index.vue5
-rw-r--r--packages/frontend/src/pages/admin/modlog.ModLog.vue12
3 files changed, 118 insertions, 2 deletions
diff --git a/packages/frontend/src/pages/admin/avatar-decorations.vue b/packages/frontend/src/pages/admin/avatar-decorations.vue
new file mode 100644
index 0000000000..b4007e6d20
--- /dev/null
+++ b/packages/frontend/src/pages/admin/avatar-decorations.vue
@@ -0,0 +1,103 @@
+<!--
+SPDX-FileCopyrightText: syuilo and other misskey contributors
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+ <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
+ <MkSpacer :contentMax="900">
+ <div class="_gaps">
+ <MkFolder v-for="avatarDecoration in avatarDecorations" :key="avatarDecoration.id ?? avatarDecoration._id" :defaultOpen="avatarDecoration.id == null">
+ <template #label>{{ avatarDecoration.name }}</template>
+ <template #caption>{{ avatarDecoration.description }}</template>
+
+ <div class="_gaps_m">
+ <MkInput v-model="avatarDecoration.name">
+ <template #label>{{ i18n.ts.name }}</template>
+ </MkInput>
+ <MkTextarea v-model="avatarDecoration.description">
+ <template #label>{{ i18n.ts.description }}</template>
+ </MkTextarea>
+ <MkInput v-model="avatarDecoration.url">
+ <template #label>{{ i18n.ts.imageUrl }}</template>
+ </MkInput>
+ <div class="buttons _buttons">
+ <MkButton class="button" inline primary @click="save(avatarDecoration)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
+ <MkButton v-if="avatarDecoration.id != null" class="button" inline danger @click="del(avatarDecoration)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+ </div>
+ </div>
+ </MkFolder>
+ </div>
+ </MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { } from 'vue';
+import XHeader from './_header_.vue';
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
+import MkSwitch from '@/components/MkSwitch.vue';
+import MkRadios from '@/components/MkRadios.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import MkFolder from '@/components/MkFolder.vue';
+
+let avatarDecorations: any[] = $ref([]);
+
+function add() {
+ avatarDecorations.unshift({
+ _id: Math.random().toString(36),
+ id: null,
+ name: '',
+ description: '',
+ url: '',
+ });
+}
+
+function del(avatarDecoration) {
+ os.confirm({
+ type: 'warning',
+ text: i18n.t('deleteAreYouSure', { x: avatarDecoration.name }),
+ }).then(({ canceled }) => {
+ if (canceled) return;
+ avatarDecorations = avatarDecorations.filter(x => x !== avatarDecoration);
+ os.api('admin/avatar-decorations/delete', avatarDecoration);
+ });
+}
+
+async function save(avatarDecoration) {
+ if (avatarDecoration.id == null) {
+ await os.apiWithDialog('admin/avatar-decorations/create', avatarDecoration);
+ load();
+ } else {
+ os.apiWithDialog('admin/avatar-decorations/update', avatarDecoration);
+ }
+}
+
+function load() {
+ os.api('admin/avatar-decorations/list').then(_avatarDecorations => {
+ avatarDecorations = _avatarDecorations;
+ });
+}
+
+load();
+
+const headerActions = $computed(() => [{
+ asFullButton: true,
+ icon: 'ti ti-plus',
+ text: i18n.ts.add,
+ handler: add,
+}]);
+
+const headerTabs = $computed(() => []);
+
+definePageMetadata({
+ title: i18n.ts.avatarDecorations,
+ icon: 'ti ti-sparkles',
+});
+</script>
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index a508c20cf3..b304edbf57 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -116,6 +116,11 @@ const menuDef = $computed(() => [{
to: '/admin/emojis',
active: currentPage?.route.name === 'emojis',
}, {
+ icon: 'ti ti-sparkles',
+ text: i18n.ts.avatarDecorations,
+ to: '/admin/avatar-decorations',
+ active: currentPage?.route.name === 'avatarDecorations',
+ }, {
icon: 'ti ti-whirl',
text: i18n.ts.federation,
to: '/admin/federation',
diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue
index 0af226f02e..bceefcf6c8 100644
--- a/packages/frontend/src/pages/admin/modlog.ModLog.vue
+++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue
@@ -8,9 +8,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #label>
<b
:class="{
- [$style.logGreen]: ['createRole', 'addCustomEmoji', 'createGlobalAnnouncement', 'createUserAnnouncement', 'createAd', 'createInvitation'].includes(log.type),
+ [$style.logGreen]: ['createRole', 'addCustomEmoji', 'createGlobalAnnouncement', 'createUserAnnouncement', 'createAd', 'createInvitation', 'createAvatarDecoration'].includes(log.type),
[$style.logYellow]: ['markSensitiveDriveFile', 'resetPassword'].includes(log.type),
- [$style.logRed]: ['suspend', 'deleteRole', 'suspendRemoteInstance', 'deleteGlobalAnnouncement', 'deleteUserAnnouncement', 'deleteCustomEmoji', 'deleteNote', 'deleteDriveFile', 'deleteAd'].includes(log.type)
+ [$style.logRed]: ['suspend', 'deleteRole', 'suspendRemoteInstance', 'deleteGlobalAnnouncement', 'deleteUserAnnouncement', 'deleteCustomEmoji', 'deleteNote', 'deleteDriveFile', 'deleteAd', 'deleteAvatarDecoration'].includes(log.type)
}"
>{{ i18n.ts._moderationLogTypes[log.type] }}</b>
<span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
@@ -37,6 +37,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<span v-else-if="log.type === 'deleteUserAnnouncement'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span>
<span v-else-if="log.type === 'deleteNote'">: @{{ log.info.noteUserUsername }}{{ log.info.noteUserHost ? '@' + log.info.noteUserHost : '' }}</span>
<span v-else-if="log.type === 'deleteDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span>
+ <span v-else-if="log.type === 'createAvatarDecoration'">: {{ log.info.avatarDecoration.name }}</span>
+ <span v-else-if="log.type === 'updateAvatarDecoration'">: {{ log.info.before.name }}</span>
+ <span v-else-if="log.type === 'deleteAvatarDecoration'">: {{ log.info.avatarDecoration.name }}</span>
</template>
<template #icon>
<MkAvatar :user="log.user" :class="$style.avatar"/>
@@ -102,6 +105,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
</div>
</template>
+ <template v-else-if="log.type === 'updateAvatarDecoration'">
+ <div :class="$style.diff">
+ <CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/>
+ </div>
+ </template>
<details>
<summary>raw</summary>