Merge branch 'bmjwt' into acct-parse-url

This commit is contained in:
tamaina 2025-08-28 10:39:04 +09:00
commit 9bf42649a5
41 changed files with 260 additions and 170 deletions

View File

@ -12,10 +12,13 @@
- 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。
- 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。
- データベースサイズへの効果が見られない場合はautovacuumが有効になっているか確認してください
- ハイパーリンクによる参照は検知できないためリンク切れとなります。
- 現時点では、2023-10-01以前にクリップされたリモートのートは検知しないため削除対象となります。
- サーバーの初期設定が完了するまでは連合がオンにならないようになりました
- 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました
- 実際の動作に即した名称になり、馴染みのない人でも理解しやすくなりました
- 他サービスにおける「ダイレクトメッセージ」に相当するMisskeyの機能は「チャット」ですが、「ダイレクト投稿」という名称の機能が存在するとそちらがダイレクトメッセージ機能であるような誤解を生んでいました
- 他サービスにおける「ダイレクトメッセージ」に相当するMisskeyの機能は「チャット」ですが(過去のバージョンのMisskeyでも、当該機能は「チャット」ではなく「ダイレクトメッセージ」でした)、「ダイレクト投稿」という名称の機能が存在するとそちらがダイレクトメッセージ機能であるような誤解を生んでいました
- 今後、「チャット」の名称を「ダイレクトメッセージ」に戻す可能性があります
- mfm.jsをアップデートしました
- Enhance: Unicode 15.1 および 16.0 に収録されている絵文字に対応
- Enhance: acctに `.` が入っているユーザーのメンションに対応
@ -27,6 +30,7 @@
- プラグインは1.xに対応したものが必要です
- Playはそのまま動作しますが、新規に作られるプリセットは1.xになります
- 以前のバージョンから無効化されていた note_view_interruptor が有効になりました
- ハンドラは同期的である必要があります
- Feat: セーフモード
- プラグイン・テーマ・カスタムCSSの使用でクライアントの起動に問題が発生した際に、これらを無効にして起動できます
- 以下の方法でセーフモードを起動できます
@ -57,6 +61,7 @@
- Fix: 照会ダイアログでap/showでローカルユーザーを解決した際@username@nullに飛ばされる問題を修正
- Fix: アイコンのデコレーションを付ける際にデコレーションが表示されなくなる問題を修正
- Fix: 管理中アカウント一覧で正しい表示が行われない問題を修正
- Fix: lookupページでリモートURLを指定した際に正しく動作しない問題を修正
### Server
- Feat: サーバー管理コマンド

6
locales/index.d.ts vendored
View File

@ -12032,15 +12032,15 @@ export interface Locale extends ILocale {
*/
"youCanConfigureMoreFederationSettingsLater": string;
/**
*
*
*/
"remoteContentsCleaning": string;
/**
*
*
*/
"remoteContentsCleaning_description": string;
/**
*
*
*/
"remoteContentsCleaning_description2": string;
/**

View File

@ -3216,9 +3216,9 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "分散型サーバーで構成されるネットワーク(Fediverse)に接続すると、他のサーバーと相互にコンテンツのやり取りが可能です。"
doYouConnectToFediverse_description2: "Fediverseと接続することは「連合」とも呼ばれます。"
youCanConfigureMoreFederationSettingsLater: "連合可能なサーバーの指定など、高度な設定も後ほど可能です。"
remoteContentsCleaning: "受信コンテンツの自動クリーニング"
remoteContentsCleaning_description: "連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったコンテンツを自動でサーバーから削除し、ストレージを節約できます。"
remoteContentsCleaning_description2: "ハイパーリンクなど、一部の参照方法はシステム上で検知できません。"
remoteContentsCleaning: "リモートコンテンツの自動クリーニング"
remoteContentsCleaning_description: "連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったリモートコンテンツを自動でサーバーから削除し、ストレージを節約できます。"
remoteContentsCleaning_description2: "ローカル内リモートコンテンツへのハイパーリンクはリンク切れとなります。"
adminInfo: "管理者情報"
adminInfo_description: "問い合わせを受け付けるために使用される管理者情報を設定します。"
adminInfo_mustBeFilled: "オープンサーバー、または連合がオンの場合は必ず入力が必要です。"

View File

@ -481,6 +481,7 @@ export class UserEntityService implements OnModuleInit {
const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null;
// TODO: 例えば avatarUrl: true など間違った型を設定しても型エラーにならないのをどうにかする(ジェネリクス使わない方法で実装するしかなさそう?)
const packed = {
id: user.id,
name: user.name,

View File

@ -65,6 +65,7 @@ import {
packedMetaDetailedSchema,
packedMetaLiteSchema,
} from '@/models/json-schema/meta.js';
import { packedUserWebhookSchema } from '@/models/json-schema/user-webhook.js';
import { packedSystemWebhookSchema } from '@/models/json-schema/system-webhook.js';
import { packedAbuseReportNotificationRecipientSchema } from '@/models/json-schema/abuse-report-notification-recipient.js';
import { packedChatMessageSchema, packedChatMessageLiteSchema, packedChatMessageLiteForRoomSchema, packedChatMessageLiteFor1on1Schema } from '@/models/json-schema/chat-message.js';
@ -134,6 +135,7 @@ export const refs = {
MetaLite: packedMetaLiteSchema,
MetaDetailedOnly: packedMetaDetailedOnlySchema,
MetaDetailed: packedMetaDetailedSchema,
UserWebhook: packedUserWebhookSchema,
SystemWebhook: packedSystemWebhookSchema,
AbuseReportNotificationRecipient: packedAbuseReportNotificationRecipientSchema,
ChatMessage: packedChatMessageSchema,

View File

@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { webhookEventTypes } from '@/models/Webhook.js';
export const packedUserWebhookSchema = {
type: 'object',
properties: {
id: {
type: 'string',
format: 'id',
optional: false, nullable: false,
},
userId: {
type: 'string',
format: 'id',
optional: false, nullable: false,
},
name: {
type: 'string',
optional: false, nullable: false,
},
on: {
type: 'array',
items: {
type: 'string',
optional: false, nullable: false,
enum: webhookEventTypes,
},
},
url: {
type: 'string',
optional: false, nullable: false,
},
secret: {
type: 'string',
optional: false, nullable: false,
},
active: {
type: 'boolean',
optional: false, nullable: false,
},
latestSentAt: {
type: 'string',
format: 'date-time',
optional: false, nullable: true,
},
latestStatus: {
type: 'integer',
optional: false, nullable: true,
},
},
} as const;

View File

@ -65,7 +65,7 @@ export const packedUserLiteSchema = {
avatarUrl: {
type: 'string',
format: 'url',
nullable: true, optional: false,
nullable: false, optional: false,
},
avatarBlurhash: {
type: 'string',
@ -591,7 +591,7 @@ export const packedMeDetailedOnlySchema = {
},
mutedInstances: {
type: 'array',
nullable: true, optional: false,
nullable: false, optional: false,
items: {
type: 'string',
nullable: false, optional: false,

View File

@ -157,6 +157,22 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
maybeSensitive: {
type: 'boolean',
optional: false, nullable: false,
},
maybePorn: {
type: 'boolean',
optional: false, nullable: false,
},
requestIp: {
type: 'string',
optional: false, nullable: true,
},
requestHeaders: {
type: 'object',
optional: false, nullable: true,
},
},
},
} as const;

View File

@ -223,10 +223,12 @@ export const meta = {
sensitiveMediaDetection: {
type: 'string',
optional: false, nullable: false,
enum: ['none', 'all', 'local', 'remote'],
},
sensitiveMediaDetectionSensitivity: {
type: 'string',
optional: false, nullable: false,
enum: ['medium', 'low', 'high', 'veryLow', 'veryHigh'],
},
setSensitiveFlagAutomatically: {
type: 'boolean',
@ -473,6 +475,10 @@ export const meta = {
type: 'string',
optional: false, nullable: true,
},
feedbackUrl: {
type: 'string',
optional: false, nullable: true,
},
summalyProxy: {
type: 'string',
optional: false, nullable: true,

View File

@ -46,6 +46,14 @@ export const meta = {
type: 'string',
},
},
iconUrl: {
type: 'string',
optional: true, nullable: true,
},
description: {
type: 'string',
optional: true, nullable: true,
},
},
},
},
@ -88,6 +96,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
createdAt: this.idService.parse(token.id).date.toISOString(),
lastUsedAt: token.lastUsedAt?.toISOString(),
permission: token.app ? token.app.permission : token.permission,
iconUrl: token.iconUrl,
description: token.description ?? token.app?.description ?? null,
})));
});
}

View File

@ -21,29 +21,7 @@ export const meta = {
type: 'array',
items: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
userId: {
type: 'string',
format: 'misskey:id',
},
name: { type: 'string' },
on: {
type: 'array',
items: {
type: 'string',
enum: webhookEventTypes,
},
},
url: { type: 'string' },
secret: { type: 'string' },
active: { type: 'boolean' },
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
latestStatus: { type: 'integer', nullable: true },
},
ref: 'UserWebhook',
},
},
} as const;
@ -65,19 +43,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
userId: me.id,
});
return webhooks.map(webhook => (
{
id: webhook.id,
userId: webhook.userId,
name: webhook.name,
on: webhook.on,
url: webhook.url,
secret: webhook.secret,
active: webhook.active,
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
latestStatus: webhook.latestStatus,
}
));
return webhooks.map(webhook => ({
id: webhook.id,
userId: webhook.userId,
name: webhook.name,
on: webhook.on,
url: webhook.url,
secret: webhook.secret,
active: webhook.active,
latestSentAt: webhook.latestSentAt ? webhook.latestSentAt.toISOString() : null,
latestStatus: webhook.latestStatus,
}));
});
}
}

View File

@ -28,29 +28,7 @@ export const meta = {
res: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'misskey:id',
},
userId: {
type: 'string',
format: 'misskey:id',
},
name: { type: 'string' },
on: {
type: 'array',
items: {
type: 'string',
enum: webhookEventTypes,
},
},
url: { type: 'string' },
secret: { type: 'string' },
active: { type: 'boolean' },
latestSentAt: { type: 'string', format: 'date-time', nullable: true },
latestStatus: { type: 'integer', nullable: true },
},
ref: 'UserWebhook',
},
} as const;

View File

@ -8,7 +8,7 @@
import { parse as vueSfcParse } from 'vue/compiler-sfc';
import {
createLogger,
EnvironmentModuleGraph,
type EnvironmentModuleGraph,
type LogErrorOptions,
type LogOptions,
normalizePath,

View File

@ -4,11 +4,11 @@
*/
import { utils, values } from '@syuilo/aiscript';
import { genId } from '@/utility/id.js';
import { ref } from 'vue';
import type { Ref } from 'vue';
import * as Misskey from 'misskey-js';
import { assertStringAndIsIn } from './common.js';
import type { Ref } from 'vue';
import { genId } from '@/utility/id.js';
const ALIGNS = ['left', 'center', 'right'] as const;
const FONTS = ['serif', 'sans-serif', 'monospace'] as const;
@ -21,16 +21,15 @@ type BorderStyle = (typeof BORDER_STYLES)[number];
export type AsUiComponentBase = {
id: string;
hidden?: boolean;
children?: AsUiComponent['id'][];
};
export type AsUiRoot = AsUiComponentBase & {
type: 'root';
children: AsUiComponent['id'][];
};
export type AsUiContainer = AsUiComponentBase & {
type: 'container';
children?: AsUiComponent['id'][];
align?: Align;
bgColor?: string;
fgColor?: string;
@ -123,7 +122,6 @@ export type AsUiSelect = AsUiComponentBase & {
export type AsUiFolder = AsUiComponentBase & {
type: 'folder';
children?: AsUiComponent['id'][];
title?: string;
opened?: boolean;
};

View File

@ -61,8 +61,8 @@ import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/utili
const props = withDefaults(defineProps<{
user: Misskey.entities.User;
withLocked: boolean;
withDescription: boolean;
withLocked?: boolean;
withDescription?: boolean;
}>(), {
withLocked: true,
withDescription: true,

View File

@ -39,13 +39,13 @@ withDefaults(defineProps<{
});
const emit = defineEmits<{
(ev: 'done', r?: Misskey.entities.DriveFolder[]): void;
(ev: 'done', r?: (Misskey.entities.DriveFolder | null)[]): void;
(ev: 'closed'): void;
}>();
const dialog = useTemplateRef('dialog');
const selected = ref<Misskey.entities.DriveFolder[]>([]);
const selected = ref<(Misskey.entities.DriveFolder | null)[]>([]);
function ok() {
emit('done', selected.value);
@ -57,7 +57,7 @@ function cancel() {
dialog.value?.close();
}
function onChangeSelection(v: Misskey.entities.DriveFolder[]) {
function onChangeSelection(v: (Misskey.entities.DriveFolder | null)[]) {
selected.value = v;
}
</script>

View File

@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<Mfm class="summaryMfm" :text="flash.summary" :plain="true" :nowrap="true"/>
</p>
<footer>
<img v-if="flash.user.avatarUrl != null" class="icon" :src="flash.user.avatarUrl"/>
<img class="icon" :src="flash.user.avatarUrl"/>
<p>{{ userName(flash.user) }}</p>
</footer>
</article>

View File

@ -94,6 +94,8 @@ async function calcAspectRatio() {
onMounted(() => {
calcAspectRatio();
if (gallery.value == null) return; // TS
lightbox = new PhotoSwipeLightbox({
dataSource: props.mediaList
.filter(media => {

View File

@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<div :class="$style.root" class="_forceShrinkSpacer">
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :key="reloadCount" :router="windowRouter"/>
<RouterView v-else :key="reloadCount" :router="windowRouter"/>
<StackingRouterView v-if="prefer.s['experimental.stackingRouterView']" :key="reloadCount.toString() + ':stacking'" :router="windowRouter"/>
<RouterView v-else :key="reloadCount.toString() + ':non-stacking'" :router="windowRouter"/>
</div>
</MkWindow>
</template>
@ -58,20 +58,15 @@ const windowRouter = createRouter(props.initialPath);
const pageMetadata = ref<null | PageMetadata>(null);
const windowEl = useTemplateRef('windowEl');
const history = ref<{ path: string; }[]>([{
const _history_ = ref<{ path: string; }[]>([{
path: windowRouter.getCurrentFullPath(),
}]);
const buttonsLeft = computed(() => {
const buttons: Record<string, unknown>[] = [];
if (history.value.length > 1) {
buttons.push({
icon: 'ti ti-arrow-left',
onClick: back,
});
}
return buttons;
return _history_.value.length > 1 ? [{
icon: 'ti ti-arrow-left',
title: i18n.ts.goBack,
onClick: back,
}] : [];
});
const buttonsRight = computed(() => {
const buttons = [{
@ -97,12 +92,12 @@ function getSearchMarker(path: string) {
const searchMarkerId = ref<string | null>(getSearchMarker(props.initialPath));
windowRouter.addListener('push', ctx => {
history.value.push({ path: ctx.fullPath });
_history_.value.push({ path: ctx.fullPath });
});
windowRouter.addListener('replace', ctx => {
history.value.pop();
history.value.push({ path: ctx.fullPath });
_history_.value.pop();
_history_.value.push({ path: ctx.fullPath });
});
windowRouter.addListener('change', ctx => {
@ -150,8 +145,8 @@ const contextmenu = computed(() => ([{
}]));
function back() {
history.value.pop();
windowRouter.replaceByPath(history.value.at(-1)!.path);
_history_.value.pop();
windowRouter.replaceByPath(_history_.value.at(-1)!.path);
}
function reload() {

View File

@ -105,9 +105,7 @@ async function addRole() {
.map(r => ({ text: r.name, value: r }));
const { canceled, result: role } = await os.select({ items });
if (canceled) {
return;
}
if (canceled || role == null) return;
selectedRoleIds.value.push(role.id);
}

View File

@ -51,6 +51,7 @@ export type DefaultStoredWidget = {
<script lang="ts" setup>
import { defineAsyncComponent, ref, computed } from 'vue';
import { isLink } from '@@/js/is-link.js';
import { genId } from '@/utility/id.js';
import MkSelect from '@/components/MkSelect.vue';
import MkButton from '@/components/MkButton.vue';
@ -58,7 +59,6 @@ import { widgets as widgetDefs, federationWidgets } from '@/widgets/index.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { isLink } from '@@/js/is-link.js';
const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@ -81,7 +81,7 @@ const emit = defineEmits<{
(ev: 'updateWidgets', widgets: Widget[]): void;
(ev: 'addWidget', widget: Widget): void;
(ev: 'removeWidget', widget: Widget): void;
(ev: 'updateWidget', widget: Partial<Widget>): void;
(ev: 'updateWidget', widget: { id: Widget['id']; data: Widget['data']; }): void;
(ev: 'exit'): void;
}>();
@ -104,7 +104,7 @@ const addWidget = () => {
const removeWidget = (widget) => {
emit('removeWidget', widget);
};
const updateWidget = (id, data) => {
const updateWidget = (id: Widget['id'], data: Widget['data']) => {
emit('updateWidget', { id, data });
};

View File

@ -2,7 +2,7 @@
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { waitFor } from '@storybook/test';
import MkPageHeader from './MkPageHeader.vue';
import type { StoryObj } from '@storybook/vue3';
@ -59,6 +59,7 @@ export const Icon = {
{
...OneTab.args.tabs[0],
icon: 'ti ti-home',
title: 'Home',
},
],
},
@ -71,6 +72,7 @@ export const IconOnly = {
{
key: Icon.args.tabs[0].key,
icon: Icon.args.tabs[0].icon,
title: Icon.args.tabs[0].title,
iconOnly: true,
},
],

View File

@ -30,19 +30,21 @@ const props = defineProps<{
router?: Router;
}>();
const router = props.router ?? inject(DI.router);
const _router = props.router ?? inject(DI.router);
if (router == null) {
if (_router == null) {
throw new Error('no router provided');
}
const router = _router;
const viewId = randomId();
provide(DI.viewId, viewId);
const currentDepth = inject(DI.routerCurrentDepth, 0);
provide(DI.routerCurrentDepth, currentDepth + 1);
const current = router.current!;
const current = router.current;
const currentPageComponent = shallowRef('component' in current.route ? current.route.component : MkLoadingPage);
const currentPageProps = ref(current.props);
let currentRoutePath = current.route.path;

View File

@ -16,9 +16,11 @@ import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
import MkAchievements from '@/components/MkAchievements.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { $i } from '@/i.js';
import { ensureSignin } from '@/i.js';
import { claimAchievement } from '@/utility/achievements.js';
const $i = ensureSignin();
let timer: number | null;
function viewAchievements3min() {

View File

@ -156,12 +156,9 @@ async function done() {
isSensitive: isSensitive.value,
localOnly: localOnly.value,
roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id),
fileId: file.value ? file.value.id : undefined,
};
if (file.value) {
params.fileId = file.value.id;
}
if (props.emoji) {
await os.apiWithDialog('admin/emoji/update', {
id: props.emoji.id,
@ -177,7 +174,12 @@ async function done() {
windowEl.value?.close();
} else {
const created = await os.apiWithDialog('admin/emoji/add', params);
if (params.fileId == null) return;
const created = await os.apiWithDialog('admin/emoji/add', {
...params,
fileId: params.fileId, // TS
});
emit('done', {
created: created,

View File

@ -48,7 +48,7 @@ function _fetch_() {
if (res.type === 'User') {
mainRouter.replace('/@:acct/:page?', {
params: {
acct: res.host != null ? `${res.object.username}@${res.object.host}` : res.object.username,
acct: res.object.host != null ? `${res.object.username}@${res.object.host}` : res.object.username,
},
});
} else if (res.type === 'Note') {

View File

@ -82,10 +82,10 @@ const logs = ref<{
text: string;
print: boolean;
}[]>([]);
const root = ref<AsUiRoot>();
const root = ref<AsUiRoot | undefined>();
const components = ref<Ref<AsUiComponent>[]>([]);
const uiKey = ref(0);
const uiInspectorOpenedComponents = ref(new Map<string, boolean>);
const uiInspectorOpenedComponents = ref(new WeakMap<AsUiComponent | Ref<AsUiComponent>, boolean>);
const saved = miLocalStorage.getItem('scratchpad');
if (saved) {
@ -186,11 +186,13 @@ const headerActions = computed(() => []);
const headerTabs = computed(() => []);
const showns = computed(() => {
if (root.value == null) return new Set<string>();
const result = new Set<string>();
(function addChildrenToResult(c: AsUiComponent) {
result.add(c.id);
if (c.children) {
const childComponents = components.value.filter(v => c.children.includes(v.value.id));
const children = c.children;
if (children) {
const childComponents = components.value.filter(v => children.includes(v.value.id));
for (const child of childComponents) {
addChildrenToResult(child.value);
}

View File

@ -91,7 +91,7 @@ async function addItem() {
value: '-', text: i18n.ts.divider,
}],
});
if (canceled) return;
if (canceled || item == null) return;
items.value = [...items.value, {
id: genId(),
type: item,

View File

@ -40,7 +40,7 @@ async function install() {
code.value = null;
router.push('/settings/plugin');
} catch (err) {
} catch (err: any) {
os.alert({
type: 'error',
title: 'Install failed',

View File

@ -53,7 +53,7 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string
misskeyApiGet('federation/instances', {
sort: '+pubSub',
limit: 20,
blocked: 'false',
blocked: false,
}).then(_instances => {
instances.value = _instances;
});

View File

@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
mode="default"
>
<MkMarqueeText :key="key" :duration="marqueeDuration" :reverse="marqueeReverse">
<span v-for="instance in instances" :key="instance.id" :class="[$style.item, { [$style.colored]: colored }]" :style="{ background: colored ? instance.themeColor : '' }">
<span v-for="instance in instances" :key="instance.id" :class="[$style.item, { [$style.colored]: colored }]" :style="{ background: colored ? instance.themeColor ?? '' : '' }">
<img :class="$style.icon" :src="getInstanceIcon(instance)" alt=""/>
<MkA :to="`/instance-info/${instance.host}`" :class="$style.host" class="_monospace">
{{ instance.host }}

View File

@ -300,15 +300,13 @@ export async function createCroppedImageDriveFileFromImageDriveFile(imageDriveFi
});
}
export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise<Misskey.entities.DriveFolder[]> {
export async function selectDriveFolder(initialFolder: Misskey.entities.DriveFolder['id'] | null): Promise<(Misskey.entities.DriveFolder | null)[]> {
return new Promise(async resolve => {
const { dispose } = await os.popupAsyncWithDialog(import('@/components/MkDriveFolderSelectDialog.vue').then(x => x.default), {
initialFolder,
}, {
done: folders => {
if (folders) {
resolve(folders);
}
resolve(folders);
},
closed: () => dispose(),
});

View File

@ -9,7 +9,7 @@ import * as mfm from 'mfm-js';
export function extractMentions(nodes: mfm.MfmNode[]): mfm.MfmMention['props'][] {
// TODO: 重複を削除
const mentionNodes = mfm.extract(nodes, (node) => node.type === 'mention');
const mentionNodes = mfm.extract(nodes, (node) => node.type === 'mention') as mfm.MfmMention[];
const mentions = mentionNodes.map(x => x.props);
return mentions;

View File

@ -15,7 +15,7 @@ import 'intersection-observer';
describe('XHome', () => {
const renderHome = (user: Partial<Misskey.entities.UserDetailed>): RenderResult => {
return render(XHome, {
props: { user, disableNotes: true },
props: { user: user as Misskey.entities.UserDetailed, disableNotes: true },
global: { directives, components },
});
};

View File

@ -29,7 +29,7 @@ describe('MkMediaImage', () => {
comment: null,
properties: {},
...image,
} as DriveFile,
} as Misskey.entities.DriveFile,
},
global: { directives, components },
});

View File

@ -55,6 +55,7 @@
"./@types/**/*.ts"
],
"exclude": [
".storybook/**/*"
".storybook/**/*",
"./src/**/*.stories.ts"
]
}

View File

@ -2157,6 +2157,7 @@ declare namespace entities {
Note,
NoteDraft,
NoteReaction,
NoteReactionWithNote,
NoteFavorite,
Notification_2 as Notification,
DriveFile,
@ -2198,6 +2199,7 @@ declare namespace entities {
MetaLite,
MetaDetailedOnly,
MetaDetailed,
UserWebhook,
SystemWebhook,
AbuseReportNotificationRecipient,
ChatMessage,
@ -2967,10 +2969,13 @@ type ModerationLog = {
} | {
type: 'deleteChatRoom';
info: ModerationLogPayloads['deleteChatRoom'];
} | {
type: 'updateProxyAccountDescription';
info: ModerationLogPayloads['updateProxyAccountDescription'];
});
// @public (undocumented)
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "forwardAbuseReport", "updateAbuseReportNote", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner", "createSystemWebhook", "updateSystemWebhook", "deleteSystemWebhook", "createAbuseReportNotificationRecipient", "updateAbuseReportNotificationRecipient", "deleteAbuseReportNotificationRecipient", "deleteAccount", "deletePage", "deleteFlash", "deleteGalleryPost", "deleteChatRoom"];
export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "updateRemoteInstanceNote", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "forwardAbuseReport", "updateAbuseReportNote", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner", "createSystemWebhook", "updateSystemWebhook", "deleteSystemWebhook", "createAbuseReportNotificationRecipient", "updateAbuseReportNotificationRecipient", "deleteAbuseReportNotificationRecipient", "deleteAccount", "deletePage", "deleteFlash", "deleteGalleryPost", "deleteChatRoom", "updateProxyAccountDescription"];
// @public (undocumented)
type MuteCreateRequest = operations['mute___create']['requestBody']['content']['application/json'];
@ -3015,6 +3020,9 @@ type NoteFavorite = components['schemas']['NoteFavorite'];
// @public (undocumented)
type NoteReaction = components['schemas']['NoteReaction'];
// @public (undocumented)
type NoteReactionWithNote = components['schemas']['NoteReactionWithNote'];
// @public (undocumented)
type NotesChildrenRequest = operations['notes___children']['requestBody']['content']['application/json'];
@ -3818,6 +3826,9 @@ type UsersShowResponse = operations['users___show']['responses']['200']['content
// @public (undocumented)
type UsersUpdateMemoRequest = operations['users___update-memo']['requestBody']['content']['application/json'];
// @public (undocumented)
type UserWebhook = components['schemas']['UserWebhook'];
// @public (undocumented)
type V2AdminEmojiListRequest = operations['v2___admin___emoji___list']['requestBody']['content']['application/json'];

View File

@ -16,6 +16,7 @@ export type App = components['schemas']['App'];
export type Note = components['schemas']['Note'];
export type NoteDraft = components['schemas']['NoteDraft'];
export type NoteReaction = components['schemas']['NoteReaction'];
export type NoteReactionWithNote = components['schemas']['NoteReactionWithNote'];
export type NoteFavorite = components['schemas']['NoteFavorite'];
export type Notification = components['schemas']['Notification'];
export type DriveFile = components['schemas']['DriveFile'];
@ -57,6 +58,7 @@ export type ReversiGameDetailed = components['schemas']['ReversiGameDetailed'];
export type MetaLite = components['schemas']['MetaLite'];
export type MetaDetailedOnly = components['schemas']['MetaDetailedOnly'];
export type MetaDetailed = components['schemas']['MetaDetailed'];
export type UserWebhook = components['schemas']['UserWebhook'];
export type SystemWebhook = components['schemas']['SystemWebhook'];
export type AbuseReportNotificationRecipient = components['schemas']['AbuseReportNotificationRecipient'];
export type ChatMessage = components['schemas']['ChatMessage'];

View File

@ -3949,7 +3949,7 @@ export type components = {
*/
host: string | null;
/** Format: url */
avatarUrl: string | null;
avatarUrl: string;
avatarBlurhash: string | null;
avatarDecorations: {
/** Format: id */
@ -4085,7 +4085,7 @@ export type components = {
unreadNotificationsCount: number;
mutedWords: string[][];
hardMutedWords: string[][];
mutedInstances: string[] | null;
mutedInstances: string[];
notificationRecieveConfig: {
note?: {
/** @enum {string} */
@ -4454,16 +4454,22 @@ export type components = {
reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null;
};
NoteReaction: {
/**
* Format: id
* @example xxxxxxxxxx
*/
/** Format: id */
id: string;
/** Format: date-time */
createdAt: string;
user: components['schemas']['UserLite'];
type: string;
};
NoteReactionWithNote: {
/** Format: id */
id: string;
/** Format: date-time */
createdAt: string;
user: components['schemas']['UserLite'];
type: string;
note: components['schemas']['Note'];
};
NoteFavorite: {
/**
* Format: id
@ -5437,6 +5443,20 @@ export type components = {
cacheRemoteSensitiveFiles: boolean;
};
MetaDetailed: components['schemas']['MetaLite'] & components['schemas']['MetaDetailedOnly'];
UserWebhook: {
/** Format: id */
id: string;
/** Format: id */
userId: string;
name: string;
on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
url: string;
secret: string;
active: boolean;
/** Format: date-time */
latestSentAt: string | null;
latestStatus: number | null;
};
SystemWebhook: {
id: string;
isActive: boolean;
@ -6694,6 +6714,13 @@ export interface operations {
updatedAt: string | null;
text: string;
title: string;
icon: string | null;
display: string;
isActive: boolean;
forExistingUsers: boolean;
silence: boolean;
needConfirmationToRead: boolean;
userId: string | null;
imageUrl: string | null;
reads: number;
}[];
@ -7655,6 +7682,10 @@ export interface operations {
folderId: string | null;
isSensitive: boolean;
isLink: boolean;
maybeSensitive: boolean;
maybePorn: boolean;
requestIp: string | null;
requestHeaders: Record<string, never> | null;
};
};
};
@ -9298,8 +9329,10 @@ export interface operations {
mcaptchaSecretKey: string | null;
recaptchaSecretKey: string | null;
turnstileSecretKey: string | null;
sensitiveMediaDetection: string;
sensitiveMediaDetectionSensitivity: string;
/** @enum {string} */
sensitiveMediaDetection: 'none' | 'all' | 'local' | 'remote';
/** @enum {string} */
sensitiveMediaDetectionSensitivity: 'medium' | 'low' | 'high' | 'veryLow' | 'veryHigh';
setSensitiveFlagAutomatically: boolean;
enableSensitiveMediaDetectionForVideos: boolean;
/** Format: id */
@ -9362,6 +9395,7 @@ export interface operations {
privacyPolicyUrl: string | null;
inquiryUrl: string | null;
repositoryUrl: string | null;
feedbackUrl: string | null;
/**
* @deprecated
* @description [Deprecated] Use "urlPreviewSummaryProxyUrl" instead.
@ -24401,6 +24435,8 @@ export interface operations {
/** Format: date-time */
lastUsedAt?: string;
permission: string[];
iconUrl?: string | null;
description?: string | null;
}[];
};
};
@ -27636,20 +27672,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
'application/json': {
/** Format: misskey:id */
id: string;
/** Format: misskey:id */
userId: string;
name: string;
on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
url: string;
secret: string;
active: boolean;
/** Format: date-time */
latestSentAt: string | null;
latestStatus: number | null;
}[];
'application/json': components['schemas']['UserWebhook'][];
};
};
/** @description Client error */
@ -27715,20 +27738,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
'application/json': {
/** Format: misskey:id */
id: string;
/** Format: misskey:id */
userId: string;
name: string;
on: ('mention' | 'unfollow' | 'follow' | 'followed' | 'note' | 'reply' | 'renote' | 'reaction')[];
url: string;
secret: string;
active: boolean;
/** Format: date-time */
latestSentAt: string | null;
latestStatus: number | null;
};
'application/json': components['schemas']['UserWebhook'];
};
};
/** @description Client error */
@ -35752,7 +35762,7 @@ export interface operations {
[name: string]: unknown;
};
content: {
'application/json': components['schemas']['NoteReaction'][];
'application/json': components['schemas']['NoteReactionWithNote'][];
};
};
/** @description Client error */

View File

@ -167,6 +167,7 @@ export const moderationLogTypes = [
'deleteFlash',
'deleteGalleryPost',
'deleteChatRoom',
'updateProxyAccountDescription',
] as const;
export const queueTypes = [
@ -193,7 +194,15 @@ export const reversiUpdateKeys = [
export type ReversiUpdateKey = typeof reversiUpdateKeys[number];
type AvatarDecoration = UserLite['avatarDecorations'][number];
type AvatarDecoration = {
id: string;
name: string;
url: string;
angle?: number;
flipH?: boolean;
offsetX?: number;
offsetY?: number;
};
type ReceivedAbuseReport = {
reportId: AbuseReportNotificationRecipient['id'];
@ -455,4 +464,8 @@ export type ModerationLogPayloads = {
roomId: string;
room: ChatRoom;
};
updateProxyAccountDescription: {
before: string | null;
after: string | null;
}
};

View File

@ -203,6 +203,9 @@ export type ModerationLog = {
} | {
type: 'deleteChatRoom';
info: ModerationLogPayloads['deleteChatRoom'];
} | {
type: 'updateProxyAccountDescription';
info: ModerationLogPayloads['updateProxyAccountDescription'];
});
export type ServerStats = {