1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-05-20 21:05:28 +02:00

Merge branch 'develop' into room

This commit is contained in:
syuilo
2026-05-07 11:33:46 +09:00
81 changed files with 2599 additions and 3513 deletions

View File

@@ -22,10 +22,10 @@
"@babylonjs/loaders": "9.5.1",
"@babylonjs/materials": "9.5.1",
"@discordapp/twemoji": "16.0.1",
"@github/webauthn-json": "2.1.1",
"@mcaptcha/core-glue": "0.1.0-alpha-5",
"@misskey-dev/browser-image-resizer": "2024.1.0",
"@sentry/vue": "10.48.0",
"@sentry/vue": "10.50.0",
"@simplewebauthn/browser": "13.3.0",
"@syuilo/aiscript": "1.2.1",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0",
@@ -40,7 +40,7 @@
"chartjs-chart-matrix": "3.0.0",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.2.0",
"chromatic": "15.3.1",
"chromatic": "16.6.0",
"compare-versions": "6.1.1",
"cropperjs": "2.1.1",
"date-fns": "4.1.0",
@@ -57,7 +57,7 @@
"is-file-animated": "1.0.2",
"json5": "2.2.3",
"matter-js": "0.20.0",
"mediabunny": "1.40.1",
"mediabunny": "1.41.0",
"mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*",
@@ -69,15 +69,15 @@
"sanitize-html": "2.17.3",
"shiki": "4.0.2",
"textarea-caret": "3.1.0",
"three": "0.183.2",
"three": "0.184.0",
"throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0",
"v-code-diff": "1.13.1",
"vue": "3.5.32",
"vue": "3.5.33",
"wanakana": "5.3.1"
},
"devDependencies": {
"@misskey-dev/summaly": "5.2.5",
"@misskey-dev/summaly": "5.3.0",
"@rollup/plugin-json": "6.1.0",
"@rollup/pluginutils": "5.3.0",
"@storybook/addon-essentials": "8.6.18",
@@ -111,22 +111,22 @@
"@types/textarea-caret": "3.0.4",
"@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6",
"@typescript-eslint/eslint-plugin": "8.58.2",
"@typescript-eslint/parser": "8.58.2",
"@vitest/coverage-v8": "4.1.4",
"@vue/compiler-core": "3.5.32",
"@typescript-eslint/eslint-plugin": "8.59.0",
"@typescript-eslint/parser": "8.59.0",
"@vitest/coverage-v8": "4.1.5",
"@vue/compiler-core": "3.5.33",
"acorn": "8.16.0",
"astring": "1.9.0",
"cross-env": "10.1.0",
"cypress": "15.13.1",
"cypress": "15.14.1",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.8.0",
"eslint-plugin-vue": "10.9.0",
"estree-walker": "3.0.3",
"happy-dom": "20.9.0",
"intersection-observer": "0.12.2",
"micromatch": "4.0.8",
"minimatch": "10.2.5",
"msw": "2.13.3",
"msw": "2.13.6",
"msw-storybook-addon": "2.0.7",
"nodemon": "3.1.14",
"prettier": "3.8.3",
@@ -139,13 +139,13 @@
"storybook": "10.3.5",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"tsx": "4.21.0",
"vite": "8.0.8",
"vite": "8.0.10",
"vite-plugin-glsl": "1.6.0",
"vite-plugin-turbosnap": "1.0.3",
"vitest": "4.1.4",
"vitest": "4.1.5",
"vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "3.2.6",
"vue-component-type-helpers": "3.2.7",
"vue-eslint-parser": "10.4.0",
"vue-tsc": "3.2.6"
"vue-tsc": "3.2.7"
}
}

View File

@@ -135,6 +135,14 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkA :to="notePage(appearNote)">
<MkTime :time="appearNote.createdAt" mode="detail" colored/>
</MkA>
<span style="margin-left: 0.5em;">
<span style="border: 1px solid var(--MI_THEME-divider); margin-right: 0.5em;"></span>
<i v-if="appearNote.visibility === 'public'" class="ti ti-world"></i>
<i v-else-if="appearNote.visibility === 'home'" class="ti ti-home"></i>
<i v-else-if="appearNote.visibility === 'followers'" class="ti ti-lock"></i>
<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ti ti-mail"></i>
<span style="margin-left: 0.3em;">{{ i18n.ts._visibility[appearNote.visibility] }}</span>
</span>
</div>
<MkReactionsViewer
v-if="appearNote.reactionAcceptance !== 'likeOnly'"

View File

@@ -22,21 +22,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { get as webAuthnRequest } from '@github/webauthn-json/browser-ponyfill';
import { startAuthentication } from '@simplewebauthn/browser';
import { i18n } from '@/i18n.js';
import MkButton from '@/components/MkButton.vue';
import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
import type { PublicKeyCredentialRequestOptionsJSON, AuthenticationResponseJSON } from '@simplewebauthn/browser';
const props = defineProps<{
credentialRequest: CredentialRequestOptions;
credentialRequest: PublicKeyCredentialRequestOptionsJSON;
isPerformingPasswordlessLogin?: boolean;
}>();
const emit = defineEmits<{
(ev: 'done', credential: AuthenticationPublicKeyCredential): void;
(ev: 'done', credential: AuthenticationResponseJSON): void;
(ev: 'useTotp'): void;
}>();
@@ -44,7 +44,7 @@ const queryingKey = ref(true);
async function queryKey() {
queryingKey.value = true;
await webAuthnRequest(props.credentialRequest)
await startAuthentication({ optionsJSON: props.credentialRequest })
.catch(() => {
return Promise.reject(null);
})

View File

@@ -67,8 +67,8 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { nextTick, onBeforeUnmount, ref, shallowRef, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import { supported as webAuthnSupported, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
import { browserSupportsWebAuthn } from '@simplewebauthn/browser';
import type { PublicKeyCredentialRequestOptionsJSON, AuthenticationResponseJSON } from '@simplewebauthn/browser';
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
import type { PwResponse } from '@/components/MkSignin.password.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -108,21 +108,18 @@ const userInfo = ref<null | Misskey.entities.UserDetailed>(null);
const password = ref('');
//#region Passkey Passwordless
const credentialRequest = shallowRef<CredentialRequestOptions | null>(null);
const credentialRequest = shallowRef<PublicKeyCredentialRequestOptionsJSON | null>(null);
const passkeyContext = ref('');
const doingPasskeyFromInputPage = ref(false);
function onPasskeyLogin(): void {
if (webAuthnSupported()) {
if (browserSupportsWebAuthn()) {
doingPasskeyFromInputPage.value = true;
waiting.value = true;
misskeyApi('signin-with-passkey', {})
.then((res) => {
passkeyContext.value = res.context ?? '';
credentialRequest.value = parseRequestOptionsFromJSON({
// @ts-expect-error TODO: misskey-js由来の型@simplewebauthn/typesとフロントエンド由来の型@github/webauthn-jsonが合わない
publicKey: res.option,
});
credentialRequest.value = res.option;
page.value = 'passkey';
waiting.value = false;
@@ -131,12 +128,12 @@ function onPasskeyLogin(): void {
}
}
function onPasskeyDone(credential: AuthenticationPublicKeyCredential): void {
function onPasskeyDone(credential: AuthenticationResponseJSON): void {
waiting.value = true;
if (doingPasskeyFromInputPage.value) {
misskeyApi<Misskey.entities.SigninWithPasskeyResponse>('signin-with-passkey', {
credential: credential.toJSON(),
misskeyApi('signin-with-passkey', {
credential: credential,
context: passkeyContext.value,
}).then((res) => {
if (res.signinResponse == null) {
@@ -150,8 +147,7 @@ function onPasskeyDone(credential: AuthenticationPublicKeyCredential): void {
tryLogin({
username: userInfo.value.username,
password: password.value,
// @ts-expect-error TODO: misskey-js由来の型@simplewebauthn/typesとフロントエンド由来の型@github/webauthn-jsonが合わない
credential: credential.toJSON(),
credential: credential,
});
}
}
@@ -253,11 +249,8 @@ async function tryLogin(req: Partial<Misskey.entities.SigninFlowRequest>): Promi
break;
}
case 'passkey': {
if (webAuthnSupported()) {
credentialRequest.value = parseRequestOptionsFromJSON({
// @ts-expect-error TODO: misskey-js由来の型@simplewebauthn/typesとフロントエンド由来の型@github/webauthn-jsonが合わない
publicKey: res.authRequest,
});
if (browserSupportsWebAuthn()) {
credentialRequest.value = res.authRequest;
page.value = 'passkey';
} else {
page.value = 'totp';

View File

@@ -86,7 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { defineAsyncComponent, onDeactivated, onUnmounted, ref } from 'vue';
import { url as local } from '@@/js/config.js';
import { versatileLang } from '@@/js/intl-const.js';
import type { summaly } from '@misskey-dev/summaly';
import type { SummalyResult } from '@misskey-dev/summaly';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { deviceKind } from '@/utility/device-kind.js';
@@ -96,8 +96,6 @@ import { store } from '@/store.js';
import { prefer } from '@/preferences.js';
import { maybeMakeRelative } from '@@/js/url.js';
type SummalyResult = Awaited<ReturnType<typeof summaly>>;
const props = withDefaults(defineProps<{
url: string;
detail?: boolean;

View File

@@ -161,6 +161,16 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
</XFolder>
<XFolder v-if="matchQuery([i18n.ts._role._options.canCreateChannel, 'canCreateChannel'])" v-model:policyMeta="policyMetaModel.canCreateChannel" :isBaseRole="isBaseRole" :readonly="readonly">
<template #label>{{ i18n.ts._role._options.canCreateChannel }}</template>
<template #suffix>{{ valuesModel.canCreateChannel ? i18n.ts.yes : i18n.ts.no }}</template>
<template #default="{ disabled }">
<MkSwitch v-model="valuesModel.canCreateChannel" :disabled="disabled">
<template #label>{{ i18n.ts.enable }}</template>
</MkSwitch>
</template>
</XFolder>
<XFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])" v-model:policyMeta="policyMetaModel.driveCapacityMb" :isBaseRole="isBaseRole" :readonly="readonly">
<template #label>{{ i18n.ts._role._options.driveCapacity }}</template>
<template #valueText>{{ valuesModel.driveCapacityMb }}MB</template>

View File

@@ -139,6 +139,7 @@ async function assign() {
await os.apiWithDialog('admin/roles/assign', { roleId: role.id, userId: user.id, expiresAt });
//role.users.push(user);
usersPaginator.reload();
}
async function unassign(userId: Misskey.entities.User['id'], ev: PointerEvent) {
@@ -149,6 +150,7 @@ async function unassign(userId: Misskey.entities.User['id'], ev: PointerEvent) {
action: async () => {
await os.apiWithDialog('admin/roles/unassign', { roleId: role.id, userId: userId });
//role.users = role.users.filter(u => u.id !== userId);
usersPaginator.reload();
},
}], ev.currentTarget ?? ev.target);
}

View File

@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkPagination>
</div>
<div v-else-if="tab === 'owned'" class="_gaps">
<MkButton type="routerLink" primary rounded to="/channels/new"><i class="ti ti-plus"></i> {{ i18n.ts.createNew }}</MkButton>
<MkButton v-if="$i?.policies.canCreateChannel" type="routerLink" primary rounded to="/channels/new"><i class="ti ti-plus"></i> {{ i18n.ts.createNew }}</MkButton>
<MkPagination v-slot="{items}" :paginator="ownedPaginator">
<div :class="$style.root">
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
@@ -74,6 +74,7 @@ import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import { useRouter } from '@/router.js';
import { Paginator } from '@/utility/paginator.js';
import { $i } from '@/i.js';
const router = useRouter();

View File

@@ -48,11 +48,11 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._2fa.securityKeyInfo }}
</MkInfo>
<MkInfo v-if="!webAuthnSupported()" warn>
<MkInfo v-if="!browserSupportsWebAuthn()" warn>
{{ i18n.ts._2fa.securityKeyNotSupported }}
</MkInfo>
<MkInfo v-else-if="webAuthnSupported() && !$i.twoFactorEnabled" warn>
<MkInfo v-else-if="browserSupportsWebAuthn() && !$i.twoFactorEnabled" warn>
{{ i18n.ts._2fa.registerTOTPBeforeKey }}
</MkInfo>
@@ -83,8 +83,8 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
import { defineAsyncComponent, computed } from 'vue';
import { supported as webAuthnSupported, create as webAuthnCreate, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
import { computed } from 'vue';
import { browserSupportsWebAuthn, startRegistration } from '@simplewebauthn/browser';
import * as Misskey from 'misskey-js';
import MkButton from '@/components/MkButton.vue';
import MkInfo from '@/components/MkInfo.vue';
@@ -196,12 +196,9 @@ async function addSecurityKey() {
const auth = await os.authenticateDialog();
if (auth.canceled) return;
const registrationOptions = parseCreationOptionsFromJSON({
// @ts-expect-error misskey-js側に型がない
publicKey: await os.apiWithDialog('i/2fa/register-key', {
password: auth.result.password,
token: auth.result.token,
}),
const registrationOptions = await os.apiWithDialog('i/2fa/register-key', {
password: auth.result.password,
token: auth.result.token,
});
const name = await os.inputText({
@@ -214,7 +211,7 @@ async function addSecurityKey() {
if (name.canceled) return;
const credential = await os.promiseDialog(
webAuthnCreate(registrationOptions),
startRegistration({ optionsJSON: registrationOptions }),
null,
() => {}, // ユーザーのキャンセルはrejectなのでエラーダイアログを出さない
i18n.ts._2fa.tapSecurityKey,
@@ -228,8 +225,7 @@ async function addSecurityKey() {
password: auth.result.password,
token: auth.result.token,
name: name.result,
// @ts-expect-error misskey-js側に型がない
credential: credential.toJSON(),
credential: credential,
});
}

View File

@@ -6,13 +6,11 @@
import { describe, test, assert, afterEach } from 'vitest';
import { render, cleanup, type RenderResult } from '@testing-library/vue';
import './init';
import type { summaly } from '@misskey-dev/summaly';
import type { SummalyResult } from '@misskey-dev/summaly';
import { components } from '@/components/index.js';
import { directives } from '@/directives/index.js';
import MkUrlPreview from '@/components/MkUrlPreview.vue';
type SummalyResult = Awaited<ReturnType<typeof summaly>>;
describe('MkUrlPreview', () => {
const renderPreviewBy = async (summary: Partial<SummalyResult>): Promise<RenderResult> => {
if (!summary.player) {