Merge branch 'no-ftt-test' into issue16270
This commit is contained in:
commit
37b4340619
|
@ -5231,7 +5231,7 @@ export interface Locale extends ILocale {
|
||||||
*/
|
*/
|
||||||
"prohibitedWordsForNameOfUser": string;
|
"prohibitedWordsForNameOfUser": string;
|
||||||
/**
|
/**
|
||||||
* このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。
|
* このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。ユーザー名(username)に対しても全て小文字に置き換えて検査します。
|
||||||
*/
|
*/
|
||||||
"prohibitedWordsForNameOfUserDescription": string;
|
"prohibitedWordsForNameOfUserDescription": string;
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1303,7 +1303,7 @@ messageToFollower: "フォロワーへのメッセージ"
|
||||||
target: "対象"
|
target: "対象"
|
||||||
testCaptchaWarning: "CAPTCHAのテストを目的とした機能です。<strong>本番環境で使用しないでください。</strong>"
|
testCaptchaWarning: "CAPTCHAのテストを目的とした機能です。<strong>本番環境で使用しないでください。</strong>"
|
||||||
prohibitedWordsForNameOfUser: "禁止ワード(ユーザーの名前)"
|
prohibitedWordsForNameOfUser: "禁止ワード(ユーザーの名前)"
|
||||||
prohibitedWordsForNameOfUserDescription: "このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。"
|
prohibitedWordsForNameOfUserDescription: "このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。ユーザー名(username)に対しても全て小文字に置き換えて検査します。"
|
||||||
yourNameContainsProhibitedWords: "変更しようとした名前に禁止された文字列が含まれています"
|
yourNameContainsProhibitedWords: "変更しようとした名前に禁止された文字列が含まれています"
|
||||||
yourNameContainsProhibitedWordsDescription: "名前に禁止されている文字列が含まれています。この名前を使用したい場合は、サーバー管理者にお問い合わせください。"
|
yourNameContainsProhibitedWordsDescription: "名前に禁止されている文字列が含まれています。この名前を使用したい場合は、サーバー管理者にお問い合わせください。"
|
||||||
thisContentsAreMarkedAsSigninRequiredByAuthor: "投稿者により、表示にはログインが必要と設定されています"
|
thisContentsAreMarkedAsSigninRequiredByAuthor: "投稿者により、表示にはログインが必要と設定されています"
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class NoActionOnDraftRelation1752502434151 {
|
||||||
|
name = 'NoActionOnDraftRelation1752502434151'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_CHANNEL_ID"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_USER_ID"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_RENOTE_ID"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_REPLY_ID"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_e4983f28b4b18b03491536052f5" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_e4983f28b4b18b03491536052f5"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_REPLY_ID" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_RENOTE_ID" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_USER_ID" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_CHANNEL_ID" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -93,6 +93,11 @@ export class SignupService {
|
||||||
if (isPreserved) {
|
if (isPreserved) {
|
||||||
throw new Error('USED_USERNAME');
|
throw new Error('USED_USERNAME');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasProhibitedWords = this.utilityService.isKeyWordIncluded(username.toLowerCase(), this.meta.prohibitedWordsForNameOfUser);
|
||||||
|
if (hasProhibitedWords) {
|
||||||
|
throw new Error('USED_USERNAME');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const keyPair = await new Promise<string[]>((res, rej) =>
|
const keyPair = await new Promise<string[]>((res, rej) =>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { ModuleRef } from '@nestjs/core';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
|
import { EntityNotFoundError } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { Packed } from '@/misc/json-schema.js';
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
|
@ -90,6 +91,17 @@ export class NoteDraftEntityService implements OnModuleInit {
|
||||||
const packedFiles = options?._hint_?.packedFiles;
|
const packedFiles = options?._hint_?.packedFiles;
|
||||||
const packedUsers = options?._hint_?.packedUsers;
|
const packedUsers = options?._hint_?.packedUsers;
|
||||||
|
|
||||||
|
async function nullIfEntityNotFound<T>(promise: Promise<T>): Promise<T | null> {
|
||||||
|
try {
|
||||||
|
return await promise;
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof EntityNotFoundError) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const packed: Packed<'NoteDraft'> = await awaitAll({
|
const packed: Packed<'NoteDraft'> = await awaitAll({
|
||||||
id: noteDraft.id,
|
id: noteDraft.id,
|
||||||
createdAt: this.idService.parse(noteDraft.id).date.toISOString(),
|
createdAt: this.idService.parse(noteDraft.id).date.toISOString(),
|
||||||
|
@ -117,15 +129,15 @@ export class NoteDraftEntityService implements OnModuleInit {
|
||||||
} : undefined,
|
} : undefined,
|
||||||
|
|
||||||
...(opts.detail ? {
|
...(opts.detail ? {
|
||||||
reply: noteDraft.replyId ? this.noteEntityService.pack(noteDraft.replyId, me, {
|
reply: noteDraft.replyId ? nullIfEntityNotFound(this.noteEntityService.pack(noteDraft.replyId, me, {
|
||||||
detail: false,
|
detail: false,
|
||||||
skipHide: opts.skipHide,
|
skipHide: opts.skipHide,
|
||||||
}) : undefined,
|
})) : undefined,
|
||||||
|
|
||||||
renote: noteDraft.renoteId ? this.noteEntityService.pack(noteDraft.renoteId, me, {
|
renote: noteDraft.renoteId ? nullIfEntityNotFound(this.noteEntityService.pack(noteDraft.renoteId, me, {
|
||||||
detail: true,
|
detail: true,
|
||||||
skipHide: opts.skipHide,
|
skipHide: opts.skipHide,
|
||||||
}) : undefined,
|
})) : undefined,
|
||||||
|
|
||||||
poll: noteDraft.hasPoll ? {
|
poll: noteDraft.hasPoll ? {
|
||||||
choices: noteDraft.pollChoices,
|
choices: noteDraft.pollChoices,
|
||||||
|
|
|
@ -24,8 +24,9 @@ export class MiNoteDraft {
|
||||||
})
|
})
|
||||||
public replyId: MiNote['id'] | null;
|
public replyId: MiNote['id'] | null;
|
||||||
|
|
||||||
|
// There is a possibility that replyId is not null but reply is null when the reply note is deleted.
|
||||||
@ManyToOne(type => MiNote, {
|
@ManyToOne(type => MiNote, {
|
||||||
onDelete: 'CASCADE',
|
createForeignKeyConstraints: false,
|
||||||
})
|
})
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public reply: MiNote | null;
|
public reply: MiNote | null;
|
||||||
|
@ -38,8 +39,9 @@ export class MiNoteDraft {
|
||||||
})
|
})
|
||||||
public renoteId: MiNote['id'] | null;
|
public renoteId: MiNote['id'] | null;
|
||||||
|
|
||||||
|
// There is a possibility that renoteId is not null but renote is null when the renote note is deleted.
|
||||||
@ManyToOne(type => MiNote, {
|
@ManyToOne(type => MiNote, {
|
||||||
onDelete: 'CASCADE',
|
createForeignKeyConstraints: false,
|
||||||
})
|
})
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public renote: MiNote | null;
|
public renote: MiNote | null;
|
||||||
|
@ -114,8 +116,10 @@ export class MiNoteDraft {
|
||||||
})
|
})
|
||||||
public channelId: MiChannel['id'] | null;
|
public channelId: MiChannel['id'] | null;
|
||||||
|
|
||||||
|
// There is a possibility that channelId is not null but channel is null when the channel is deleted.
|
||||||
|
// (deleting channel is not implemented so it's not happening now but may happen in the future)
|
||||||
@ManyToOne(type => MiChannel, {
|
@ManyToOne(type => MiChannel, {
|
||||||
onDelete: 'CASCADE',
|
createForeignKeyConstraints: false,
|
||||||
})
|
})
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public channel: MiChannel | null;
|
public channel: MiChannel | null;
|
||||||
|
|
|
@ -51,11 +51,13 @@ export const packedNoteDraftSchema = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
ref: 'Note',
|
ref: 'Note',
|
||||||
|
description: 'The reply target note contents if exists. If the reply target has been deleted since the draft was created, this will be null while replyId is not null.',
|
||||||
},
|
},
|
||||||
renote: {
|
renote: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: true, nullable: true,
|
optional: true, nullable: true,
|
||||||
ref: 'Note',
|
ref: 'Note',
|
||||||
|
description: 'The renote target note contents if exists. If the renote target has been deleted since the draft was created, this will be null while renoteId is not null.',
|
||||||
},
|
},
|
||||||
visibility: {
|
visibility: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
|
|
@ -237,7 +237,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.withRenotes === false) {
|
if (ps.withRenotes === false) {
|
||||||
query.andWhere('note.renoteId IS NULL');
|
query.andWhere(new Brackets(qb => {
|
||||||
|
qb.orWhere('note.renoteId IS NULL');
|
||||||
|
qb.orWhere(new Brackets(qb => {
|
||||||
|
qb.orWhere('note.text IS NOT NULL');
|
||||||
|
qb.orWhere('note.fileIds != \'{}\'');
|
||||||
|
}));
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => {
|
test('withReplies: true でフォローしているユーザーの他人への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -155,6 +157,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
|
test('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -175,6 +179,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
|
test('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -227,6 +233,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
|
test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -243,6 +251,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('自分の他人への返信が含まれる', async () => {
|
test('自分の他人への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
const bobNote = await post(bob, { text: 'hi' });
|
const bobNote = await post(bob, { text: 'hi' });
|
||||||
|
@ -272,6 +282,48 @@ describe('Timelines', () => {
|
||||||
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('[withRenotes: false] フォローしているユーザーの投稿が含まれる', async () => {
|
||||||
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
await setTimeout(250);
|
||||||
|
const bobNote = await post(bob, { text: 'hi' });
|
||||||
|
const carolNote = await post(carol, { text: 'hi' });
|
||||||
|
|
||||||
|
await waitForPushToTl();
|
||||||
|
|
||||||
|
const res = await api('notes/timeline', {
|
||||||
|
limit: 100,
|
||||||
|
withRenotes: false,
|
||||||
|
}, alice);
|
||||||
|
|
||||||
|
assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
|
||||||
|
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('[withRenotes: false] フォローしているユーザーのファイルのみの投稿が含まれる', async () => {
|
||||||
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
await setTimeout(250);
|
||||||
|
const [bobFile, carolFile] = await Promise.all([
|
||||||
|
uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
|
||||||
|
uploadUrl(carol, 'https://raw.githubusercontent.com/misskey-dev/assets/main/public/icon.png'),
|
||||||
|
]);
|
||||||
|
const bobNote = await post(bob, { fileIds: [bobFile.id] });
|
||||||
|
const carolNote = await post(carol, { fileIds: [carolFile.id] });
|
||||||
|
|
||||||
|
await waitForPushToTl();
|
||||||
|
|
||||||
|
const res = await api('notes/timeline', {
|
||||||
|
limit: 100,
|
||||||
|
withRenotes: false,
|
||||||
|
}, alice);
|
||||||
|
|
||||||
|
assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
|
||||||
|
assert.strictEqual(res.body.some(note => note.id === carolNote.id), false);
|
||||||
|
});
|
||||||
|
|
||||||
test('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => {
|
test('[withRenotes: false] フォローしているユーザーの他人の投稿のリノートが含まれない', async () => {
|
||||||
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
|
@ -519,6 +571,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => {
|
test('フォローしていないユーザーからの visibility: specified なノートに返信したときの自身のノートが含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
|
const bobNote = await post(bob, { text: 'hi', visibility: 'specified', visibleUserIds: [alice.id] });
|
||||||
|
@ -535,14 +589,10 @@ describe('Timelines', () => {
|
||||||
/* TODO
|
/* TODO
|
||||||
test('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => {
|
test('自身の visibility: specified なノートへのフォローしていないユーザーからの返信が含まれる', async () => {
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] });
|
const aliceNote = await post(alice, { text: 'hi', visibility: 'specified', visibleUserIds: [bob.id] });
|
||||||
const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id });
|
const bobNote = await post(bob, { text: 'ok', visibility: 'specified', visibleUserIds: [alice.id], replyId: aliceNote.id });
|
||||||
|
|
||||||
await waitForPushToTl();
|
await waitForPushToTl();
|
||||||
|
|
||||||
const res = await api('notes/timeline', { limit: 100 }, alice);
|
const res = await api('notes/timeline', { limit: 100 }, alice);
|
||||||
|
|
||||||
assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
|
assert.strictEqual(res.body.some(note => note.id === bobNote.id), true);
|
||||||
assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok');
|
assert.strictEqual(res.body.find(note => note.id === bobNote.id).text, 'ok');
|
||||||
});
|
});
|
||||||
|
@ -800,6 +850,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
|
test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -816,6 +868,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
|
test('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
await setTimeout(250);
|
await setTimeout(250);
|
||||||
|
@ -920,6 +974,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
|
test('withReplies: false でフォローしているユーザーからの自分への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -954,6 +1010,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
|
test('withReplies: true でフォローしているユーザーの行った別のフォローしているユーザーの visibility: followers な投稿への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -974,6 +1032,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
|
test('withReplies: true でフォローしているユーザーの自分の visibility: followers な投稿への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
await api('following/create', { userId: bob.id }, alice);
|
await api('following/create', { userId: bob.id }, alice);
|
||||||
|
@ -1048,6 +1108,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
|
test('withReplies: false でフォローしていないユーザーからの自分への返信が含まれる', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob] = await Promise.all([signup(), signup()]);
|
const [alice, bob] = await Promise.all([signup(), signup()]);
|
||||||
|
|
||||||
await setTimeout(250);
|
await setTimeout(250);
|
||||||
|
@ -1446,6 +1508,8 @@ describe('Timelines', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('[withReplies: false] 他人への返信が含まれない', async () => {
|
test('[withReplies: false] 他人への返信が含まれない', async () => {
|
||||||
|
/* FIXME: https://github.com/misskey-dev/misskey/issues/12065 */ if (!enableFanoutTimeline) return;
|
||||||
|
|
||||||
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
const [alice, bob, carol] = await Promise.all([signup(), signup(), signup()]);
|
||||||
|
|
||||||
const carolNote = await post(carol, { text: 'hi' });
|
const carolNote = await post(carol, { text: 'hi' });
|
||||||
|
|
|
@ -42,6 +42,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
</I18n>
|
</I18n>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="draft.replyId" class="_nowrap">
|
||||||
|
<i class="ti ti-arrow-back-up"></i> <I18n :src="i18n.ts._drafts.replyTo" tag="span">
|
||||||
|
<template #user>
|
||||||
|
{{ i18n.ts.deletedNote }}
|
||||||
|
</template>
|
||||||
|
</I18n>
|
||||||
|
</div>
|
||||||
<div v-if="draft.renote && draft.text != null" class="_nowrap">
|
<div v-if="draft.renote && draft.text != null" class="_nowrap">
|
||||||
<i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span">
|
<i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span">
|
||||||
<template #user>
|
<template #user>
|
||||||
|
@ -50,6 +57,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||||
</template>
|
</template>
|
||||||
</I18n>
|
</I18n>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else-if="draft.renoteId" class="_nowrap">
|
||||||
|
<i class="ti ti-quote"></i> <I18n :src="i18n.ts._drafts.quoteOf" tag="span">
|
||||||
|
<template #user>
|
||||||
|
{{ i18n.ts.deletedNote }}
|
||||||
|
</template>
|
||||||
|
</I18n>
|
||||||
|
</div>
|
||||||
<div v-if="draft.channel" class="_nowrap">
|
<div v-if="draft.channel" class="_nowrap">
|
||||||
<i class="ti ti-device-tv"></i> {{ i18n.tsx._drafts.postTo({ channel: draft.channel.name }) }}
|
<i class="ti ti-device-tv"></i> {{ i18n.tsx._drafts.postTo({ channel: draft.channel.name }) }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4401,7 +4401,9 @@ export type components = {
|
||||||
* @example xxxxxxxxxx
|
* @example xxxxxxxxxx
|
||||||
*/
|
*/
|
||||||
renoteId?: string | null;
|
renoteId?: string | null;
|
||||||
|
/** @description The reply target note contents if exists. If the reply target has been deleted since the draft was created, this will be null while replyId is not null. */
|
||||||
reply?: components['schemas']['Note'] | null;
|
reply?: components['schemas']['Note'] | null;
|
||||||
|
/** @description The renote target note contents if exists. If the renote target has been deleted since the draft was created, this will be null while renoteId is not null. */
|
||||||
renote?: components['schemas']['Note'] | null;
|
renote?: components['schemas']['Note'] | null;
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
visibility: 'public' | 'home' | 'followers' | 'specified';
|
visibility: 'public' | 'home' | 'followers' | 'specified';
|
||||||
|
|
Loading…
Reference in New Issue