Skip to content

Commit b6a5a36

Browse files
authored
feat(profile): 相互リンク機能の追加 (#675)
1 parent b059162 commit b6a5a36

37 files changed

+1130
-20
lines changed

locales/index.d.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2400,6 +2400,14 @@ export interface Locale extends ILocale {
24002400
* バナーを解除しますか?
24012401
*/
24022402
"unsetUserBannerConfirm": string;
2403+
/**
2404+
* 相互バナーを解除
2405+
*/
2406+
"unsetUserMutualBanner": string;
2407+
/**
2408+
* 相互バナーを解除しますか?
2409+
*/
2410+
"unsetUserMutualBannerConfirm": string;
24032411
/**
24042412
* すべてのファイルを削除
24052413
*/
@@ -5083,6 +5091,18 @@ export interface Locale extends ILocale {
50835091
* こちら
50845092
*/
50855093
"here": string;
5094+
/**
5095+
* 相互バナー
5096+
*/
5097+
"mutualBanner": string;
5098+
/**
5099+
* このユーザーのバナー
5100+
*/
5101+
"mutualBannerThisUser": string;
5102+
/**
5103+
* 最大
5104+
*/
5105+
"maximum": string;
50865106
"_bubbleGame": {
50875107
/**
50885108
* 遊び方
@@ -8212,6 +8232,10 @@ export interface Locale extends ILocale {
82128232
* ユーザーのバーナーを削除する
82138233
*/
82148234
"write:admin:unset-user-banner": string;
8235+
/**
8236+
* ユーザーの相互バナーを削除する
8237+
*/
8238+
"write:admin:unset-user-mutual-banner": string;
82158239
/**
82168240
* ユーザーの凍結を解除する
82178241
*/
@@ -8798,6 +8822,22 @@ export interface Locale extends ILocale {
87988822
* 最大{max}つまでデコレーションを付けられます。
87998823
*/
88008824
"avatarDecorationMax": ParameterizedString<"max">;
8825+
/**
8826+
* 自身の相互リンクのバナーを設定
8827+
*/
8828+
"myMutualBanner": string;
8829+
/**
8830+
* あなた自身が相互リンクのバナーとして設定してほしい画像を設定することができます。
8831+
*/
8832+
"myMutualBannerDescription": string;
8833+
/**
8834+
* 相互リンクのバナー
8835+
*/
8836+
"mutualBanner": string;
8837+
/**
8838+
* 説明
8839+
*/
8840+
"mutualBannerDescriptionEdit": string;
88018841
};
88028842
"_exportOrImport": {
88038843
/**

locales/ja-JP.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,8 @@ unsetUserAvatar: "アイコンを解除"
596596
unsetUserAvatarConfirm: "アイコンを解除しますか?"
597597
unsetUserBanner: "バナーを解除"
598598
unsetUserBannerConfirm: "バナーを解除しますか?"
599+
unsetUserMutualBanner: "相互バナーを解除"
600+
unsetUserMutualBannerConfirm: "相互バナーを解除しますか?"
599601
deleteAllFiles: "すべてのファイルを削除"
600602
deleteAllFilesConfirm: "すべてのファイルを削除しますか?"
601603
removeAllFollowing: "フォローを全解除"
@@ -1266,6 +1268,9 @@ reportComplete: "通報完了"
12661268
blockThisUser: "このユーザーをブロックする"
12671269
muteThisUser: "このユーザーをミュートする"
12681270
here: "こちら"
1271+
mutualBanner: "相互バナー"
1272+
mutualBannerThisUser: "このユーザーのバナー"
1273+
maximum: "最大"
12691274

12701275
_bubbleGame:
12711276
howToPlay: "遊び方"
@@ -2152,6 +2157,7 @@ _permissions:
21522157
"write:admin:suspend-user": "ユーザーを凍結する"
21532158
"write:admin:unset-user-avatar": "ユーザーのアバターを削除する"
21542159
"write:admin:unset-user-banner": "ユーザーのバーナーを削除する"
2160+
"write:admin:unset-user-mutual-banner": "ユーザーの相互バナーを削除する"
21552161
"write:admin:unsuspend-user": "ユーザーの凍結を解除する"
21562162
"write:admin:meta": "インスタンスのメタデータを操作する"
21572163
"write:admin:user-note": "モデレーションノートを操作する"
@@ -2313,6 +2319,10 @@ _profile:
23132319
changeBanner: "バナー画像を変更"
23142320
verifiedLinkDescription: "内容にURLを設定すると、リンク先のWebサイトに自分のプロフィールへのリンクが含まれている場合に所有者確認済みアイコンを表示させることができます。"
23152321
avatarDecorationMax: "最大{max}つまでデコレーションを付けられます。"
2322+
myMutualBanner: "自身の相互リンクのバナーを設定"
2323+
myMutualBannerDescription: "あなた自身が相互リンクのバナーとして設定してほしい画像を設定することができます。"
2324+
mutualBanner: "相互リンクのバナー"
2325+
mutualBannerDescriptionEdit: "説明"
23162326

23172327
_exportOrImport:
23182328
allNotes: "全てのノート"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
export class MutualBanner1723213482131 {
2+
name = 'MutualBanner1723213482131'
3+
4+
async up(queryRunner) {
5+
await queryRunner.query(`CREATE TABLE "user_banner" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "description" character varying(1024), "url" character varying(1024), "fileId" character varying(32) NOT NULL, CONSTRAINT "PK_0d9a418f048e308dbfb6562149d" PRIMARY KEY ("id"))`);
6+
await queryRunner.query(`CREATE INDEX "IDX_fa06ea2e2375449537ced781f1" ON "user_banner" ("userId") `);
7+
await queryRunner.query(`CREATE TABLE "user_banner_pining" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "pinnedBannerId" character varying(32) NOT NULL, CONSTRAINT "PK_970d24f72e8d2b20f8c21ec5d11" PRIMARY KEY ("id"))`);
8+
await queryRunner.query(`CREATE INDEX "IDX_3b74dc21b68da606011c81609c" ON "user_banner_pining" ("userId") `);
9+
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_7d51b5a8ae859e0023a98837a1" ON "user_banner_pining" ("userId", "pinnedBannerId") `);
10+
await queryRunner.query(`ALTER TABLE "user_banner" ADD CONSTRAINT "FK_fa06ea2e2375449537ced781f15" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
11+
await queryRunner.query(`ALTER TABLE "user_banner" ADD CONSTRAINT "FK_3de9f17cce2c10f6938fb261c0b" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
12+
await queryRunner.query(`ALTER TABLE "user_banner_pining" ADD CONSTRAINT "FK_3b74dc21b68da606011c81609c9" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
13+
await queryRunner.query(`ALTER TABLE "user_banner_pining" ADD CONSTRAINT "FK_d13be8242980f7018d664f780f6" FOREIGN KEY ("pinnedBannerId") REFERENCES "user_banner"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
14+
}
15+
16+
async down(queryRunner) {
17+
await queryRunner.query(`ALTER TABLE "user_banner_pining" DROP CONSTRAINT "FK_d13be8242980f7018d664f780f6"`);
18+
await queryRunner.query(`ALTER TABLE "user_banner_pining" DROP CONSTRAINT "FK_3b74dc21b68da606011c81609c9"`);
19+
await queryRunner.query(`ALTER TABLE "user_banner" DROP CONSTRAINT "FK_3de9f17cce2c10f6938fb261c0b"`);
20+
await queryRunner.query(`ALTER TABLE "user_banner" DROP CONSTRAINT "FK_fa06ea2e2375449537ced781f15"`);
21+
await queryRunner.query(`DROP INDEX "public"."IDX_7d51b5a8ae859e0023a98837a1"`);
22+
await queryRunner.query(`DROP INDEX "public"."IDX_3b74dc21b68da606011c81609c"`);
23+
await queryRunner.query(`DROP TABLE "user_banner_pining"`);
24+
await queryRunner.query(`DROP INDEX "public"."IDX_fa06ea2e2375449537ced781f1"`);
25+
await queryRunner.query(`DROP TABLE "user_banner"`);
26+
}
27+
}

packages/backend/src/core/CoreModule.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import { Module } from '@nestjs/common';
77
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
8+
import { UserBannerEntityService } from '@/core/entities/UserBannerEntityService.js';
9+
import { UserBannerPiningEntityService } from '@/core/entities/UserBannerPiningEntityService.js';
810
import { AccountMoveService } from './AccountMoveService.js';
911
import { AccountUpdateService } from './AccountUpdateService.js';
1012
import { AiService } from './AiService.js';
@@ -35,6 +37,8 @@ import { ModerationLogService } from './ModerationLogService.js';
3537
import { NoteCreateService } from './NoteCreateService.js';
3638
import { NoteDeleteService } from './NoteDeleteService.js';
3739
import { NotePiningService } from './NotePiningService.js';
40+
import { UserBannerPiningService } from './UserBannerPiningService.js';
41+
import { UserBannerService } from './UserBannerService.js';
3842
import { NoteReadService } from './NoteReadService.js';
3943
import { NotificationService } from './NotificationService.js';
4044
import { PollService } from './PollService.js';
@@ -173,6 +177,8 @@ const $ModerationLogService: Provider = { provide: 'ModerationLogService', useEx
173177
const $NoteCreateService: Provider = { provide: 'NoteCreateService', useExisting: NoteCreateService };
174178
const $NoteDeleteService: Provider = { provide: 'NoteDeleteService', useExisting: NoteDeleteService };
175179
const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting: NotePiningService };
180+
const $UserBannerPiningService: Provider = { provide: 'UserBannerPiningService', useExisting: UserBannerPiningService };
181+
const $UserBannerService: Provider = { provide: 'UserBannerService', useExisting: UserBannerService };
176182
const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService };
177183
const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService };
178184
const $PollService: Provider = { provide: 'PollService', useExisting: PollService };
@@ -253,6 +259,8 @@ const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', use
253259
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService };
254260
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService };
255261
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
262+
const $UserBannerEntityService: Provider = { provide: 'UserBannerEntityService', useExisting: UserBannerEntityService };
263+
const $UserBannerPiningEntityService: Provider = { provide: 'UserBannerPiningEntityService', useExisting: UserBannerPiningEntityService };
256264
const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService };
257265
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
258266
const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService };
@@ -315,6 +323,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
315323
NoteCreateService,
316324
NoteDeleteService,
317325
NotePiningService,
326+
UserBannerPiningService,
327+
UserBannerService,
318328
NoteReadService,
319329
NotificationService,
320330
PollService,
@@ -393,6 +403,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
393403
SigninEntityService,
394404
UserEntityService,
395405
UserListEntityService,
406+
UserBannerEntityService,
407+
UserBannerPiningEntityService,
396408
FlashEntityService,
397409
FlashLikeEntityService,
398410
RoleEntityService,
@@ -451,6 +463,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
451463
$NoteCreateService,
452464
$NoteDeleteService,
453465
$NotePiningService,
466+
$UserBannerService,
467+
$UserBannerPiningService,
454468
$NoteReadService,
455469
$NotificationService,
456470
$PollService,
@@ -529,6 +543,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
529543
$SigninEntityService,
530544
$UserEntityService,
531545
$UserListEntityService,
546+
$UserBannerEntityService,
547+
$UserBannerPiningEntityService,
532548
$FlashEntityService,
533549
$FlashLikeEntityService,
534550
$RoleEntityService,
@@ -588,6 +604,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
588604
NoteCreateService,
589605
NoteDeleteService,
590606
NotePiningService,
607+
UserBannerService,
608+
UserBannerPiningService,
591609
NoteReadService,
592610
NotificationService,
593611
PollService,
@@ -665,6 +683,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
665683
SigninEntityService,
666684
UserEntityService,
667685
UserListEntityService,
686+
UserBannerEntityService,
687+
UserBannerPiningEntityService,
668688
FlashEntityService,
669689
FlashLikeEntityService,
670690
RoleEntityService,
@@ -723,6 +743,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
723743
$NoteCreateService,
724744
$NoteDeleteService,
725745
$NotePiningService,
746+
$UserBannerService,
747+
$UserBannerPiningService,
726748
$NoteReadService,
727749
$NotificationService,
728750
$PollService,
@@ -800,6 +822,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
800822
$SigninEntityService,
801823
$UserEntityService,
802824
$UserListEntityService,
825+
$UserBannerEntityService,
826+
$UserBannerPiningEntityService,
803827
$FlashEntityService,
804828
$FlashLikeEntityService,
805829
$RoleEntityService,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Inject, Injectable } from '@nestjs/common';
2+
import { In } from 'typeorm';
3+
import { bindThis } from '@/decorators.js';
4+
import { MiUser } from '@/models/User.js';
5+
import { MiUserBanner } from '@/models/UserBanner.js';
6+
import type { MiUserBannerPining, UserBannerPiningRepository, UserBannerRepository } from '@/models/_.js';
7+
import { DI } from '@/di-symbols.js';
8+
import { IdentifiableError } from '@/misc/identifiable-error.js';
9+
import { IdService } from '@/core/IdService.js';
10+
11+
@Injectable()
12+
export class UserBannerPiningService {
13+
constructor(
14+
@Inject(DI.userBannerRepository)
15+
private userBannerRepository: UserBannerRepository,
16+
@Inject(DI.userBannerPiningRepository)
17+
private userBannerPiningRepository: UserBannerPiningRepository,
18+
19+
private idService: IdService,
20+
) {
21+
22+
}
23+
24+
/**
25+
* 指定したユーザーのバナーをピン留めします
26+
* @param userId
27+
* @param bannerIds
28+
*/
29+
public async addPinned(userId: MiUser['id'], bannerIds: MiUserBanner['id'][]) {
30+
const pinsToInsert = bannerIds.map(bannerId => ({
31+
id: this.idService.gen(),
32+
userId,
33+
pinnedBannerId: bannerId,
34+
} as MiUserBannerPining));
35+
await this.userBannerPiningRepository
36+
.createQueryBuilder()
37+
.insert()
38+
.values(pinsToInsert)
39+
.orIgnore()
40+
.execute();
41+
}
42+
43+
/**
44+
* 指定したユーザーのバナーのピン留めを解除します
45+
* @param userId
46+
* @param bannerIds
47+
*/
48+
@bindThis
49+
public async removePinned(userId:MiUser['id'], bannerIds:MiUserBanner['id'][]) {
50+
await this.userBannerPiningRepository.delete({
51+
userId,
52+
pinnedBannerId: In(bannerIds),
53+
});
54+
}
55+
}

0 commit comments

Comments
 (0)