リアクション一覧の公開をオプトインに
This commit is contained in:
parent
1bfb176667
commit
7413634734
|
@ -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…
Reference in a new issue