Merge remote-tracking branch 'misskey-dev/develop' into io

This commit is contained in:
まっちゃとーにゅ 2024-02-27 04:08:00 +09:00
commit d18e3e9b93
No known key found for this signature in database
GPG Key ID: 6AFBBF529601C1DB
12 changed files with 59 additions and 22 deletions

View File

@ -28,6 +28,10 @@
- Fix: nodeinfoにenableMcaptchaとenableTurnstileが無いのを修正
- エンドポイント`flash/update`の`flashId`以外のパラメータは必須ではなくなりました
- Fix: 禁止キーワードを含むートがDelayed Queueに追加されて再処理される問題を修正
- エンドポイント`admin/emoji/update`の各種修正
- 必須パラメータを`id`または`name`のいずれかのみに
- `id`の代わりに`name`で絵文字を指定可能に(`id`・`name`両指定時は従来通り`name`を変更する挙動)
- `category`および`licence`が指定なしの時勝手にnullに上書きされる挙動を修正
## 2024.2.0

View File

@ -405,6 +405,11 @@ export class CustomEmojiService implements OnApplicationShutdown {
return this.emojisRepository.findOneBy({ id });
}
@bindThis
public getEmojiByName(name: string): Promise<MiEmoji | null> {
return this.emojisRepository.findOneBy({ name, host: IsNull() });
}
@bindThis
public dispose(): void {
this.cache.dispose();

View File

@ -148,6 +148,9 @@ export const packedUserLiteSchema = {
emojis: {
type: 'object',
nullable: false, optional: false,
additionalProperties: {
type: 'string',
},
},
onlineStatus: {
type: 'string',

View File

@ -31,7 +31,10 @@ export const meta = {
},
},
ref: 'EmojiDetailed',
res: {
type: 'object',
ref: 'EmojiDetailed',
},
} as const;
export const paramDef = {

View File

@ -64,7 +64,10 @@ export const paramDef = {
format: 'misskey:id',
} },
},
required: ['id', 'name', 'aliases'],
anyOf: [
{ required: ['id'] },
{ required: ['name'] },
],
} as const;
@Injectable()
@ -77,27 +80,33 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
) {
super(meta, paramDef, async (ps, me) => {
let driveFile;
if (ps.fileId) {
driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId });
if (driveFile == null) throw new ApiError(meta.errors.noSuchFile);
}
const emoji = await this.customEmojiService.getEmojiById(ps.id);
if (emoji != null) {
if (ps.name !== emoji.name) {
let emojiId;
if (ps.id) {
emojiId = ps.id;
const emoji = await this.customEmojiService.getEmojiById(ps.id);
if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
if (ps.name && (ps.name !== emoji.name)) {
const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name);
if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists);
}
} else {
throw new ApiError(meta.errors.noSuchEmoji);
if (!ps.name) throw new Error('Invalid Params unexpectedly passed. This is a BUG. Please report it to the development team.');
const emoji = await this.customEmojiService.getEmojiByName(ps.name);
if (!emoji) throw new ApiError(meta.errors.noSuchEmoji);
emojiId = emoji.id;
}
await this.customEmojiService.update(ps.id, {
await this.customEmojiService.update(emojiId, {
driveFile,
name: ps.name,
category: ps.category ?? null,
category: ps.category,
aliases: ps.aliases,
license: ps.license ?? null,
license: ps.license,
isSensitive: ps.isSensitive,
localOnly: ps.localOnly,
requestedBy: ps.requestedBy ?? null,

View File

@ -401,7 +401,8 @@ function toStories(component: string): Promise<string> {
// glob('src/{components,pages,ui,widgets}/**/*.vue')
(async () => {
const globs = await Promise.all([
glob('src/components/global/*.vue'),
glob('src/components/global/Mk*.vue'),
glob('src/components/global/RouterView.vue'),
glob('src/components/Mk{A,B}*.vue'),
glob('src/components/MkDigitalClock.vue'),
glob('src/components/MkGalleryPostPreview.vue'),

View File

@ -32,7 +32,8 @@ export const Default = {
async play({ canvasElement }) {
const canvas = within(canvasElement);
const a = canvas.getByRole<HTMLAnchorElement>('link');
await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
// FIXME: 通るけどその後落ちるのでコメントアウト
// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
await userEvent.pointer({ keys: '[MouseRight]', target: a });
await tick();
const menu = canvas.getByRole('menu');
@ -44,6 +45,7 @@ export const Default = {
},
args: {
to: '#test',
behavior: 'browser',
},
parameters: {
layout: 'centered',

View File

@ -10,7 +10,7 @@ import MkTime from './MkTime.vue';
import { i18n } from '@/i18n.js';
import { dateTimeFormat } from '@/scripts/intl-const.js';
const now = new Date('2023-04-01T00:00:00.000Z');
const future = new Date('3000-04-01T00:00:00.000Z');
const future = new Date('2024-04-01T00:00:00.000Z');
const oneHourAgo = new Date(now.getTime() - 3600000);
const oneDayAgo = new Date(now.getTime() - 86400000);
const oneWeekAgo = new Date(now.getTime() - 604800000);
@ -49,7 +49,7 @@ export const Empty = {
export const RelativeFuture = {
...Empty,
async play({ canvasElement }) {
await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 977 }));
await expect(canvasElement).toHaveTextContent(i18n.tsx._timeIn.years({ n: 1 })); // n (1) = future (2024) - now (2023)
},
args: {
...Empty.args,

View File

@ -145,6 +145,9 @@ type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk'
// @public (undocumented)
type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
// @public (undocumented)
type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json'];
// @public (undocumented)
type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
@ -1202,6 +1205,7 @@ declare namespace entities {
AdminDriveShowFileResponse,
AdminEmojiAddAliasesBulkRequest,
AdminEmojiAddRequest,
AdminEmojiAddResponse,
AdminEmojiCopyRequest,
AdminEmojiCopyResponse,
AdminEmojiDeleteBulkRequest,

View File

@ -41,6 +41,7 @@ import type {
AdminDriveShowFileResponse,
AdminEmojiAddAliasesBulkRequest,
AdminEmojiAddRequest,
AdminEmojiAddResponse,
AdminEmojiCopyRequest,
AdminEmojiCopyResponse,
AdminEmojiDeleteBulkRequest,
@ -597,7 +598,7 @@ export type Endpoints = {
'admin/drive/files': { req: AdminDriveFilesRequest; res: AdminDriveFilesResponse };
'admin/drive/show-file': { req: AdminDriveShowFileRequest; res: AdminDriveShowFileResponse };
'admin/emoji/add-aliases-bulk': { req: AdminEmojiAddAliasesBulkRequest; res: EmptyResponse };
'admin/emoji/add': { req: AdminEmojiAddRequest; res: EmptyResponse };
'admin/emoji/add': { req: AdminEmojiAddRequest; res: AdminEmojiAddResponse };
'admin/emoji/copy': { req: AdminEmojiCopyRequest; res: AdminEmojiCopyResponse };
'admin/emoji/delete-bulk': { req: AdminEmojiDeleteBulkRequest; res: EmptyResponse };
'admin/emoji/delete': { req: AdminEmojiDeleteRequest; res: EmptyResponse };

View File

@ -43,6 +43,7 @@ export type AdminDriveShowFileRequest = operations['admin/drive/show-file']['req
export type AdminDriveShowFileResponse = operations['admin/drive/show-file']['responses']['200']['content']['application/json'];
export type AdminEmojiAddAliasesBulkRequest = operations['admin/emoji/add-aliases-bulk']['requestBody']['content']['application/json'];
export type AdminEmojiAddRequest = operations['admin/emoji/add']['requestBody']['content']['application/json'];
export type AdminEmojiAddResponse = operations['admin/emoji/add']['responses']['200']['content']['application/json'];
export type AdminEmojiCopyRequest = operations['admin/emoji/copy']['requestBody']['content']['application/json'];
export type AdminEmojiCopyResponse = operations['admin/emoji/copy']['responses']['200']['content']['application/json'];
export type AdminEmojiDeleteBulkRequest = operations['admin/emoji/delete-bulk']['requestBody']['content']['application/json'];

View File

@ -3678,7 +3678,9 @@ export type components = {
faviconUrl: string | null;
themeColor: string | null;
};
emojis: Record<string, never>;
emojis: {
[key: string]: string;
};
/** @enum {string} */
onlineStatus: 'unknown' | 'online' | 'active' | 'offline';
badgeRoles?: ({
@ -6947,9 +6949,11 @@ export type operations = {
};
};
responses: {
/** @description OK (without any results) */
204: {
content: never;
/** @description OK (with results) */
200: {
content: {
'application/json': components['schemas']['EmojiDetailed'];
};
};
/** @description Client error */
400: {
@ -7560,13 +7564,13 @@ export type operations = {
content: {
'application/json': {
/** Format: misskey:id */
id: string;
name: string;
id?: string;
name?: string;
/** Format: misskey:id */
fileId?: string;
/** @description Use `null` to reset the category. */
category?: string | null;
aliases: string[];
aliases?: string[];
license?: string | null;
isSensitive?: boolean;
localOnly?: boolean;