summaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
Diffstat (limited to 'packages')
-rw-r--r--packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts57
-rw-r--r--packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts3
-rw-r--r--packages/frontend/src/pages/avatar-decoration-edit-dialog.vue220
-rw-r--r--packages/frontend/src/pages/avatar-decorations.vue172
-rw-r--r--packages/frontend/src/pages/emoji-edit-dialog.vue2
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.decoration.vue7
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.dialog.vue4
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md4
-rw-r--r--packages/misskey-js/src/autogen/endpoint.ts3
-rw-r--r--packages/misskey-js/src/autogen/entities.ts1
-rw-r--r--packages/misskey-js/src/autogen/types.ts19
11 files changed, 369 insertions, 123 deletions
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
index fd21309818..87d80cbe80 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts
@@ -6,6 +6,7 @@
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
+import { IdService } from '@/core/IdService.js';
export const meta = {
tags: ['admin'],
@@ -13,6 +14,49 @@ export const meta = {
requireCredential: true,
requireRolePolicy: 'canManageAvatarDecorations',
kind: 'write:admin:avatar-decorations',
+
+ res: {
+ type: 'object',
+ optional: false, nullable: false,
+ properties: {
+ id: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ createdAt: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'date-time',
+ },
+ updatedAt: {
+ type: 'string',
+ optional: false, nullable: true,
+ format: 'date-time',
+ },
+ name: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ description: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ url: {
+ type: 'string',
+ optional: false, nullable: false,
+ },
+ roleIdsThatCanBeUsedThisDecoration: {
+ type: 'array',
+ optional: false, nullable: false,
+ items: {
+ type: 'string',
+ optional: false, nullable: false,
+ format: 'id',
+ },
+ },
+ },
+ },
} as const;
export const paramDef = {
@@ -32,14 +76,25 @@ export const paramDef = {
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private avatarDecorationService: AvatarDecorationService,
+ private idService: IdService,
) {
super(meta, paramDef, async (ps, me) => {
- await this.avatarDecorationService.create({
+ const created = await this.avatarDecorationService.create({
name: ps.name,
description: ps.description,
url: ps.url,
roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration,
}, me);
+
+ return {
+ id: created.id,
+ createdAt: this.idService.parse(created.id).date.toISOString(),
+ updatedAt: null,
+ name: created.name,
+ description: created.description,
+ url: created.url,
+ roleIdsThatCanBeUsedThisDecoration: created.roleIdsThatCanBeUsedThisDecoration,
+ };
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
index aee90023e1..d785f085ac 100644
--- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts
@@ -4,10 +4,7 @@
*/
import { Inject, Injectable } from '@nestjs/common';
-import type { AnnouncementsRepository, AnnouncementReadsRepository } from '@/models/_.js';
-import type { MiAnnouncement } from '@/models/Announcement.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
-import { QueryService } from '@/core/QueryService.js';
import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js';
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
diff --git a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
new file mode 100644
index 0000000000..a834f1c5fd
--- /dev/null
+++ b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
@@ -0,0 +1,220 @@
+<!--
+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 v-if="avatarDecoration" #header>{{ avatarDecoration.name }}</template>
+ <template v-else #header>New decoration</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 :class="$style.preview">
+ <div :class="[$style.previewItem, $style.light]">
+ <MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="url != '' ? [{ url }] : []" forceShowDecoration/>
+ </div>
+ <div :class="[$style.previewItem, $style.dark]">
+ <MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="url != '' ? [{ url }] : []" forceShowDecoration/>
+ </div>
+ </div>
+ <MkInput v-model="name">
+ <template #label>{{ i18n.ts.name }}</template>
+ </MkInput>
+ <MkInput v-model="url">
+ <template #label>{{ i18n.ts.imageUrl }}</template>
+ </MkInput>
+ <MkTextarea v-model="description">
+ <template #label>{{ i18n.ts.description }}</template>
+ </MkTextarea>
+ <MkFolder>
+ <template #label>{{ i18n.ts.availableRoles }}</template>
+ <template #suffix>{{ rolesThatCanBeUsedThisDecoration.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisDecoration.length }}</template>
+
+ <div class="_gaps">
+ <MkButton rounded @click="addRole"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
+
+ <div v-for="role in rolesThatCanBeUsedThisDecoration" :key="role.id" :class="$style.roleItem">
+ <MkRolePreview :class="$style.role" :role="role" :forModeration="true" :detailed="false" style="pointer-events: none;"/>
+ <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="removeRole(role, $event)"><i class="ti ti-x"></i></button>
+ <button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button>
+ </div>
+ </div>
+ </MkFolder>
+ <MkButton v-if="avatarDecoration" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
+ </div>
+ </MkSpacer>
+ <div :class="$style.footer">
+ <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.avatarDecoration ? i18n.ts.update : i18n.ts.create }}</MkButton>
+ </div>
+ </div>
+</MkWindow>
+</template>
+
+<script lang="ts" setup>
+import { computed, watch, ref } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkWindow from '@/components/MkWindow.vue';
+import MkButton from '@/components/MkButton.vue';
+import MkInput from '@/components/MkInput.vue';
+import MkInfo from '@/components/MkInfo.vue';
+import MkFolder from '@/components/MkFolder.vue';
+import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import { i18n } from '@/i18n.js';
+import MkSwitch from '@/components/MkSwitch.vue';
+import MkRolePreview from '@/components/MkRolePreview.vue';
+import MkTextarea from '@/components/MkTextarea.vue';
+import { signinRequired } from '@/account.js';
+
+const $i = signinRequired();
+
+const props = defineProps<{
+ avatarDecoration?: any,
+}>();
+
+const emit = defineEmits<{
+ (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void,
+ (ev: 'closed'): void
+}>();
+
+const windowEl = ref<InstanceType<typeof MkWindow> | null>(null);
+const url = ref<string>(props.avatarDecoration ? props.avatarDecoration.url : '');
+const name = ref<string>(props.avatarDecoration ? props.avatarDecoration.name : '');
+const description = ref<string>(props.avatarDecoration ? props.avatarDecoration.description : '');
+const roleIdsThatCanBeUsedThisDecoration = ref(props.avatarDecoration ? props.avatarDecoration.roleIdsThatCanBeUsedThisDecoration : []);
+const rolesThatCanBeUsedThisDecoration = ref<Misskey.entities.Role[]>([]);
+
+watch(roleIdsThatCanBeUsedThisDecoration, async () => {
+ rolesThatCanBeUsedThisDecoration.value = (await Promise.all(roleIdsThatCanBeUsedThisDecoration.value.map((id) => misskeyApi('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
+}, { immediate: true });
+
+async function addRole() {
+ const roles = await misskeyApi('admin/roles/list');
+ const currentRoleIds = rolesThatCanBeUsedThisDecoration.value.map(x => x.id);
+
+ const { canceled, result: role } = await os.select({
+ items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })),
+ });
+ if (canceled || role == null) return;
+
+ rolesThatCanBeUsedThisDecoration.value.push(role);
+}
+
+async function removeRole(role, ev) {
+ rolesThatCanBeUsedThisDecoration.value = rolesThatCanBeUsedThisDecoration.value.filter(x => x.id !== role.id);
+}
+
+async function done() {
+ const params = {
+ url: url.value,
+ name: name.value,
+ description: description.value,
+ roleIdsThatCanBeUsedThisDecoration: rolesThatCanBeUsedThisDecoration.value.map(x => x.id),
+ };
+
+ if (props.avatarDecoration) {
+ await os.apiWithDialog('admin/avatar-decorations/update', {
+ id: props.avatarDecoration.id,
+ ...params,
+ });
+
+ emit('done', {
+ updated: {
+ id: props.avatarDecoration.id,
+ ...params,
+ },
+ });
+
+ windowEl.value?.close();
+ } else {
+ const created = await os.apiWithDialog('admin/avatar-decorations/create', params);
+
+ emit('done', {
+ created: created,
+ });
+
+ windowEl.value?.close();
+ }
+}
+
+async function del() {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ text: i18n.tsx.removeAreYouSure({ x: name.value }),
+ });
+ if (canceled) return;
+
+ misskeyApi('admin/avatar-decorations/delete', {
+ id: props.avatarDecoration.id,
+ }).then(() => {
+ emit('done', {
+ deleted: true,
+ });
+ windowEl.value?.close();
+ });
+}
+</script>
+
+<style lang="scss" module>
+.preview {
+ display: grid;
+ place-items: center;
+ grid-template-columns: 1fr 1fr;
+ grid-template-rows: 1fr;
+ gap: var(--MI-margin);
+}
+
+.previewItem {
+ width: 100%;
+ height: 100%;
+ min-height: 160px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: var(--MI-radius);
+
+ &.light {
+ background: #eee;
+ }
+
+ &.dark {
+ background: #222;
+ }
+}
+
+.roleItem {
+ display: flex;
+}
+
+.role {
+ flex: 1;
+}
+
+.roleUnassign {
+ width: 32px;
+ height: 32px;
+ margin-left: 8px;
+ align-self: center;
+}
+
+.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/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index b97e7c0eea..a5cafb1678 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -5,92 +5,38 @@ SPDX-License-Identifier: AGPL-3.0-only
<template>
<MkStickyContainer>
- <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
+ <template #header><MkPageHeader :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="$style.editorRoot">
- <div :class="$style.editorWrapper">
- <div :class="$style.preview">
- <div :class="[$style.previewItem, $style.light]">
- <MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[avatarDecoration]" forceShowDecoration/>
- </div>
- <div :class="[$style.previewItem, $style.dark]">
- <MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[avatarDecoration]" forceShowDecoration/>
- </div>
- </div>
- <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">
- <MkButton inline primary @click="save(avatarDecoration)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
- <MkButton v-if="avatarDecoration.id != null" inline danger @click="del(avatarDecoration)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
- </div>
- </div>
- </div>
+ <div :class="$style.decorations">
+ <div
+ v-for="avatarDecoration in avatarDecorations"
+ :key="avatarDecoration.id"
+ v-panel
+ :class="$style.decoration"
+ @click="edit(avatarDecoration)"
+ >
+ <div :class="$style.decorationName"><MkCondensedLine :minScale="0.5">{{ avatarDecoration.name }}</MkCondensedLine></div>
+ <MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: avatarDecoration.url }]" forceShowDecoration/>
</div>
- </MkFolder>
+ </div>
</div>
</MkSpacer>
</MkStickyContainer>
</template>
<script lang="ts" setup>
-import { ref, computed } from 'vue';
+import { ref, computed, defineAsyncComponent } from 'vue';
import * as Misskey from 'misskey-js';
-import MkButton from '@/components/MkButton.vue';
-import MkInput from '@/components/MkInput.vue';
-import MkTextarea from '@/components/MkTextarea.vue';
import { signinRequired } from '@/account.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-import MkFolder from '@/components/MkFolder.vue';
-
-const avatarDecorations = ref<Misskey.entities.AdminAvatarDecorationsListResponse>([]);
const $i = signinRequired();
-function add() {
- avatarDecorations.value.unshift({
- _id: Math.random().toString(36),
- id: null,
- name: '',
- description: '',
- url: '',
- });
-}
-
-function del(avatarDecoration) {
- os.confirm({
- type: 'warning',
- text: i18n.tsx.deleteAreYouSure({ x: avatarDecoration.name }),
- }).then(({ canceled }) => {
- if (canceled) return;
- avatarDecorations.value = avatarDecorations.value.filter(x => x !== avatarDecoration);
- misskeyApi('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);
- }
-}
+const avatarDecorations = ref<Misskey.entities.AdminAvatarDecorationsListResponse>([]);
function load() {
misskeyApi('admin/avatar-decorations/list').then(_avatarDecorations => {
@@ -100,6 +46,37 @@ function load() {
load();
+async function add(ev: MouseEvent) {
+ const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration-edit-dialog.vue')), {
+ }, {
+ done: result => {
+ if (result.created) {
+ avatarDecorations.value.unshift(result.created);
+ }
+ },
+ closed: () => dispose(),
+ });
+}
+
+function edit(avatarDecoration) {
+ const { dispose } = os.popup(defineAsyncComponent(() => import('./avatar-decoration-edit-dialog.vue')), {
+ avatarDecoration: avatarDecoration,
+ }, {
+ done: result => {
+ if (result.updated) {
+ const index = avatarDecorations.value.findIndex(x => x.id === avatarDecoration.id);
+ avatarDecorations.value[index] = {
+ ...avatarDecorations.value[index],
+ ...result.updated,
+ };
+ } else if (result.deleted) {
+ avatarDecorations.value = avatarDecorations.value.filter(x => x.id !== avatarDecoration.id);
+ }
+ },
+ closed: () => dispose(),
+ });
+}
+
const headerActions = computed(() => [{
asFullButton: true,
icon: 'ti ti-plus',
@@ -116,53 +93,26 @@ definePageMetadata(() => ({
</script>
<style lang="scss" module>
-.editorRoot {
- container: editor / inline-size;
-}
-
-.editorWrapper {
+.decorations {
display: grid;
- grid-template-columns: 1fr;
- grid-template-rows: auto auto;
- gap: var(--MI-margin);
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+ grid-gap: 12px;
}
-.preview {
- display: grid;
- place-items: center;
- grid-template-columns: 1fr 1fr;
- grid-template-rows: 1fr;
- gap: var(--MI-margin);
-}
-
-.previewItem {
- width: 100%;
- height: 100%;
- min-height: 160px;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: var(--MI-radius);
-
- &.light {
- background: #eee;
- }
-
- &.dark {
- background: #222;
- }
+.decoration {
+ cursor: pointer;
+ padding: 16px 16px 28px 16px;
+ border-radius: 8px;
+ text-align: center;
+ font-size: 90%;
+ overflow: clip;
+ contain: content;
}
-@container editor (min-width: 600px) {
- .editorWrapper {
- grid-template-columns: 200px 1fr;
- grid-template-rows: 1fr;
- gap: calc(var(--MI-margin) * 2);
- }
-
- .preview {
- grid-template-columns: 1fr;
- grid-template-rows: 1fr 1fr;
- }
+.decorationName {
+ position: relative;
+ z-index: 10;
+ font-weight: bold;
+ margin-bottom: 20px;
}
</style>
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 969aa6bbf7..3b3f41d9b1 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
ref="windowEl"
:initialWidth="400"
:initialHeight="500"
- :canResize="false"
+ :canResize="true"
@close="windowEl.close()"
@closed="$emit('closed')"
>
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
index f72a0b9383..3c9914b4e2 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
@@ -10,12 +10,12 @@ SPDX-License-Identifier: AGPL-3.0-only
>
<div :class="$style.name"><MkCondensedLine :minScale="0.5">{{ decoration.name }}</MkCondensedLine></div>
<MkAvatar style="width: 60px; height: 60px;" :user="$i" :decorations="[{ url: decoration.url, angle, flipH, offsetX, offsetY }]" forceShowDecoration/>
- <i v-if="decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id))" :class="$style.lock" class="ti ti-lock"></i>
+ <i v-if="locked" :class="$style.lock" class="ti ti-lock"></i>
</div>
</template>
<script lang="ts" setup>
-import { } from 'vue';
+import { computed } from 'vue';
import { signinRequired } from '@/account.js';
const $i = signinRequired();
@@ -37,6 +37,8 @@ const props = defineProps<{
const emit = defineEmits<{
(ev: 'click'): void;
}>();
+
+const locked = computed(() => props.decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => props.decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id)));
</script>
<style lang="scss" module>
@@ -67,5 +69,6 @@ const emit = defineEmits<{
position: absolute;
bottom: 12px;
right: 12px;
+ color: var(--MI_THEME-warn);
}
</style>
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
index 853e536ea3..aa899ac649 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div :class="$style.footer" class="_buttonsCenter">
<MkButton v-if="usingIndex != null" primary rounded @click="update"><i class="ti ti-check"></i> {{ i18n.ts.update }}</MkButton>
<MkButton v-if="usingIndex != null" rounded @click="detach"><i class="ti ti-x"></i> {{ i18n.ts.detach }}</MkButton>
- <MkButton v-else :disabled="exceeded" primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
+ <MkButton v-else :disabled="exceeded || locked" primary rounded @click="attach"><i class="ti ti-check"></i> {{ i18n.ts.attach }}</MkButton>
</div>
</div>
</MkModalWindow>
@@ -61,6 +61,7 @@ const props = defineProps<{
id: string;
url: string;
name: string;
+ roleIdsThatCanBeUsedThisDecoration: string[];
};
}>();
@@ -83,6 +84,7 @@ const emit = defineEmits<{
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
const exceeded = computed(() => ($i.policies.avatarDecorationLimit - $i.avatarDecorations.length) <= 0);
+const locked = computed(() => props.decoration.roleIdsThatCanBeUsedThisDecoration.length > 0 && !$i.roles.some(r => props.decoration.roleIdsThatCanBeUsedThisDecoration.includes(r.id)));
const angle = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].angle : null) ?? 0);
const flipH = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].flipH : null) ?? false);
const offsetX = ref((props.usingIndex != null ? $i.avatarDecorations[props.usingIndex].offsetX : null) ?? 0);
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 61de8b8c7e..061b533b72 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -122,6 +122,9 @@ type AdminAnnouncementsUpdateRequest = operations['admin___announcements___updat
type AdminAvatarDecorationsCreateRequest = operations['admin___avatar-decorations___create']['requestBody']['content']['application/json'];
// @public (undocumented)
+type AdminAvatarDecorationsCreateResponse = operations['admin___avatar-decorations___create']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
type AdminAvatarDecorationsDeleteRequest = operations['admin___avatar-decorations___delete']['requestBody']['content']['application/json'];
// @public (undocumented)
@@ -1253,6 +1256,7 @@ declare namespace entities {
AdminAnnouncementsListResponse,
AdminAnnouncementsUpdateRequest,
AdminAvatarDecorationsCreateRequest,
+ AdminAvatarDecorationsCreateResponse,
AdminAvatarDecorationsDeleteRequest,
AdminAvatarDecorationsListRequest,
AdminAvatarDecorationsListResponse,
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index d0367d8496..5e6bc0a99c 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -31,6 +31,7 @@ import type {
AdminAnnouncementsListResponse,
AdminAnnouncementsUpdateRequest,
AdminAvatarDecorationsCreateRequest,
+ AdminAvatarDecorationsCreateResponse,
AdminAvatarDecorationsDeleteRequest,
AdminAvatarDecorationsListRequest,
AdminAvatarDecorationsListResponse,
@@ -597,7 +598,7 @@ export type Endpoints = {
'admin/announcements/delete': { req: AdminAnnouncementsDeleteRequest; res: EmptyResponse };
'admin/announcements/list': { req: AdminAnnouncementsListRequest; res: AdminAnnouncementsListResponse };
'admin/announcements/update': { req: AdminAnnouncementsUpdateRequest; res: EmptyResponse };
- 'admin/avatar-decorations/create': { req: AdminAvatarDecorationsCreateRequest; res: EmptyResponse };
+ 'admin/avatar-decorations/create': { req: AdminAvatarDecorationsCreateRequest; res: AdminAvatarDecorationsCreateResponse };
'admin/avatar-decorations/delete': { req: AdminAvatarDecorationsDeleteRequest; res: EmptyResponse };
'admin/avatar-decorations/list': { req: AdminAvatarDecorationsListRequest; res: AdminAvatarDecorationsListResponse };
'admin/avatar-decorations/update': { req: AdminAvatarDecorationsUpdateRequest; res: EmptyResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index ced87c4c7e..f3ddf64481 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -34,6 +34,7 @@ export type AdminAnnouncementsListRequest = operations['admin___announcements___
export type AdminAnnouncementsListResponse = operations['admin___announcements___list']['responses']['200']['content']['application/json'];
export type AdminAnnouncementsUpdateRequest = operations['admin___announcements___update']['requestBody']['content']['application/json'];
export type AdminAvatarDecorationsCreateRequest = operations['admin___avatar-decorations___create']['requestBody']['content']['application/json'];
+export type AdminAvatarDecorationsCreateResponse = operations['admin___avatar-decorations___create']['responses']['200']['content']['application/json'];
export type AdminAvatarDecorationsDeleteRequest = operations['admin___avatar-decorations___delete']['requestBody']['content']['application/json'];
export type AdminAvatarDecorationsListRequest = operations['admin___avatar-decorations___list']['requestBody']['content']['application/json'];
export type AdminAvatarDecorationsListResponse = operations['admin___avatar-decorations___list']['responses']['200']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 560960f018..a5333d4f93 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -6324,9 +6324,22 @@ export type operations = {
};
};
responses: {
- /** @description OK (without any results) */
- 204: {
- content: never;
+ /** @description OK (with results) */
+ 200: {
+ content: {
+ 'application/json': {
+ /** Format: id */
+ id: string;
+ /** Format: date-time */
+ createdAt: string;
+ /** Format: date-time */
+ updatedAt: string | null;
+ name: string;
+ description: string;
+ url: string;
+ roleIdsThatCanBeUsedThisDecoration: string[];
+ };
+ };
};
/** @description Client error */
400: {