Skip to content

Commit

Permalink
feat(profile): 相互リンク機能の追加 (#675)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattyatea authored Aug 9, 2024
1 parent b059162 commit b6a5a36
Show file tree
Hide file tree
Showing 37 changed files with 1,130 additions and 20 deletions.
40 changes: 40 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2400,6 +2400,14 @@ export interface Locale extends ILocale {
* バナーを解除しますか?
*/
"unsetUserBannerConfirm": string;
/**
* 相互バナーを解除
*/
"unsetUserMutualBanner": string;
/**
* 相互バナーを解除しますか?
*/
"unsetUserMutualBannerConfirm": string;
/**
* すべてのファイルを削除
*/
Expand Down Expand Up @@ -5083,6 +5091,18 @@ export interface Locale extends ILocale {
* こちら
*/
"here": string;
/**
* 相互バナー
*/
"mutualBanner": string;
/**
* このユーザーのバナー
*/
"mutualBannerThisUser": string;
/**
* 最大
*/
"maximum": string;
"_bubbleGame": {
/**
* 遊び方
Expand Down Expand Up @@ -8212,6 +8232,10 @@ export interface Locale extends ILocale {
* ユーザーのバーナーを削除する
*/
"write:admin:unset-user-banner": string;
/**
* ユーザーの相互バナーを削除する
*/
"write:admin:unset-user-mutual-banner": string;
/**
* ユーザーの凍結を解除する
*/
Expand Down Expand Up @@ -8798,6 +8822,22 @@ export interface Locale extends ILocale {
* 最大{max}つまでデコレーションを付けられます。
*/
"avatarDecorationMax": ParameterizedString<"max">;
/**
* 自身の相互リンクのバナーを設定
*/
"myMutualBanner": string;
/**
* あなた自身が相互リンクのバナーとして設定してほしい画像を設定することができます。
*/
"myMutualBannerDescription": string;
/**
* 相互リンクのバナー
*/
"mutualBanner": string;
/**
* 説明
*/
"mutualBannerDescriptionEdit": string;
};
"_exportOrImport": {
/**
Expand Down
10 changes: 10 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,8 @@ unsetUserAvatar: "アイコンを解除"
unsetUserAvatarConfirm: "アイコンを解除しますか?"
unsetUserBanner: "バナーを解除"
unsetUserBannerConfirm: "バナーを解除しますか?"
unsetUserMutualBanner: "相互バナーを解除"
unsetUserMutualBannerConfirm: "相互バナーを解除しますか?"
deleteAllFiles: "すべてのファイルを削除"
deleteAllFilesConfirm: "すべてのファイルを削除しますか?"
removeAllFollowing: "フォローを全解除"
Expand Down Expand Up @@ -1266,6 +1268,9 @@ reportComplete: "通報完了"
blockThisUser: "このユーザーをブロックする"
muteThisUser: "このユーザーをミュートする"
here: "こちら"
mutualBanner: "相互バナー"
mutualBannerThisUser: "このユーザーのバナー"
maximum: "最大"

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

_exportOrImport:
allNotes: "全てのノート"
Expand Down
27 changes: 27 additions & 0 deletions packages/backend/migration/1723213482131-mutualBanner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export class MutualBanner1723213482131 {
name = 'MutualBanner1723213482131'

async up(queryRunner) {
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"))`);
await queryRunner.query(`CREATE INDEX "IDX_fa06ea2e2375449537ced781f1" ON "user_banner" ("userId") `);
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"))`);
await queryRunner.query(`CREATE INDEX "IDX_3b74dc21b68da606011c81609c" ON "user_banner_pining" ("userId") `);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_7d51b5a8ae859e0023a98837a1" ON "user_banner_pining" ("userId", "pinnedBannerId") `);
await queryRunner.query(`ALTER TABLE "user_banner" ADD CONSTRAINT "FK_fa06ea2e2375449537ced781f15" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
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`);
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`);
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`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "user_banner_pining" DROP CONSTRAINT "FK_d13be8242980f7018d664f780f6"`);
await queryRunner.query(`ALTER TABLE "user_banner_pining" DROP CONSTRAINT "FK_3b74dc21b68da606011c81609c9"`);
await queryRunner.query(`ALTER TABLE "user_banner" DROP CONSTRAINT "FK_3de9f17cce2c10f6938fb261c0b"`);
await queryRunner.query(`ALTER TABLE "user_banner" DROP CONSTRAINT "FK_fa06ea2e2375449537ced781f15"`);
await queryRunner.query(`DROP INDEX "public"."IDX_7d51b5a8ae859e0023a98837a1"`);
await queryRunner.query(`DROP INDEX "public"."IDX_3b74dc21b68da606011c81609c"`);
await queryRunner.query(`DROP TABLE "user_banner_pining"`);
await queryRunner.query(`DROP INDEX "public"."IDX_fa06ea2e2375449537ced781f1"`);
await queryRunner.query(`DROP TABLE "user_banner"`);
}
}
24 changes: 24 additions & 0 deletions packages/backend/src/core/CoreModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import { Module } from '@nestjs/common';
import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js';
import { UserBannerEntityService } from '@/core/entities/UserBannerEntityService.js';
import { UserBannerPiningEntityService } from '@/core/entities/UserBannerPiningEntityService.js';
import { AccountMoveService } from './AccountMoveService.js';
import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js';
Expand Down Expand Up @@ -35,6 +37,8 @@ import { ModerationLogService } from './ModerationLogService.js';
import { NoteCreateService } from './NoteCreateService.js';
import { NoteDeleteService } from './NoteDeleteService.js';
import { NotePiningService } from './NotePiningService.js';
import { UserBannerPiningService } from './UserBannerPiningService.js';
import { UserBannerService } from './UserBannerService.js';
import { NoteReadService } from './NoteReadService.js';
import { NotificationService } from './NotificationService.js';
import { PollService } from './PollService.js';
Expand Down Expand Up @@ -173,6 +177,8 @@ const $ModerationLogService: Provider = { provide: 'ModerationLogService', useEx
const $NoteCreateService: Provider = { provide: 'NoteCreateService', useExisting: NoteCreateService };
const $NoteDeleteService: Provider = { provide: 'NoteDeleteService', useExisting: NoteDeleteService };
const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting: NotePiningService };
const $UserBannerPiningService: Provider = { provide: 'UserBannerPiningService', useExisting: UserBannerPiningService };
const $UserBannerService: Provider = { provide: 'UserBannerService', useExisting: UserBannerService };
const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService };
const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService };
const $PollService: Provider = { provide: 'PollService', useExisting: PollService };
Expand Down Expand Up @@ -253,6 +259,8 @@ const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', use
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService };
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService };
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
const $UserBannerEntityService: Provider = { provide: 'UserBannerEntityService', useExisting: UserBannerEntityService };
const $UserBannerPiningEntityService: Provider = { provide: 'UserBannerPiningEntityService', useExisting: UserBannerPiningEntityService };
const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService };
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService };
Expand Down Expand Up @@ -315,6 +323,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
NoteCreateService,
NoteDeleteService,
NotePiningService,
UserBannerPiningService,
UserBannerService,
NoteReadService,
NotificationService,
PollService,
Expand Down Expand Up @@ -393,6 +403,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
SigninEntityService,
UserEntityService,
UserListEntityService,
UserBannerEntityService,
UserBannerPiningEntityService,
FlashEntityService,
FlashLikeEntityService,
RoleEntityService,
Expand Down Expand Up @@ -451,6 +463,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$NoteCreateService,
$NoteDeleteService,
$NotePiningService,
$UserBannerService,
$UserBannerPiningService,
$NoteReadService,
$NotificationService,
$PollService,
Expand Down Expand Up @@ -529,6 +543,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SigninEntityService,
$UserEntityService,
$UserListEntityService,
$UserBannerEntityService,
$UserBannerPiningEntityService,
$FlashEntityService,
$FlashLikeEntityService,
$RoleEntityService,
Expand Down Expand Up @@ -588,6 +604,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
NoteCreateService,
NoteDeleteService,
NotePiningService,
UserBannerService,
UserBannerPiningService,
NoteReadService,
NotificationService,
PollService,
Expand Down Expand Up @@ -665,6 +683,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
SigninEntityService,
UserEntityService,
UserListEntityService,
UserBannerEntityService,
UserBannerPiningEntityService,
FlashEntityService,
FlashLikeEntityService,
RoleEntityService,
Expand Down Expand Up @@ -723,6 +743,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$NoteCreateService,
$NoteDeleteService,
$NotePiningService,
$UserBannerService,
$UserBannerPiningService,
$NoteReadService,
$NotificationService,
$PollService,
Expand Down Expand Up @@ -800,6 +822,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$SigninEntityService,
$UserEntityService,
$UserListEntityService,
$UserBannerEntityService,
$UserBannerPiningEntityService,
$FlashEntityService,
$FlashLikeEntityService,
$RoleEntityService,
Expand Down
55 changes: 55 additions & 0 deletions packages/backend/src/core/UserBannerPiningService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm';
import { bindThis } from '@/decorators.js';
import { MiUser } from '@/models/User.js';
import { MiUserBanner } from '@/models/UserBanner.js';
import type { MiUserBannerPining, UserBannerPiningRepository, UserBannerRepository } from '@/models/_.js';
import { DI } from '@/di-symbols.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { IdService } from '@/core/IdService.js';

@Injectable()
export class UserBannerPiningService {
constructor(
@Inject(DI.userBannerRepository)
private userBannerRepository: UserBannerRepository,
@Inject(DI.userBannerPiningRepository)
private userBannerPiningRepository: UserBannerPiningRepository,

private idService: IdService,
) {

}

/**
* 指定したユーザーのバナーをピン留めします
* @param userId
* @param bannerIds
*/
public async addPinned(userId: MiUser['id'], bannerIds: MiUserBanner['id'][]) {
const pinsToInsert = bannerIds.map(bannerId => ({
id: this.idService.gen(),
userId,
pinnedBannerId: bannerId,
} as MiUserBannerPining));
await this.userBannerPiningRepository
.createQueryBuilder()
.insert()
.values(pinsToInsert)
.orIgnore()
.execute();
}

/**
* 指定したユーザーのバナーのピン留めを解除します
* @param userId
* @param bannerIds
*/
@bindThis
public async removePinned(userId:MiUser['id'], bannerIds:MiUserBanner['id'][]) {
await this.userBannerPiningRepository.delete({
userId,
pinnedBannerId: In(bannerIds),
});
}
}
Loading

0 comments on commit b6a5a36

Please sign in to comment.