リアクション一覧の公開をオプトインに
This commit is contained in:
		
							parent
							
								
									1bfb176667
								
							
						
					
					
						commit
						7413634734
					
				
					 9 changed files with 51 additions and 4 deletions
				
			
		|  | @ -10,7 +10,8 @@ | ||||||
| ## 12.x.x (unreleased) | ## 12.x.x (unreleased) | ||||||
| 
 | 
 | ||||||
| ### Improvements | ### Improvements | ||||||
| - クライアント: ユーザーのリアクション一覧を見れるように | - クライアント: 自分のリアクション一覧を見れるように | ||||||
|  | 	- 設定により、リアクション一覧を全員に公開することも可能 | ||||||
| - クライアント: ユーザー検索の精度を強化 | - クライアント: ユーザー検索の精度を強化 | ||||||
| - クライアント: 新しいライトテーマを追加 | - クライアント: 新しいライトテーマを追加 | ||||||
| - API: ユーザーのリアクション一覧を取得する users/reactions を追加 | - API: ユーザーのリアクション一覧を取得する users/reactions を追加 | ||||||
|  |  | ||||||
|  | @ -797,6 +797,8 @@ unread: "未読" | ||||||
| filter: "フィルタ" | filter: "フィルタ" | ||||||
| controllPanel: "コントロールパネル" | controllPanel: "コントロールパネル" | ||||||
| manageAccounts: "アカウントを管理" | manageAccounts: "アカウントを管理" | ||||||
|  | makeReactionsPublic: "リアクション一覧を公開する" | ||||||
|  | makeReactionsPublicDescription: "あなたがしたリアクション一覧を誰でも見れるようにします。" | ||||||
| 
 | 
 | ||||||
| _signup: | _signup: | ||||||
|   almostThere: "ほとんど完了です" |   almostThere: "ほとんど完了です" | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								migration/1634486652000-user-public-reactions.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								migration/1634486652000-user-public-reactions.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | import {MigrationInterface, QueryRunner} from "typeorm"; | ||||||
|  | 
 | ||||||
|  | export class userPublicReactions1634486652000 implements MigrationInterface { | ||||||
|  |     name = 'userPublicReactions1634486652000' | ||||||
|  | 
 | ||||||
|  |     public async up(queryRunner: QueryRunner): Promise<void> { | ||||||
|  | 			await queryRunner.query(`ALTER TABLE "user_profile" ADD "publicReactions" boolean NOT NULL DEFAULT false`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public async down(queryRunner: QueryRunner): Promise<void> { | ||||||
|  |         await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "publicReactions"`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -5,6 +5,10 @@ | ||||||
| 		<FormSwitch v-model="autoAcceptFollowed" :disabled="!isLocked" @update:modelValue="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch> | 		<FormSwitch v-model="autoAcceptFollowed" :disabled="!isLocked" @update:modelValue="save()">{{ $ts.autoAcceptFollowed }}</FormSwitch> | ||||||
| 		<template #caption>{{ $ts.lockedAccountInfo }}</template> | 		<template #caption>{{ $ts.lockedAccountInfo }}</template> | ||||||
| 	</FormGroup> | 	</FormGroup> | ||||||
|  | 	<FormSwitch v-model="publicReactions" @update:modelValue="save()"> | ||||||
|  | 		{{ $ts.makeReactionsPublic }} | ||||||
|  | 		<template #desc>{{ $ts.makeReactionsPublicDescription }}</template> | ||||||
|  | 	</FormSwitch> | ||||||
| 	<FormSwitch v-model="hideOnlineStatus" @update:modelValue="save()"> | 	<FormSwitch v-model="hideOnlineStatus" @update:modelValue="save()"> | ||||||
| 		{{ $ts.hideOnlineStatus }} | 		{{ $ts.hideOnlineStatus }} | ||||||
| 		<template #desc>{{ $ts.hideOnlineStatusDescription }}</template> | 		<template #desc>{{ $ts.hideOnlineStatusDescription }}</template> | ||||||
|  | @ -64,6 +68,7 @@ export default defineComponent({ | ||||||
| 			noCrawle: false, | 			noCrawle: false, | ||||||
| 			isExplorable: false, | 			isExplorable: false, | ||||||
| 			hideOnlineStatus: false, | 			hideOnlineStatus: false, | ||||||
|  | 			publicReactions: false, | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
|  | @ -80,6 +85,7 @@ export default defineComponent({ | ||||||
| 		this.noCrawle = this.$i.noCrawle; | 		this.noCrawle = this.$i.noCrawle; | ||||||
| 		this.isExplorable = this.$i.isExplorable; | 		this.isExplorable = this.$i.isExplorable; | ||||||
| 		this.hideOnlineStatus = this.$i.hideOnlineStatus; | 		this.hideOnlineStatus = this.$i.hideOnlineStatus; | ||||||
|  | 		this.publicReactions = this.$i.publicReactions; | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	mounted() { | 	mounted() { | ||||||
|  | @ -94,6 +100,7 @@ export default defineComponent({ | ||||||
| 				noCrawle: !!this.noCrawle, | 				noCrawle: !!this.noCrawle, | ||||||
| 				isExplorable: !!this.isExplorable, | 				isExplorable: !!this.isExplorable, | ||||||
| 				hideOnlineStatus: !!this.hideOnlineStatus, | 				hideOnlineStatus: !!this.hideOnlineStatus, | ||||||
|  | 				publicReactions: !!this.publicReactions, | ||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -270,12 +270,12 @@ export default defineComponent({ | ||||||
| 					title: this.$ts.overview, | 					title: this.$ts.overview, | ||||||
| 					icon: 'fas fa-home', | 					icon: 'fas fa-home', | ||||||
| 					onClick: () => { this.$router.push('/@' + getAcct(this.user)); }, | 					onClick: () => { this.$router.push('/@' + getAcct(this.user)); }, | ||||||
| 				}, { | 				}, ...(this.$i && (this.$i.id === this.user.id)) || this.user.publicReactions ? [{ | ||||||
| 					active: this.page === 'reactions', | 					active: this.page === 'reactions', | ||||||
| 					title: this.$ts.reaction, | 					title: this.$ts.reaction, | ||||||
| 					icon: 'fas fa-laugh', | 					icon: 'fas fa-laugh', | ||||||
| 					onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/reactions'); }, | 					onClick: () => { this.$router.push('/@' + getAcct(this.user) + '/reactions'); }, | ||||||
| 				}, { | 				}] : [], { | ||||||
| 					active: this.page === 'clips', | 					active: this.page === 'clips', | ||||||
| 					title: this.$ts.clips, | 					title: this.$ts.clips, | ||||||
| 					icon: 'fas fa-paperclip', | 					icon: 'fas fa-paperclip', | ||||||
|  |  | ||||||
|  | @ -75,6 +75,11 @@ export class UserProfile { | ||||||
| 	}) | 	}) | ||||||
| 	public emailNotificationTypes: string[]; | 	public emailNotificationTypes: string[]; | ||||||
| 
 | 
 | ||||||
|  | 	@Column('boolean', { | ||||||
|  | 		default: false, | ||||||
|  | 	}) | ||||||
|  | 	public publicReactions: boolean; | ||||||
|  | 
 | ||||||
| 	@Column('varchar', { | 	@Column('varchar', { | ||||||
| 		length: 128, nullable: true, | 		length: 128, nullable: true, | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
|  | @ -231,6 +231,7 @@ export class UserRepository extends Repository<User> { | ||||||
| 				}), | 				}), | ||||||
| 				pinnedPageId: profile!.pinnedPageId, | 				pinnedPageId: profile!.pinnedPageId, | ||||||
| 				pinnedPage: profile!.pinnedPageId ? Pages.pack(profile!.pinnedPageId, me) : null, | 				pinnedPage: profile!.pinnedPageId ? Pages.pack(profile!.pinnedPageId, me) : null, | ||||||
|  | 				publicReactions: profile!.publicReactions, | ||||||
| 				twoFactorEnabled: profile!.twoFactorEnabled, | 				twoFactorEnabled: profile!.twoFactorEnabled, | ||||||
| 				usePasswordLessLogin: profile!.usePasswordLessLogin, | 				usePasswordLessLogin: profile!.usePasswordLessLogin, | ||||||
| 				securityKeys: profile!.twoFactorEnabled | 				securityKeys: profile!.twoFactorEnabled | ||||||
|  |  | ||||||
|  | @ -68,6 +68,10 @@ export const meta = { | ||||||
| 			validator: $.optional.bool, | 			validator: $.optional.bool, | ||||||
| 		}, | 		}, | ||||||
| 
 | 
 | ||||||
|  | 		publicReactions: { | ||||||
|  | 			validator: $.optional.bool, | ||||||
|  | 		}, | ||||||
|  | 
 | ||||||
| 		carefulBot: { | 		carefulBot: { | ||||||
| 			validator: $.optional.bool, | 			validator: $.optional.bool, | ||||||
| 		}, | 		}, | ||||||
|  | @ -180,6 +184,7 @@ export default define(meta, async (ps, _user, token) => { | ||||||
| 	if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; | 	if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; | ||||||
| 	if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; | 	if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; | ||||||
| 	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.isBot === 'boolean') updates.isBot = ps.isBot; | 	if (typeof ps.isBot === 'boolean') updates.isBot = ps.isBot; | ||||||
| 	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; | ||||||
|  |  | ||||||
|  | @ -1,9 +1,10 @@ | ||||||
| import $ from 'cafy'; | import $ from 'cafy'; | ||||||
| import { ID } from '@/misc/cafy-id'; | import { ID } from '@/misc/cafy-id'; | ||||||
| import define from '../../define'; | import define from '../../define'; | ||||||
| import { NoteReactions } from '@/models/index'; | import { NoteReactions, UserProfiles } from '@/models/index'; | ||||||
| import { makePaginationQuery } from '../../common/make-pagination-query'; | import { makePaginationQuery } from '../../common/make-pagination-query'; | ||||||
| import { generateVisibilityQuery } from '../../common/generate-visibility-query'; | import { generateVisibilityQuery } from '../../common/generate-visibility-query'; | ||||||
|  | import { ApiError } from '../../error'; | ||||||
| 
 | 
 | ||||||
| export const meta = { | export const meta = { | ||||||
| 	tags: ['users', 'reactions'], | 	tags: ['users', 'reactions'], | ||||||
|  | @ -48,10 +49,21 @@ export const meta = { | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	errors: { | 	errors: { | ||||||
|  | 		reactionsNotPublic: { | ||||||
|  | 			message: 'Reactions of the user is not public.', | ||||||
|  | 			code: 'REACTIONS_NOT_PUBLIC', | ||||||
|  | 			id: '673a7dd2-6924-1093-e0c0-e68456ceae5c' | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default define(meta, async (ps, me) => { | export default define(meta, async (ps, me) => { | ||||||
|  | 	const profile = await UserProfiles.findOneOrFail(ps.userId); | ||||||
|  | 
 | ||||||
|  | 	if (me == null || (me.id !== ps.userId && !profile.publicReactions)) { | ||||||
|  | 		throw new ApiError(meta.errors.reactionsNotPublic); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	const query = makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), | 	const query = makePaginationQuery(NoteReactions.createQueryBuilder('reaction'), | ||||||
| 			ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) | 			ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) | ||||||
| 		.andWhere(`reaction.userId = :userId`, { userId: ps.userId }) | 		.andWhere(`reaction.userId = :userId`, { userId: ps.userId }) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue