リアクション一覧の公開をオプトインに

This commit is contained in:
syuilo 2021-10-18 01:16:59 +09:00
parent 1bfb176667
commit 7413634734
9 changed files with 51 additions and 4 deletions

View file

@ -10,7 +10,8 @@
## 12.x.x (unreleased) ## 12.x.x (unreleased)
### Improvements ### Improvements
- クライアント: ユーザーのリアクション一覧を見れるように - クライアント: 自分のリアクション一覧を見れるように
- 設定により、リアクション一覧を全員に公開することも可能
- クライアント: ユーザー検索の精度を強化 - クライアント: ユーザー検索の精度を強化
- クライアント: 新しいライトテーマを追加 - クライアント: 新しいライトテーマを追加
- API: ユーザーのリアクション一覧を取得する users/reactions を追加 - API: ユーザーのリアクション一覧を取得する users/reactions を追加

View file

@ -797,6 +797,8 @@ unread: "未読"
filter: "フィルタ" filter: "フィルタ"
controllPanel: "コントロールパネル" controllPanel: "コントロールパネル"
manageAccounts: "アカウントを管理" manageAccounts: "アカウントを管理"
makeReactionsPublic: "リアクション一覧を公開する"
makeReactionsPublicDescription: "あなたがしたリアクション一覧を誰でも見れるようにします。"
_signup: _signup:
almostThere: "ほとんど完了です" almostThere: "ほとんど完了です"

View 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"`);
}
}

View file

@ -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,
}); });
} }
} }

View file

@ -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',

View file

@ -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,
}) })

View file

@ -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

View file

@ -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;

View file

@ -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 })