Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

新規にフォローした人のwithRepliesをtrueにする機能を追加 #12048

Merged
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9f5ab14
feat: add defaultWithReplies to MiUser
anatawa12 Oct 16, 2023
846994b
feat: use defaultWithReplies when creating MiFollowing
anatawa12 Oct 16, 2023
95e3cee
feat: update defaultWithReplies from API
anatawa12 Oct 16, 2023
f2cc4fe
feat: return defaultWithReplies as a part of $i
anatawa12 Oct 16, 2023
1b571c8
feat(frontend): configure defaultWithReplies
anatawa12 Oct 16, 2023
758c46e
docs(changelog): 新規にフォローした人のをデフォルトでTL二追加できるように
anatawa12 Oct 16, 2023
8054ce9
Merge branch 'develop' into with-replies-for-newly-followed-users
anatawa12 Oct 16, 2023
7351c84
fix: typo
Sayamame-beans Oct 16, 2023
680517f
style: fix lint failure
anatawa12 Oct 16, 2023
3bccba5
chore: improve UI text
syuilo Oct 16, 2023
8e3e5ae
chore: make optional params of UserFollowingService.follow() object
anatawa12 Oct 16, 2023
c183736
chore: UserFollowingService.follow() accept withReplies
anatawa12 Oct 16, 2023
e289eec
chore: add withReplies to MiFollowRequest
anatawa12 Oct 16, 2023
6e2b432
chore: process withReplies for follow request
anatawa12 Oct 16, 2023
0a2befa
feat: accept withReplies on 'following/create' endpoint
anatawa12 Oct 16, 2023
aa39b77
feat: store defaultWithReplies in client store
anatawa12 Oct 16, 2023
03b2004
Revert "feat: return defaultWithReplies as a part of $i"
anatawa12 Oct 16, 2023
7eac656
Revert "feat: update defaultWithReplies from API"
anatawa12 Oct 16, 2023
2002af6
Revert "feat: add defaultWithReplies to MiUser"
anatawa12 Oct 16, 2023
d436f8e
feat: configuring withReplies in import-following
anatawa12 Oct 16, 2023
2862d36
feat(frontend): configure withReplies
anatawa12 Oct 16, 2023
9a94194
fix(frontend): incorrectly showRepliesToOthersInTimeline can be shown
anatawa12 Oct 16, 2023
da5a0b5
fix(backend): withReplies of following/create not working
anatawa12 Oct 16, 2023
bc554fb
fix(frontend): importFollowing error
anatawa12 Oct 16, 2023
29c848a
fix: withReplies is not working with follow import
anatawa12 Oct 16, 2023
dd5dfe2
Merge branch 'develop' into with-replies-for-newly-followed-users
anatawa12 Oct 16, 2023
9284002
fix(frontend): use v-model
anatawa12 Oct 16, 2023
491a832
Merge branch 'develop' into with-replies-for-newly-followed-users
anatawa12 Oct 16, 2023
148beb6
style: fix lint
anatawa12 Oct 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

### General
- Feat: アンテナでローカルの投稿のみ収集できるようになりました
- Enhance: 新規にフォローした人のをデフォルトでTLに追加できるように

### Client
- Enhance: TLの返信表示オプションを記憶するように
Expand Down
2 changes: 2 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,7 @@ export interface Locale {
"deleteAll": string;
"showFixedPostForm": string;
"showFixedPostFormInChannel": string;
"withRepliesByDefaultForNewlyFollowed": string;
"newNoteRecived": string;
"sounds": string;
"sound": string;
Expand Down Expand Up @@ -2051,6 +2052,7 @@ export interface Locale {
"userLists": string;
"excludeMutingUsers": string;
"excludeInactiveUsers": string;
"withReplies": string;
};
"_charts": {
"federation": string;
Expand Down
2 changes: 2 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ serverLogs: "サーバーログ"
deleteAll: "全て削除"
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)"
withRepliesByDefaultForNewlyFollowed: "フォローする際、デフォルトで返信をTLに含むようにする"
newNoteRecived: "新しいノートがあります"
sounds: "サウンド"
sound: "サウンド"
Expand Down Expand Up @@ -1966,6 +1967,7 @@ _exportOrImport:
userLists: "リスト"
excludeMutingUsers: "ミュートしているユーザーを除外"
excludeInactiveUsers: "使われていないアカウントを除外"
withReplies: "インポートした人による返信をTLに含むようにする"

_charts:
federation: "連合"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/


export class FollowRequestWithReplies1697441463087 {
name = 'FollowRequestWithReplies1697441463087'

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "follow_request" ADD "withReplies" boolean NOT NULL DEFAULT false`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "follow_request" DROP COLUMN "withReplies"`);
}
}
10 changes: 6 additions & 4 deletions packages/backend/src/core/QueueService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,19 +237,20 @@ export class QueueService {
}

@bindThis
public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id']) {
public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id'], withReplies?: boolean) {
return this.dbQueue.add('importFollowing', {
user: { id: user.id },
fileId: fileId,
withReplies,
}, {
removeOnComplete: true,
removeOnFail: true,
});
}

@bindThis
public createImportFollowingToDbJob(user: ThinUser, targets: string[]) {
const jobs = targets.map(rel => this.generateToDbJobData('importFollowingToDb', { user, target: rel }));
public createImportFollowingToDbJob(user: ThinUser, targets: string[], withReplies?: boolean) {
const jobs = targets.map(rel => this.generateToDbJobData('importFollowingToDb', { user, target: rel, withReplies }));
return this.dbQueue.addBulk(jobs);
}

Expand Down Expand Up @@ -342,7 +343,7 @@ export class QueueService {
}

@bindThis
public createFollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string, silent?: boolean }[]) {
public createFollowJob(followings: { from: ThinUser, to: ThinUser, requestId?: string, silent?: boolean, withReplies?: boolean }[]) {
const jobs = followings.map(rel => this.generateRelationshipJobData('follow', rel));
return this.relationshipQueue.addBulk(jobs);
}
Expand Down Expand Up @@ -384,6 +385,7 @@ export class QueueService {
to: { id: data.to.id },
silent: data.silent,
requestId: data.requestId,
withReplies: data.withReplies,
},
opts: {
removeOnComplete: true,
Expand Down
20 changes: 16 additions & 4 deletions packages/backend/src/core/UserFollowingService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,15 @@ export class UserFollowingService implements OnModuleInit {
}

@bindThis
public async follow(_follower: { id: MiUser['id'] }, _followee: { id: MiUser['id'] }, requestId?: string, silent = false): Promise<void> {
public async follow(
_follower: { id: MiUser['id'] },
_followee: { id: MiUser['id'] },
{ requestId, silent = false, withReplies }: {
requestId?: string,
silent?: boolean,
withReplies?: boolean,
} = {},
): Promise<void> {
const [follower, followee] = await Promise.all([
this.usersRepository.findOneByOrFail({ id: _follower.id }),
this.usersRepository.findOneByOrFail({ id: _followee.id }),
Expand Down Expand Up @@ -168,12 +176,12 @@ export class UserFollowingService implements OnModuleInit {
}

if (!autoAccept) {
await this.createFollowRequest(follower, followee, requestId);
await this.createFollowRequest(follower, followee, requestId, withReplies);
return;
}
}

await this.insertFollowingDoc(followee, follower, silent);
await this.insertFollowingDoc(followee, follower, silent, withReplies);

if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee, requestId), followee));
Expand All @@ -190,6 +198,7 @@ export class UserFollowingService implements OnModuleInit {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']
},
silent = false,
withReplies?: boolean,
): Promise<void> {
if (follower.id === followee.id) return;

Expand All @@ -199,6 +208,7 @@ export class UserFollowingService implements OnModuleInit {
id: this.idService.gen(),
followerId: follower.id,
followeeId: followee.id,
withReplies: withReplies,

// 非正規化
followerHost: follower.host,
Expand Down Expand Up @@ -451,6 +461,7 @@ export class UserFollowingService implements OnModuleInit {
id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
},
requestId?: string,
withReplies?: boolean,
): Promise<void> {
if (follower.id === followee.id) return;

Expand All @@ -468,6 +479,7 @@ export class UserFollowingService implements OnModuleInit {
followerId: follower.id,
followeeId: followee.id,
requestId,
withReplies,

// 非正規化
followerHost: follower.host,
Expand Down Expand Up @@ -552,7 +564,7 @@ export class UserFollowingService implements OnModuleInit {
throw new IdentifiableError('8884c2dd-5795-4ac9-b27e-6a01d38190f9', 'No follow request.');
}

await this.insertFollowingDoc(followee, follower);
await this.insertFollowingDoc(followee, follower, false, request.withReplies);

if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee));
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/core/activitypub/ApInboxService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export class ApInboxService {
}

// don't queue because the sender may attempt again when timeout
await this.userFollowingService.follow(actor, followee, activity.id);
await this.userFollowingService.follow(actor, followee, { requestId: activity.id });
return 'ok';
}

Expand Down
5 changes: 5 additions & 0 deletions packages/backend/src/models/FollowRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export class MiFollowRequest {
})
public requestId: string | null;

@Column('boolean', {
default: false,
})
public withReplies: boolean;

//#region Denormalized fields
@Column('varchar', {
length: 128, nullable: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class ImportFollowingProcessorService {

const csv = await this.downloadService.downloadTextFile(file.url);
const targets = csv.trim().split('\n');
this.queueService.createImportFollowingToDbJob({ id: user.id }, targets);
this.queueService.createImportFollowingToDbJob({ id: user.id }, targets, job.data.withReplies);

this.logger.succ('Import jobs created');
}
Expand Down Expand Up @@ -93,9 +93,9 @@ export class ImportFollowingProcessorService {
// skip myself
if (target.id === job.data.user.id) return;

this.logger.info(`Follow ${target.id} ...`);
this.logger.info(`Follow ${target.id} ${job.data.withReplies ? 'with replies' : 'without replies'} ...`);

this.queueService.createFollowJob([{ from: user, to: { id: target.id }, silent: true }]);
this.queueService.createFollowJob([{ from: user, to: { id: target.id }, silent: true, withReplies: job.data.withReplies }]);
} catch (e) {
this.logger.warn(`Error: ${e}`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ export class RelationshipProcessorService {

@bindThis
public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> {
this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id}`);
await this.userFollowingService.follow(job.data.from, job.data.to, job.data.requestId, job.data.silent);
this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`);
await this.userFollowingService.follow(job.data.from, job.data.to, {
requestId: job.data.requestId,
silent: job.data.silent,
withReplies: job.data.withReplies,
});
return 'ok';
}

Expand Down
3 changes: 3 additions & 0 deletions packages/backend/src/queue/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export type RelationshipJobData = {
to: ThinUser;
silent?: boolean;
requestId?: string;
withReplies?: boolean;
}

export type DbJobData<T extends keyof DbJobMap> = DbJobMap[T];
Expand Down Expand Up @@ -79,6 +80,7 @@ export type DbUserDeleteJobData = {
export type DbUserImportJobData = {
user: ThinUser;
fileId: MiDriveFile['id'];
withReplies?: boolean;
};

export type DBAntennaImportJobData = {
Expand All @@ -89,6 +91,7 @@ export type DBAntennaImportJobData = {
export type DbUserImportToDbJobData = {
user: ThinUser;
target: string;
withReplies?: boolean;
};

export type ObjectStorageJobData = ObjectStorageFileJobData | Record<string, unknown>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const paramDef = {
type: 'object',
properties: {
userId: { type: 'string', format: 'misskey:id' },
withReplies: { type: 'boolean' }
},
required: ['userId'],
} as const;
Expand Down Expand Up @@ -112,7 +113,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
}

try {
await this.userFollowingService.follow(follower, followee);
await this.userFollowingService.follow(follower, followee, { withReplies: ps.withReplies });
} catch (e) {
if (e instanceof IdentifiableError) {
if (e.id === '710e8fb0-b8c3-4922-be49-d5d93d8e6a6e') throw new ApiError(meta.errors.blocking);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const paramDef = {
type: 'object',
properties: {
fileId: { type: 'string', format: 'misskey:id' },
withReplies: { type: 'boolean' },
},
required: ['fileId'],
} as const;
Expand Down Expand Up @@ -79,7 +80,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
);
if (checkMoving ? file.size > 32 * 1024 * 1024 : file.size > 64 * 1024) throw new ApiError(meta.errors.tooBigFile);

this.queueService.createImportFollowingJob(me, file.id);
this.queueService.createImportFollowingJob(me, file.id, ps.withReplies);
});
}
}
3 changes: 3 additions & 0 deletions packages/frontend/src/components/MkFollowButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/scripts/achievements.js';
import { $i } from '@/account.js';
import { defaultStore } from "@/store.js";

const props = withDefaults(defineProps<{
user: Misskey.entities.UserDetailed,
Expand Down Expand Up @@ -95,7 +96,9 @@
} else {
await os.api('following/create', {
userId: props.user.id,
withReplies: defaultStore.state.defaultWithReplies,
});
props.user.withReplies = defaultStore.state.defaultWithReplies;

Check failure on line 101 in packages/frontend/src/components/MkFollowButton.vue

View workflow job for this annotation

GitHub Actions / lint (frontend)

Unexpected mutation of "user" prop
anatawa12 marked this conversation as resolved.
Show resolved Hide resolved
hasPendingFollowRequestFromYou = true;

claimAchievement('following1');
Expand Down
3 changes: 3 additions & 0 deletions packages/frontend/src/pages/follow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import * as Misskey from 'misskey-js';
import * as os from '@/os.js';
import { mainRouter } from '@/router.js';
import { i18n } from '@/i18n.js';
import { defaultStore } from "@/store.js";

async function follow(user): Promise<void> {
const { canceled } = await os.confirm({
Expand All @@ -28,7 +29,9 @@ async function follow(user): Promise<void> {

os.apiWithDialog('following/create', {
userId: user.id,
withReplies: defaultStore.state.defaultWithReplies,
});
user.withReplies = defaultStore.state.defaultWithReplies;
}

const acct = new URL(location.href).searchParams.get('acct');
Expand Down
2 changes: 2 additions & 0 deletions packages/frontend/src/pages/settings/general.vue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

クライアント設定の中にあるのは違うかも(他に良い場所は思いつかないけど)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

若干違和感はありますが元々のshowPreviewを一応参考にしました

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_s">
<MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch>
<MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch>
<MkSwitch v-model="defaultWithReplies">{{ i18n.ts.withRepliesByDefaultForNewlyFollowed }}</MkSwitch>
<MkFolder>
<template #label>{{ i18n.ts.pinnedList }}</template>
<!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ -->
Expand Down Expand Up @@ -249,6 +250,7 @@ const mediaListWithOneImageAppearance = computed(defaultStore.makeGetterSetter('
const notificationPosition = computed(defaultStore.makeGetterSetter('notificationPosition'));
const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis'));
const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn'));
const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies'));

watch(lang, () => {
miLocalStorage.setItem('lang', lang.value as string);
Expand Down
10 changes: 9 additions & 1 deletion packages/frontend/src/pages/settings/import-export.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template>
<MkSwitch v-model="withReplies">
{{ i18n.ts._exportOrImport.withReplies }}
</MkSwitch>
<MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
</MkFolder>
</div>
Expand Down Expand Up @@ -118,9 +121,11 @@ import { selectFile } from '@/scripts/select-file.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { $i } from '@/account.js';
import { defaultStore } from "@/store.js";

const excludeMutingUsers = ref(false);
const excludeInactiveUsers = ref(false);
const withReplies = ref(defaultStore.state.defaultWithReplies);

const onExportSuccess = () => {
os.alert({
Expand Down Expand Up @@ -177,7 +182,10 @@ const exportAntennas = () => {

const importFollowing = async (ev) => {
const file = await selectFile(ev.currentTarget ?? ev.target);
os.api('i/import-following', { fileId: file.id }).then(onImportSuccess).catch(onError);
os.api('i/import-following', {
fileId: file.id,
withReplies: withReplies.value,
}).then(onImportSuccess).catch(onError);
};

const importUserLists = async (ev) => {
Expand Down
4 changes: 4 additions & 0 deletions packages/frontend/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: false,
},
defaultWithReplies: {
where: 'account',
default: false,
},
}));

// TODO: 他のタブと永続化されたstateを同期
Expand Down
Loading
Loading