diff --git a/CHANGELOG.md b/CHANGELOG.md index db26ffc8e..e40c5f0fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ - Fix: MFM `$[unixtime ]` に不正な値を入力した際に発生する各種エラーを修正 ### Client +- Feat: 今日誕生日のフォロー中のユーザーを一覧表示できるウィジェットを追加 - Enhance: 絵文字のオートコンプリート機能強化 #12364 - Enhance: ユーザーのRawデータを表示するページが復活 - Enhance: リアクション選択時に音を鳴らせるように diff --git a/locales/index.d.ts b/locales/index.d.ts index 6036c6fa6..d46281649 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2110,6 +2110,7 @@ export interface Locale { "chooseList": string; }; "clicker": string; + "birthdayFollowings": string; }; "_cw": { "hide": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0f4164652..aa3cdf075 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2014,6 +2014,7 @@ _widgets: _userList: chooseList: "リストを選択" clicker: "クリッカー" + birthdayFollowings: "今日誕生日のユーザー" _cw: hide: "隠す" diff --git a/packages/backend/migration/1700902349231-add-bday-index.js b/packages/backend/migration/1700902349231-add-bday-index.js new file mode 100644 index 000000000..251526fc2 --- /dev/null +++ b/packages/backend/migration/1700902349231-add-bday-index.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class AddBdayIndex1700902349231 { + name = 'AddBdayIndex1700902349231' + + async up(queryRunner) { + await queryRunner.query(`CREATE INDEX "IDX_de22cd2b445eee31ae51cdbe99" ON "user_profile" (SUBSTR("birthday", 6, 5))`); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_de22cd2b445eee31ae51cdbe99"`); + } +} diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 8a43b6003..6659a0141 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -29,6 +29,7 @@ export class MiUserProfile { }) public location: string | null; + @Index() @Column('char', { length: 10, nullable: true, comment: 'The birthday (YYYY-MM-DD) of the User.', diff --git a/packages/backend/src/server/api/endpoints/users/following.ts b/packages/backend/src/server/api/endpoints/users/following.ts index 03487275a..ead7ba8c4 100644 --- a/packages/backend/src/server/api/endpoints/users/following.ts +++ b/packages/backend/src/server/api/endpoints/users/following.ts @@ -42,6 +42,12 @@ export const meta = { code: 'FORBIDDEN', id: 'f6cdb0df-c19f-ec5c-7dbb-0ba84a1f92ba', }, + + birthdayInvalid: { + message: 'Birthday date format is invalid.', + code: 'BIRTHDAY_DATE_FORMAT_INVALID', + id: 'a2b007b9-4782-4eba-abd3-93b05ed4130d', + }, }, } as const; @@ -59,6 +65,8 @@ export const paramDef = { nullable: true, description: 'The local host is represented with `null`.', }, + + birthday: { type: 'string', nullable: true }, }, anyOf: [ { required: ['userId'] }, @@ -117,6 +125,21 @@ export default class extends Endpoint { // eslint- .andWhere('following.followerId = :userId', { userId: user.id }) .innerJoinAndSelect('following.followee', 'followee'); + if (ps.birthday) { + try { + const d = new Date(ps.birthday); + d.setHours(0, 0, 0, 0); + const birthday = `${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`; + const birthdayUserQuery = this.userProfilesRepository.createQueryBuilder('user_profile'); + birthdayUserQuery.select('user_profile.userId') + .where(`SUBSTR(user_profile.birthday, 6, 5) = '${birthday}'`); + + query.andWhere(`following.followeeId IN (${ birthdayUserQuery.getQuery() })`); + } catch (err) { + throw new ApiError(meta.errors.birthdayInvalid); + } + } + const followings = await query .limit(ps.limit) .getMany(); diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue new file mode 100644 index 000000000..7c4455516 --- /dev/null +++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue @@ -0,0 +1,127 @@ + + + + + + + diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index 405c49ab0..36925e1bd 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -33,6 +33,7 @@ export default function(app: App) { app.component('WidgetAichan', defineAsyncComponent(() => import('./WidgetAichan.vue'))); app.component('WidgetUserList', defineAsyncComponent(() => import('./WidgetUserList.vue'))); app.component('WidgetClicker', defineAsyncComponent(() => import('./WidgetClicker.vue'))); + app.component('WidgetBirthdayFollowings', defineAsyncComponent(() => import('./WidgetBirthdayFollowings.vue'))); } export const widgets = [ @@ -63,4 +64,5 @@ export const widgets = [ 'aichan', 'userList', 'clicker', + 'birthdayFollowings', ];