* Add an option for timeline replies. Credit to Emilis (puffaboo) * update db on request
This commit is contained in:
		
							parent
							
								
									c0ef868ad2
								
							
						
					
					
						commit
						3ff89fa7ec
					
				
					 12 changed files with 39 additions and 7 deletions
				
			
		|  | @ -141,6 +141,8 @@ flagAsBot: "Botとして設定" | ||||||
| flagAsBotDescription: "このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったものになります。" | flagAsBotDescription: "このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったものになります。" | ||||||
| flagAsCat: "Catとして設定" | flagAsCat: "Catとして設定" | ||||||
| flagAsCatDescription: "このアカウントが猫であることを示す場合は、このフラグをオンにします。" | flagAsCatDescription: "このアカウントが猫であることを示す場合は、このフラグをオンにします。" | ||||||
|  | flagShowTimelineReplies: "タイムラインにノートへの返信を表示する" | ||||||
|  | flagShowTimelineRepliesDescription: "オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。" | ||||||
| autoAcceptFollowed: "フォロー中ユーザーからのフォロリクを自動承認" | autoAcceptFollowed: "フォロー中ユーザーからのフォロリクを自動承認" | ||||||
| addAccount: "アカウントを追加" | addAccount: "アカウントを追加" | ||||||
| loginFailed: "ログインに失敗しました" | loginFailed: "ログインに失敗しました" | ||||||
|  |  | ||||||
							
								
								
									
										15
									
								
								packages/backend/migration/1629833361000-AddShowTLReplies.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packages/backend/migration/1629833361000-AddShowTLReplies.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | ||||||
|  | "use strict"; | ||||||
|  | Object.defineProperty(exports, "__esModule", { value: true }); | ||||||
|  | class addShowTLReplies1629833361000 { | ||||||
|  | 	constructor() { | ||||||
|  | 		this.name = 'addShowTLReplies1629833361000'; | ||||||
|  | 	} | ||||||
|  | 	async up(queryRunner) { | ||||||
|  | 		await queryRunner.query(`ALTER TABLE "user" ADD "showTimelineReplies" boolean NOT NULL DEFAULT false`); | ||||||
|  | 		await queryRunner.query(`COMMENT ON COLUMN "user"."showTimelineReplies" IS 'Whether to show users replying to other users in the timeline.'`); | ||||||
|  | 	} | ||||||
|  | 	async down(queryRunner) { | ||||||
|  | 		await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "showTimelineReplies"`); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | exports.addShowTLReplies1629833361000 = addShowTLReplies1629833361000; | ||||||
|  | @ -225,6 +225,12 @@ export class User { | ||||||
| 	}) | 	}) | ||||||
| 	public followersUri: string | null; | 	public followersUri: string | null; | ||||||
| 
 | 
 | ||||||
|  | 	@Column('boolean', { | ||||||
|  | 		default: false, | ||||||
|  | 		comment: 'Whether to show users replying to other users in the timeline' | ||||||
|  | 	}) | ||||||
|  | 	public showTimelineReplies: boolean; | ||||||
|  | 
 | ||||||
| 	@Index({ unique: true }) | 	@Index({ unique: true }) | ||||||
| 	@Column('char', { | 	@Column('char', { | ||||||
| 		length: 16, nullable: true, unique: true, | 		length: 16, nullable: true, unique: true, | ||||||
|  |  | ||||||
|  | @ -220,6 +220,7 @@ export class UserRepository extends Repository<User> { | ||||||
| 			isModerator: user.isModerator || falsy, | 			isModerator: user.isModerator || falsy, | ||||||
| 			isBot: user.isBot || falsy, | 			isBot: user.isBot || falsy, | ||||||
| 			isCat: user.isCat || falsy, | 			isCat: user.isCat || falsy, | ||||||
|  | 			showTimelineReplies: user.showTimelineReplies || falsy, | ||||||
| 			instance: user.host ? Instances.findOne({ host: user.host }).then(instance => instance ? { | 			instance: user.host ? Instances.findOne({ host: user.host }).then(instance => instance ? { | ||||||
| 				name: instance.name, | 				name: instance.name, | ||||||
| 				softwareName: instance.softwareName, | 				softwareName: instance.softwareName, | ||||||
|  |  | ||||||
|  | @ -164,6 +164,7 @@ export async function createPerson(uri: string, resolver?: Resolver): Promise<Us | ||||||
| 				tags, | 				tags, | ||||||
| 				isBot, | 				isBot, | ||||||
| 				isCat: (person as any).isCat === true, | 				isCat: (person as any).isCat === true, | ||||||
|  | 				showTimelineReplies: false, | ||||||
| 			})) as IRemoteUser; | 			})) as IRemoteUser; | ||||||
| 
 | 
 | ||||||
| 			await transactionalEntityManager.save(new UserProfile({ | 			await transactionalEntityManager.save(new UserProfile({ | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import { User } from '@/models/entities/user'; | import { User } from '@/models/entities/user'; | ||||||
| import { Brackets, SelectQueryBuilder } from 'typeorm'; | import { Brackets, SelectQueryBuilder } from 'typeorm'; | ||||||
| 
 | 
 | ||||||
| export function generateRepliesQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null) { | export function generateRepliesQuery(q: SelectQueryBuilder<any>, me?: Pick<User, 'id' | 'showTimelineReplies'> | null) { | ||||||
| 	if (me == null) { | 	if (me == null) { | ||||||
| 		q.andWhere(new Brackets(qb => { qb | 		q.andWhere(new Brackets(qb => { qb | ||||||
| 			.where(`note.replyId IS NULL`) // 返信ではない
 | 			.where(`note.replyId IS NULL`) // 返信ではない
 | ||||||
|  | @ -10,7 +10,7 @@ export function generateRepliesQuery(q: SelectQueryBuilder<any>, me?: { id: User | ||||||
| 				.andWhere('note.replyUserId = note.userId'); | 				.andWhere('note.replyUserId = note.userId'); | ||||||
| 			})); | 			})); | ||||||
| 		})); | 		})); | ||||||
| 	} else { | 	} else if(!me.showTimelineReplies) { | ||||||
| 		q.andWhere(new Brackets(qb => { qb | 		q.andWhere(new Brackets(qb => { qb | ||||||
| 			.where(`note.replyId IS NULL`) // 返信ではない
 | 			.where(`note.replyId IS NULL`) // 返信ではない
 | ||||||
| 			.orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信
 | 			.orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信
 | ||||||
|  |  | ||||||
|  | @ -96,6 +96,10 @@ export const meta = { | ||||||
| 			validator: $.optional.bool, | 			validator: $.optional.bool, | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | 		showTimelineReplies: { | ||||||
|  | 			validator: $.optional.bool, | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
| 		injectFeaturedNote: { | 		injectFeaturedNote: { | ||||||
| 			validator: $.optional.bool, | 			validator: $.optional.bool, | ||||||
| 		}, | 		}, | ||||||
|  | @ -197,6 +201,7 @@ export default define(meta, async (ps, _user, token) => { | ||||||
| 	if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; | 	if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; | ||||||
| 	if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; | 	if (typeof ps.publicReactions === 'boolean') profileUpdates.publicReactions = ps.publicReactions; | ||||||
| 	if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; | 	if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; | ||||||
|  | 	if (typeof ps.showTimelineReplies === 'boolean') updates.showTimelineReplies = ps.showTimelineReplies; | ||||||
| 	if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; | 	if (typeof ps.carefulBot === 'boolean') profileUpdates.carefulBot = ps.carefulBot; | ||||||
| 	if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; | 	if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; | ||||||
| 	if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; | 	if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; | ||||||
|  |  | ||||||
|  | @ -43,7 +43,7 @@ export default class extends Channel { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// 関係ない返信は除外
 | 		// 関係ない返信は除外
 | ||||||
| 		if (note.reply) { | 		if (note.reply && !this.user!.showTimelineReplies) { | ||||||
| 			const reply = note.reply; | 			const reply = note.reply; | ||||||
| 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | ||||||
| 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ export default class extends Channel { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// 関係ない返信は除外
 | 		// 関係ない返信は除外
 | ||||||
| 		if (note.reply) { | 		if (note.reply && !this.user!.showTimelineReplies) { | ||||||
| 			const reply = note.reply; | 			const reply = note.reply; | ||||||
| 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | ||||||
| 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | ||||||
|  |  | ||||||
|  | @ -62,7 +62,7 @@ export default class extends Channel { | ||||||
| 		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return; | 		if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return; | ||||||
| 
 | 
 | ||||||
| 		// 関係ない返信は除外
 | 		// 関係ない返信は除外
 | ||||||
| 		if (note.reply) { | 		if (note.reply && !this.user!.showTimelineReplies) { | ||||||
| 			const reply = note.reply; | 			const reply = note.reply; | ||||||
| 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | ||||||
| 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | ||||||
|  |  | ||||||
|  | @ -43,7 +43,7 @@ export default class extends Channel { | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// 関係ない返信は除外
 | 		// 関係ない返信は除外
 | ||||||
| 		if (note.reply) { | 		if (note.reply && !this.user!.showTimelineReplies) { | ||||||
| 			const reply = note.reply; | 			const reply = note.reply; | ||||||
| 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | 			// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
 | ||||||
| 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | 			if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; | ||||||
|  |  | ||||||
|  | @ -38,7 +38,7 @@ | ||||||
| 	</FormSlot> | 	</FormSlot> | ||||||
| 
 | 
 | ||||||
| 	<FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch> | 	<FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch> | ||||||
| 
 | 	<FormSwitch v-model="profile.showTimelineReplies" class="_formBlock">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }}</template></FormSwitch> | ||||||
| 	<FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch> | 	<FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch> | ||||||
| 
 | 
 | ||||||
| 	<FormSwitch v-model="profile.alwaysMarkNsfw" class="_formBlock">{{ i18n.ts.alwaysMarkSensitive }}</FormSwitch> | 	<FormSwitch v-model="profile.alwaysMarkNsfw" class="_formBlock">{{ i18n.ts.alwaysMarkSensitive }}</FormSwitch> | ||||||
|  | @ -68,6 +68,7 @@ const profile = reactive({ | ||||||
| 	lang: $i.lang, | 	lang: $i.lang, | ||||||
| 	isBot: $i.isBot, | 	isBot: $i.isBot, | ||||||
| 	isCat: $i.isCat, | 	isCat: $i.isCat, | ||||||
|  | 	showTimelineReplies: $i.showTimelineReplies, | ||||||
| 	alwaysMarkNsfw: $i.alwaysMarkNsfw, | 	alwaysMarkNsfw: $i.alwaysMarkNsfw, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|  | @ -97,6 +98,7 @@ function save() { | ||||||
| 		lang: profile.lang || null, | 		lang: profile.lang || null, | ||||||
| 		isBot: !!profile.isBot, | 		isBot: !!profile.isBot, | ||||||
| 		isCat: !!profile.isCat, | 		isCat: !!profile.isCat, | ||||||
|  | 		showTimelineReplies: !!profile.showTimelineReplies, | ||||||
| 		alwaysMarkNsfw: !!profile.alwaysMarkNsfw, | 		alwaysMarkNsfw: !!profile.alwaysMarkNsfw, | ||||||
| 	}); | 	}); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue