mirror of
https://github.com/kokonect-link/cherrypick
synced 2024-12-12 05:38:55 +09:00
parent
520b072c09
commit
8373e6642d
@ -430,15 +430,24 @@ markAsReadAllTalkMessages: "すべてのチャットを既読にする"
|
|||||||
help: "ヘルプ"
|
help: "ヘルプ"
|
||||||
inputMessageHere: "ここにメッセージを入力"
|
inputMessageHere: "ここにメッセージを入力"
|
||||||
close: "閉じる"
|
close: "閉じる"
|
||||||
|
group: "グループ"
|
||||||
|
groups: "グループ"
|
||||||
|
createGroup: "グループを作成"
|
||||||
|
ownedGroups: "所有グループ"
|
||||||
|
joinedGroups: "参加しているグループ"
|
||||||
invites: "招待"
|
invites: "招待"
|
||||||
|
groupName: "グループ名"
|
||||||
members: "メンバー"
|
members: "メンバー"
|
||||||
transfer: "譲渡"
|
transfer: "譲渡"
|
||||||
|
messagingWithUser: "ユーザーとチャット"
|
||||||
|
messagingWithGroup: "グループでチャット"
|
||||||
title: "タイトル"
|
title: "タイトル"
|
||||||
text: "テキスト"
|
text: "テキスト"
|
||||||
enable: "有効にする"
|
enable: "有効にする"
|
||||||
next: "次"
|
next: "次"
|
||||||
retype: "再入力"
|
retype: "再入力"
|
||||||
noteOf: "{user}のノート"
|
noteOf: "{user}のノート"
|
||||||
|
inviteToGroup: "グループに招待"
|
||||||
quoteAttached: "引用付き"
|
quoteAttached: "引用付き"
|
||||||
quoteQuestion: "引用として添付しますか?"
|
quoteQuestion: "引用として添付しますか?"
|
||||||
noMessagesYet: "まだチャットはありません"
|
noMessagesYet: "まだチャットはありません"
|
||||||
@ -464,10 +473,13 @@ tapSecurityKey: "セキュリティキーにタッチ"
|
|||||||
or: "もしくは"
|
or: "もしくは"
|
||||||
language: "言語"
|
language: "言語"
|
||||||
uiLanguage: "UIの表示言語"
|
uiLanguage: "UIの表示言語"
|
||||||
|
groupInvited: "グループに招待されました"
|
||||||
aboutX: "{x}について"
|
aboutX: "{x}について"
|
||||||
emojiStyle: "絵文字のスタイル"
|
emojiStyle: "絵文字のスタイル"
|
||||||
native: "ネイティブ"
|
native: "ネイティブ"
|
||||||
disableDrawer: "メニューをドロワーで表示しない"
|
disableDrawer: "メニューをドロワーで表示しない"
|
||||||
|
youHaveNoGroups: "グループがありません"
|
||||||
|
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
|
||||||
noHistory: "履歴はありません"
|
noHistory: "履歴はありません"
|
||||||
signinHistory: "ログイン履歴"
|
signinHistory: "ログイン履歴"
|
||||||
enableAdvancedMfm: "高度なMFMを有効にする"
|
enableAdvancedMfm: "高度なMFMを有効にする"
|
||||||
@ -842,6 +854,8 @@ deleteAccountConfirm: "アカウントが削除されます。よろしいです
|
|||||||
incorrectPassword: "パスワードが間違っています。"
|
incorrectPassword: "パスワードが間違っています。"
|
||||||
voteConfirm: "「{choice}」に投票しますか?"
|
voteConfirm: "「{choice}」に投票しますか?"
|
||||||
hide: "隠す"
|
hide: "隠す"
|
||||||
|
leaveGroup: "グループから抜ける"
|
||||||
|
leaveGroupConfirm: "「{name}」から抜けますか?"
|
||||||
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
|
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
|
||||||
welcomeBackWithName: "おかえりなさい、{name}さん"
|
welcomeBackWithName: "おかえりなさい、{name}さん"
|
||||||
clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。"
|
clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。"
|
||||||
@ -1673,6 +1687,7 @@ _antennaSources:
|
|||||||
homeTimeline: "フォローしているユーザーのノート"
|
homeTimeline: "フォローしているユーザーのノート"
|
||||||
users: "指定した一人または複数のユーザーのノート"
|
users: "指定した一人または複数のユーザーのノート"
|
||||||
userList: "指定したリストのユーザーのノート"
|
userList: "指定したリストのユーザーのノート"
|
||||||
|
userGroup: "指定したグループのユーザーのノート"
|
||||||
|
|
||||||
_weekday:
|
_weekday:
|
||||||
sunday: "日曜日"
|
sunday: "日曜日"
|
||||||
@ -1902,9 +1917,12 @@ _notification:
|
|||||||
youGotReply: "{name}からのリプライ"
|
youGotReply: "{name}からのリプライ"
|
||||||
youGotQuote: "{name}による引用"
|
youGotQuote: "{name}による引用"
|
||||||
youRenoted: "{name}がRenoteしました"
|
youRenoted: "{name}がRenoteしました"
|
||||||
|
youGotMessagingMessageFromUser: "{name}からのチャットがあります"
|
||||||
|
youGotMessagingMessageFromGroup: "{name}のチャットがあります"
|
||||||
youWereFollowed: "フォローされました"
|
youWereFollowed: "フォローされました"
|
||||||
youReceivedFollowRequest: "フォローリクエストが来ました"
|
youReceivedFollowRequest: "フォローリクエストが来ました"
|
||||||
yourFollowRequestAccepted: "フォローリクエストが承認されました"
|
yourFollowRequestAccepted: "フォローリクエストが承認されました"
|
||||||
|
youWereInvitedToGroup: "{userName}があなたをグループに招待しました"
|
||||||
pollEnded: "アンケートの結果が出ました"
|
pollEnded: "アンケートの結果が出ました"
|
||||||
unreadAntennaNote: "アンテナ {name}"
|
unreadAntennaNote: "アンテナ {name}"
|
||||||
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
|
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
|
||||||
@ -1921,6 +1939,7 @@ _notification:
|
|||||||
pollEnded: "アンケートが終了"
|
pollEnded: "アンケートが終了"
|
||||||
receiveFollowRequest: "フォロー申請を受け取った"
|
receiveFollowRequest: "フォロー申請を受け取った"
|
||||||
followRequestAccepted: "フォローが受理された"
|
followRequestAccepted: "フォローが受理された"
|
||||||
|
groupInvited: "グループに招待された"
|
||||||
app: "連携アプリからの通知"
|
app: "連携アプリからの通知"
|
||||||
|
|
||||||
_actions:
|
_actions:
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
export class dropGroup1676434944993 {
|
|
||||||
name = 'dropGroup1676434944993'
|
|
||||||
|
|
||||||
async up(queryRunner) {
|
|
||||||
await queryRunner.query(`ALTER TABLE "antenna" DROP CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "notification" DROP CONSTRAINT "FK_8fe87814e978053a53b1beb7e98"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "userGroupJoiningId"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "notification" DROP COLUMN "userGroupInvitationId"`);
|
|
||||||
await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum" RENAME TO "antenna_src_enum_old"`);
|
|
||||||
await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum" AS ENUM('home', 'all', 'users', 'list')`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum" USING "src"::"text"::"public"."antenna_src_enum"`);
|
|
||||||
await queryRunner.query(`DROP TYPE "public"."antenna_src_enum_old"`);
|
|
||||||
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum" RENAME TO "notification_type_enum_old"`);
|
|
||||||
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app')`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum" USING "type"::"text"::"public"."notification_type_enum"`);
|
|
||||||
await queryRunner.query(`DROP TYPE "public"."notification_type_enum_old"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow","receiveFollowRequest"]'`);
|
|
||||||
await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum" RENAME TO "user_profile_mutingnotificationtypes_enum_old"`);
|
|
||||||
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app')`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum"[]`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
|
|
||||||
await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum_old"`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async down(queryRunner) {
|
|
||||||
await queryRunner.query(`CREATE TYPE "public"."user_profile_mutingnotificationtypes_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" DROP DEFAULT`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" TYPE "public"."user_profile_mutingnotificationtypes_enum_old"[] USING "mutingNotificationTypes"::"text"::"public"."user_profile_mutingnotificationtypes_enum_old"[]`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "mutingNotificationTypes" SET DEFAULT '{}'`);
|
|
||||||
await queryRunner.query(`DROP TYPE "public"."user_profile_mutingnotificationtypes_enum"`);
|
|
||||||
await queryRunner.query(`ALTER TYPE "public"."user_profile_mutingnotificationtypes_enum_old" RENAME TO "user_profile_mutingnotificationtypes_enum"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "user_profile" ALTER COLUMN "emailNotificationTypes" SET DEFAULT '["follow", "receiveFollowRequest", "groupInvited"]'`);
|
|
||||||
await queryRunner.query(`CREATE TYPE "public"."notification_type_enum_old" AS ENUM('follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app')`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "notification" ALTER COLUMN "type" TYPE "public"."notification_type_enum_old" USING "type"::"text"::"public"."notification_type_enum_old"`);
|
|
||||||
await queryRunner.query(`DROP TYPE "public"."notification_type_enum"`);
|
|
||||||
await queryRunner.query(`ALTER TYPE "public"."notification_type_enum_old" RENAME TO "notification_type_enum"`);
|
|
||||||
await queryRunner.query(`CREATE TYPE "public"."antenna_src_enum_old" AS ENUM('home', 'all', 'users', 'list', 'group')`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "antenna" ALTER COLUMN "src" TYPE "public"."antenna_src_enum_old" USING "src"::"text"::"public"."antenna_src_enum_old"`);
|
|
||||||
await queryRunner.query(`DROP TYPE "public"."antenna_src_enum"`);
|
|
||||||
await queryRunner.query(`ALTER TYPE "public"."antenna_src_enum_old" RENAME TO "antenna_src_enum"`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "notification" ADD "userGroupInvitationId" character varying(32)`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "userGroupJoiningId" character varying(32)`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "notification" ADD CONSTRAINT "FK_8fe87814e978053a53b1beb7e98" FOREIGN KEY ("userGroupInvitationId") REFERENCES "user_group_invitation"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
|
||||||
await queryRunner.query(`ALTER TABLE "antenna" ADD CONSTRAINT "FK_ccbf5a8c0be4511133dcc50ddeb" FOREIGN KEY ("userGroupJoiningId") REFERENCES "user_group_joining"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ import { PushNotificationService } from '@/core/PushNotificationService.js';
|
|||||||
import * as Acct from '@/misc/acct.js';
|
import * as Acct from '@/misc/acct.js';
|
||||||
import type { Packed } from '@/misc/schema.js';
|
import type { Packed } from '@/misc/schema.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { MutingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserListJoiningsRepository } from '@/models/index.js';
|
import type { MutingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js';
|
||||||
import { UtilityService } from '@/core/UtilityService.js';
|
import { UtilityService } from '@/core/UtilityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { StreamMessages } from '@/server/api/stream/types.js';
|
import { StreamMessages } from '@/server/api/stream/types.js';
|
||||||
@ -39,6 +39,9 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
@Inject(DI.antennasRepository)
|
@Inject(DI.antennasRepository)
|
||||||
private antennasRepository: AntennasRepository,
|
private antennasRepository: AntennasRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
@Inject(DI.userListJoiningsRepository)
|
@Inject(DI.userListJoiningsRepository)
|
||||||
private userListJoiningsRepository: UserListJoiningsRepository,
|
private userListJoiningsRepository: UserListJoiningsRepository,
|
||||||
|
|
||||||
@ -157,6 +160,14 @@ export class AntennaService implements OnApplicationShutdown {
|
|||||||
})).map(x => x.userId);
|
})).map(x => x.userId);
|
||||||
|
|
||||||
if (!listUsers.includes(note.userId)) return false;
|
if (!listUsers.includes(note.userId)) return false;
|
||||||
|
} else if (antenna.src === 'group') {
|
||||||
|
const joining = await this.userGroupJoiningsRepository.findOneByOrFail({ id: antenna.userGroupJoiningId! });
|
||||||
|
|
||||||
|
const groupUsers = (await this.userGroupJoiningsRepository.findBy({
|
||||||
|
userGroupId: joining.userGroupId,
|
||||||
|
})).map(x => x.userId);
|
||||||
|
|
||||||
|
if (!groupUsers.includes(note.userId)) return false;
|
||||||
} else if (antenna.src === 'users') {
|
} else if (antenna.src === 'users') {
|
||||||
const accts = antenna.users.map(x => {
|
const accts = antenna.users.map(x => {
|
||||||
const { username, host } = Acct.parse(x);
|
const { username, host } = Acct.parse(x);
|
||||||
|
@ -91,6 +91,8 @@ import { PageEntityService } from './entities/PageEntityService.js';
|
|||||||
import { PageLikeEntityService } from './entities/PageLikeEntityService.js';
|
import { PageLikeEntityService } from './entities/PageLikeEntityService.js';
|
||||||
import { SigninEntityService } from './entities/SigninEntityService.js';
|
import { SigninEntityService } from './entities/SigninEntityService.js';
|
||||||
import { UserEntityService } from './entities/UserEntityService.js';
|
import { UserEntityService } from './entities/UserEntityService.js';
|
||||||
|
import { UserGroupEntityService } from './entities/UserGroupEntityService.js';
|
||||||
|
import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js';
|
||||||
import { UserListEntityService } from './entities/UserListEntityService.js';
|
import { UserListEntityService } from './entities/UserListEntityService.js';
|
||||||
import { FlashEntityService } from './entities/FlashEntityService.js';
|
import { FlashEntityService } from './entities/FlashEntityService.js';
|
||||||
import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js';
|
import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js';
|
||||||
@ -212,6 +214,8 @@ const $PageEntityService: Provider = { provide: 'PageEntityService', useExisting
|
|||||||
const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useExisting: PageLikeEntityService };
|
const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useExisting: PageLikeEntityService };
|
||||||
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService };
|
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService };
|
||||||
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService };
|
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService };
|
||||||
|
const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useExisting: UserGroupEntityService };
|
||||||
|
const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useExisting: UserGroupInvitationEntityService };
|
||||||
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
|
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
|
||||||
const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService };
|
const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService };
|
||||||
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
|
const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService };
|
||||||
@ -334,6 +338,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
PageLikeEntityService,
|
PageLikeEntityService,
|
||||||
SigninEntityService,
|
SigninEntityService,
|
||||||
UserEntityService,
|
UserEntityService,
|
||||||
|
UserGroupEntityService,
|
||||||
|
UserGroupInvitationEntityService,
|
||||||
UserListEntityService,
|
UserListEntityService,
|
||||||
FlashEntityService,
|
FlashEntityService,
|
||||||
FlashLikeEntityService,
|
FlashLikeEntityService,
|
||||||
@ -451,6 +457,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$PageLikeEntityService,
|
$PageLikeEntityService,
|
||||||
$SigninEntityService,
|
$SigninEntityService,
|
||||||
$UserEntityService,
|
$UserEntityService,
|
||||||
|
$UserGroupEntityService,
|
||||||
|
$UserGroupInvitationEntityService,
|
||||||
$UserListEntityService,
|
$UserListEntityService,
|
||||||
$FlashEntityService,
|
$FlashEntityService,
|
||||||
$FlashLikeEntityService,
|
$FlashLikeEntityService,
|
||||||
@ -568,6 +576,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
PageLikeEntityService,
|
PageLikeEntityService,
|
||||||
SigninEntityService,
|
SigninEntityService,
|
||||||
UserEntityService,
|
UserEntityService,
|
||||||
|
UserGroupEntityService,
|
||||||
|
UserGroupInvitationEntityService,
|
||||||
UserListEntityService,
|
UserListEntityService,
|
||||||
FlashEntityService,
|
FlashEntityService,
|
||||||
FlashLikeEntityService,
|
FlashLikeEntityService,
|
||||||
@ -684,6 +694,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||||||
$PageLikeEntityService,
|
$PageLikeEntityService,
|
||||||
$SigninEntityService,
|
$SigninEntityService,
|
||||||
$UserEntityService,
|
$UserEntityService,
|
||||||
|
$UserGroupEntityService,
|
||||||
|
$UserGroupInvitationEntityService,
|
||||||
$UserListEntityService,
|
$UserListEntityService,
|
||||||
$FlashEntityService,
|
$FlashEntityService,
|
||||||
$FlashLikeEntityService,
|
$FlashLikeEntityService,
|
||||||
|
@ -3,6 +3,7 @@ import Redis from 'ioredis';
|
|||||||
import type { User } from '@/models/entities/User.js';
|
import type { User } from '@/models/entities/User.js';
|
||||||
import type { Note } from '@/models/entities/Note.js';
|
import type { Note } from '@/models/entities/Note.js';
|
||||||
import type { UserList } from '@/models/entities/UserList.js';
|
import type { UserList } from '@/models/entities/UserList.js';
|
||||||
|
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
||||||
import type { Antenna } from '@/models/entities/Antenna.js';
|
import type { Antenna } from '@/models/entities/Antenna.js';
|
||||||
import type { Channel } from '@/models/entities/Channel.js';
|
import type { Channel } from '@/models/entities/Channel.js';
|
||||||
import type {
|
import type {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { AntennaNotesRepository, AntennasRepository } from '@/models/index.js';
|
import type { AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
import type { Packed } from '@/misc/schema.js';
|
import type { Packed } from '@/misc/schema.js';
|
||||||
import type { Antenna } from '@/models/entities/Antenna.js';
|
import type { Antenna } from '@/models/entities/Antenna.js';
|
||||||
@ -14,6 +14,9 @@ export class AntennaEntityService {
|
|||||||
|
|
||||||
@Inject(DI.antennaNotesRepository)
|
@Inject(DI.antennaNotesRepository)
|
||||||
private antennaNotesRepository: AntennaNotesRepository,
|
private antennaNotesRepository: AntennaNotesRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +27,7 @@ export class AntennaEntityService {
|
|||||||
const antenna = typeof src === 'object' ? src : await this.antennasRepository.findOneByOrFail({ id: src });
|
const antenna = typeof src === 'object' ? src : await this.antennasRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
const hasUnreadNote = (await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false })) != null;
|
const hasUnreadNote = (await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false })) != null;
|
||||||
|
const userGroupJoining = antenna.userGroupJoiningId ? await this.userGroupJoiningsRepository.findOneBy({ id: antenna.userGroupJoiningId }) : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: antenna.id,
|
id: antenna.id,
|
||||||
@ -33,6 +37,7 @@ export class AntennaEntityService {
|
|||||||
excludeKeywords: antenna.excludeKeywords,
|
excludeKeywords: antenna.excludeKeywords,
|
||||||
src: antenna.src,
|
src: antenna.src,
|
||||||
userListId: antenna.userListId,
|
userListId: antenna.userListId,
|
||||||
|
userGroupId: userGroupJoining ? userGroupJoining.userGroupId : null,
|
||||||
users: antenna.users,
|
users: antenna.users,
|
||||||
caseSensitive: antenna.caseSensitive,
|
caseSensitive: antenna.caseSensitive,
|
||||||
notify: antenna.notify,
|
notify: antenna.notify,
|
||||||
|
@ -13,11 +13,13 @@ import type { OnModuleInit } from '@nestjs/common';
|
|||||||
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
import type { CustomEmojiService } from '../CustomEmojiService.js';
|
||||||
import type { UserEntityService } from './UserEntityService.js';
|
import type { UserEntityService } from './UserEntityService.js';
|
||||||
import type { NoteEntityService } from './NoteEntityService.js';
|
import type { NoteEntityService } from './NoteEntityService.js';
|
||||||
|
import type { UserGroupInvitationEntityService } from './UserGroupInvitationEntityService.js';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NotificationEntityService implements OnModuleInit {
|
export class NotificationEntityService implements OnModuleInit {
|
||||||
private userEntityService: UserEntityService;
|
private userEntityService: UserEntityService;
|
||||||
private noteEntityService: NoteEntityService;
|
private noteEntityService: NoteEntityService;
|
||||||
|
private userGroupInvitationEntityService: UserGroupInvitationEntityService;
|
||||||
private customEmojiService: CustomEmojiService;
|
private customEmojiService: CustomEmojiService;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -34,6 +36,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||||||
|
|
||||||
//private userEntityService: UserEntityService,
|
//private userEntityService: UserEntityService,
|
||||||
//private noteEntityService: NoteEntityService,
|
//private noteEntityService: NoteEntityService,
|
||||||
|
//private userGroupInvitationEntityService: UserGroupInvitationEntityService,
|
||||||
//private customEmojiService: CustomEmojiService,
|
//private customEmojiService: CustomEmojiService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@ -41,6 +44,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
this.userEntityService = this.moduleRef.get('UserEntityService');
|
this.userEntityService = this.moduleRef.get('UserEntityService');
|
||||||
this.noteEntityService = this.moduleRef.get('NoteEntityService');
|
this.noteEntityService = this.moduleRef.get('NoteEntityService');
|
||||||
|
this.userGroupInvitationEntityService = this.moduleRef.get('UserGroupInvitationEntityService');
|
||||||
this.customEmojiService = this.moduleRef.get('CustomEmojiService');
|
this.customEmojiService = this.moduleRef.get('CustomEmojiService');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +111,9 @@ export class NotificationEntityService implements OnModuleInit {
|
|||||||
_hint_: options._hintForEachNotes_,
|
_hint_: options._hintForEachNotes_,
|
||||||
}),
|
}),
|
||||||
} : {}),
|
} : {}),
|
||||||
|
...(notification.type === 'groupInvited' ? {
|
||||||
|
invitation: this.userGroupInvitationEntityService.pack(notification.userGroupInvitationId!),
|
||||||
|
} : {}),
|
||||||
...(notification.type === 'achievementEarned' ? {
|
...(notification.type === 'achievementEarned' ? {
|
||||||
achievement: notification.achievement,
|
achievement: notification.achievement,
|
||||||
} : {}),
|
} : {}),
|
||||||
|
@ -12,7 +12,7 @@ import { Cache } from '@/misc/cache.js';
|
|||||||
import type { Instance } from '@/models/entities/Instance.js';
|
import type { Instance } from '@/models/entities/Instance.js';
|
||||||
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js';
|
||||||
import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js';
|
import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/entities/User.js';
|
||||||
import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository, UserProfile } from '@/models/index.js';
|
import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, ChannelFollowingsRepository, NotificationsRepository, UserNotePiningsRepository, UserProfilesRepository, InstancesRepository, AnnouncementReadsRepository, UserGroupJoiningsRepository, AnnouncementsRepository, AntennaNotesRepository, PagesRepository, UserProfile } from '@/models/index.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import type { OnModuleInit } from '@nestjs/common';
|
import type { OnModuleInit } from '@nestjs/common';
|
||||||
@ -102,6 +102,9 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
@Inject(DI.announcementReadsRepository)
|
@Inject(DI.announcementReadsRepository)
|
||||||
private announcementReadsRepository: AnnouncementReadsRepository,
|
private announcementReadsRepository: AnnouncementReadsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
@Inject(DI.announcementsRepository)
|
@Inject(DI.announcementsRepository)
|
||||||
private announcementsRepository: AnnouncementsRepository,
|
private announcementsRepository: AnnouncementsRepository,
|
||||||
|
|
||||||
|
44
packages/backend/src/core/entities/UserGroupEntityService.ts
Normal file
44
packages/backend/src/core/entities/UserGroupEntityService.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import type { UserGroupJoiningsRepository, UserGroupsRepository } from '@/models/index.js';
|
||||||
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
|
import type { Packed } from '@/misc/schema.js';
|
||||||
|
import type { } from '@/models/entities/Blocking.js';
|
||||||
|
import type { User } from '@/models/entities/User.js';
|
||||||
|
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
||||||
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserGroupEntityService {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private userEntityService: UserEntityService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async pack(
|
||||||
|
src: UserGroup['id'] | UserGroup,
|
||||||
|
): Promise<Packed<'UserGroup'>> {
|
||||||
|
const userGroup = typeof src === 'object' ? src : await this.userGroupsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
|
const users = await this.userGroupJoiningsRepository.findBy({
|
||||||
|
userGroupId: userGroup.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: userGroup.id,
|
||||||
|
createdAt: userGroup.createdAt.toISOString(),
|
||||||
|
name: userGroup.name,
|
||||||
|
ownerId: userGroup.userId,
|
||||||
|
userIds: users.map(x => x.userId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import type { UserGroupInvitationsRepository } from '@/models/index.js';
|
||||||
|
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||||
|
import type { Packed } from '@/misc/schema.js';
|
||||||
|
import type { } from '@/models/entities/Blocking.js';
|
||||||
|
import type { User } from '@/models/entities/User.js';
|
||||||
|
import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
|
||||||
|
import { UserEntityService } from './UserEntityService.js';
|
||||||
|
import { UserGroupEntityService } from './UserGroupEntityService.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserGroupInvitationEntityService {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupInvitationsRepository)
|
||||||
|
private userGroupInvitationsRepository: UserGroupInvitationsRepository,
|
||||||
|
|
||||||
|
private userGroupEntityService: UserGroupEntityService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async pack(
|
||||||
|
src: UserGroupInvitation['id'] | UserGroupInvitation,
|
||||||
|
) {
|
||||||
|
const invitation = typeof src === 'object' ? src : await this.userGroupInvitationsRepository.findOneByOrFail({ id: src });
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: invitation.id,
|
||||||
|
group: await this.userGroupEntityService.pack(invitation.userGroup ?? invitation.userGroupId),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public packMany(
|
||||||
|
invitations: any[],
|
||||||
|
) {
|
||||||
|
return Promise.all(invitations.map(x => this.pack(x)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,9 @@ export const DI = {
|
|||||||
userPublickeysRepository: Symbol('userPublickeysRepository'),
|
userPublickeysRepository: Symbol('userPublickeysRepository'),
|
||||||
userListsRepository: Symbol('userListsRepository'),
|
userListsRepository: Symbol('userListsRepository'),
|
||||||
userListJoiningsRepository: Symbol('userListJoiningsRepository'),
|
userListJoiningsRepository: Symbol('userListJoiningsRepository'),
|
||||||
|
userGroupsRepository: Symbol('userGroupsRepository'),
|
||||||
|
userGroupJoiningsRepository: Symbol('userGroupJoiningsRepository'),
|
||||||
|
userGroupInvitationsRepository: Symbol('userGroupInvitationsRepository'),
|
||||||
userNotePiningsRepository: Symbol('userNotePiningsRepository'),
|
userNotePiningsRepository: Symbol('userNotePiningsRepository'),
|
||||||
userIpsRepository: Symbol('userIpsRepository'),
|
userIpsRepository: Symbol('userIpsRepository'),
|
||||||
usedUsernamesRepository: Symbol('usedUsernamesRepository'),
|
usedUsernamesRepository: Symbol('usedUsernamesRepository'),
|
||||||
|
@ -19,6 +19,7 @@ import { packedBlockingSchema } from '@/models/schema/blocking.js';
|
|||||||
import { packedNoteReactionSchema } from '@/models/schema/note-reaction.js';
|
import { packedNoteReactionSchema } from '@/models/schema/note-reaction.js';
|
||||||
import { packedHashtagSchema } from '@/models/schema/hashtag.js';
|
import { packedHashtagSchema } from '@/models/schema/hashtag.js';
|
||||||
import { packedPageSchema } from '@/models/schema/page.js';
|
import { packedPageSchema } from '@/models/schema/page.js';
|
||||||
|
import { packedUserGroupSchema } from '@/models/schema/user-group.js';
|
||||||
import { packedNoteFavoriteSchema } from '@/models/schema/note-favorite.js';
|
import { packedNoteFavoriteSchema } from '@/models/schema/note-favorite.js';
|
||||||
import { packedChannelSchema } from '@/models/schema/channel.js';
|
import { packedChannelSchema } from '@/models/schema/channel.js';
|
||||||
import { packedAntennaSchema } from '@/models/schema/antenna.js';
|
import { packedAntennaSchema } from '@/models/schema/antenna.js';
|
||||||
@ -38,6 +39,7 @@ export const refs = {
|
|||||||
User: packedUserSchema,
|
User: packedUserSchema,
|
||||||
|
|
||||||
UserList: packedUserListSchema,
|
UserList: packedUserListSchema,
|
||||||
|
UserGroup: packedUserGroupSchema,
|
||||||
App: packedAppSchema,
|
App: packedAppSchema,
|
||||||
Note: packedNoteSchema,
|
Note: packedNoteSchema,
|
||||||
NoteReaction: packedNoteReactionSchema,
|
NoteReaction: packedNoteReactionSchema,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js';
|
import { User, Note, Announcement, AnnouncementRead, App, NoteFavorite, NoteThreadMuting, NoteReaction, NoteUnread, Notification, Poll, PollVote, UserProfile, UserKeypair, UserPending, AttestationChallenge, UserSecurityKey, UserPublickey, UserList, UserListJoining, UserGroup, UserGroupJoining, UserGroupInvitation, UserNotePining, UserIp, UsedUsername, Following, FollowRequest, Instance, Emoji, DriveFile, DriveFolder, Meta, Muting, Blocking, SwSubscription, Hashtag, AbuseUserReport, RegistrationTicket, AuthSession, AccessToken, Signin, Page, PageLike, GalleryPost, GalleryLike, ModerationLog, Clip, ClipNote, Antenna, AntennaNote, PromoNote, PromoRead, Relay, MutedNote, Channel, ChannelFollowing, ChannelNotePining, RegistryItem, Webhook, Ad, PasswordResetRequest, RetentionAggregation, FlashLike, Flash, Role, RoleAssignment } from './index.js';
|
||||||
import type { DataSource } from 'typeorm';
|
import type { DataSource } from 'typeorm';
|
||||||
import type { Provider } from '@nestjs/common';
|
import type { Provider } from '@nestjs/common';
|
||||||
|
|
||||||
@ -118,6 +118,24 @@ const $userListJoiningsRepository: Provider = {
|
|||||||
inject: [DI.db],
|
inject: [DI.db],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $userGroupsRepository: Provider = {
|
||||||
|
provide: DI.userGroupsRepository,
|
||||||
|
useFactory: (db: DataSource) => db.getRepository(UserGroup),
|
||||||
|
inject: [DI.db],
|
||||||
|
};
|
||||||
|
|
||||||
|
const $userGroupJoiningsRepository: Provider = {
|
||||||
|
provide: DI.userGroupJoiningsRepository,
|
||||||
|
useFactory: (db: DataSource) => db.getRepository(UserGroupJoining),
|
||||||
|
inject: [DI.db],
|
||||||
|
};
|
||||||
|
|
||||||
|
const $userGroupInvitationsRepository: Provider = {
|
||||||
|
provide: DI.userGroupInvitationsRepository,
|
||||||
|
useFactory: (db: DataSource) => db.getRepository(UserGroupInvitation),
|
||||||
|
inject: [DI.db],
|
||||||
|
};
|
||||||
|
|
||||||
const $userNotePiningsRepository: Provider = {
|
const $userNotePiningsRepository: Provider = {
|
||||||
provide: DI.userNotePiningsRepository,
|
provide: DI.userNotePiningsRepository,
|
||||||
useFactory: (db: DataSource) => db.getRepository(UserNotePining),
|
useFactory: (db: DataSource) => db.getRepository(UserNotePining),
|
||||||
@ -411,6 +429,9 @@ const $roleAssignmentsRepository: Provider = {
|
|||||||
$userPublickeysRepository,
|
$userPublickeysRepository,
|
||||||
$userListsRepository,
|
$userListsRepository,
|
||||||
$userListJoiningsRepository,
|
$userListJoiningsRepository,
|
||||||
|
$userGroupsRepository,
|
||||||
|
$userGroupJoiningsRepository,
|
||||||
|
$userGroupInvitationsRepository,
|
||||||
$userNotePiningsRepository,
|
$userNotePiningsRepository,
|
||||||
$userIpsRepository,
|
$userIpsRepository,
|
||||||
$usedUsernamesRepository,
|
$usedUsernamesRepository,
|
||||||
@ -477,6 +498,9 @@ const $roleAssignmentsRepository: Provider = {
|
|||||||
$userPublickeysRepository,
|
$userPublickeysRepository,
|
||||||
$userListsRepository,
|
$userListsRepository,
|
||||||
$userListJoiningsRepository,
|
$userListJoiningsRepository,
|
||||||
|
$userGroupsRepository,
|
||||||
|
$userGroupJoiningsRepository,
|
||||||
|
$userGroupInvitationsRepository,
|
||||||
$userNotePiningsRepository,
|
$userNotePiningsRepository,
|
||||||
$userIpsRepository,
|
$userIpsRepository,
|
||||||
$usedUsernamesRepository,
|
$usedUsernamesRepository,
|
||||||
|
@ -2,6 +2,7 @@ import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typ
|
|||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
import { UserList } from './UserList.js';
|
import { UserList } from './UserList.js';
|
||||||
|
import { UserGroupJoining } from './UserGroupJoining.js';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
export class Antenna {
|
export class Antenna {
|
||||||
@ -32,8 +33,8 @@ export class Antenna {
|
|||||||
})
|
})
|
||||||
public name: string;
|
public name: string;
|
||||||
|
|
||||||
@Column('enum', { enum: ['home', 'all', 'users', 'list'] })
|
@Column('enum', { enum: ['home', 'all', 'users', 'list', 'group'] })
|
||||||
public src: 'home' | 'all' | 'users' | 'list';
|
public src: 'home' | 'all' | 'users' | 'list' | 'group';
|
||||||
|
|
||||||
@Column({
|
@Column({
|
||||||
...id(),
|
...id(),
|
||||||
@ -47,6 +48,18 @@ export class Antenna {
|
|||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public userList: UserList | null;
|
public userList: UserList | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public userGroupJoiningId: UserGroupJoining['id'] | null;
|
||||||
|
|
||||||
|
@ManyToOne(type => UserGroupJoining, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public userGroupJoining: UserGroupJoining | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 1024, array: true,
|
length: 1024, array: true,
|
||||||
default: '{}',
|
default: '{}',
|
||||||
|
@ -4,6 +4,7 @@ import { id } from '../id.js';
|
|||||||
import { User } from './User.js';
|
import { User } from './User.js';
|
||||||
import { Note } from './Note.js';
|
import { Note } from './Note.js';
|
||||||
import { FollowRequest } from './FollowRequest.js';
|
import { FollowRequest } from './FollowRequest.js';
|
||||||
|
import { UserGroupInvitation } from './UserGroupInvitation.js';
|
||||||
import { AccessToken } from './AccessToken.js';
|
import { AccessToken } from './AccessToken.js';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@ -62,6 +63,7 @@ export class Notification {
|
|||||||
* pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
|
* pollEnded - 自分のアンケートもしくは自分が投票したアンケートが終了した
|
||||||
* receiveFollowRequest - フォローリクエストされた
|
* receiveFollowRequest - フォローリクエストされた
|
||||||
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
* followRequestAccepted - 自分の送ったフォローリクエストが承認された
|
||||||
|
* groupInvited - グループに招待された
|
||||||
* achievementEarned - 実績を獲得
|
* achievementEarned - 実績を獲得
|
||||||
* app - アプリ通知
|
* app - アプリ通知
|
||||||
*/
|
*/
|
||||||
@ -106,6 +108,18 @@ export class Notification {
|
|||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public followRequest: FollowRequest | null;
|
public followRequest: FollowRequest | null;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
public userGroupInvitationId: UserGroupInvitation['id'] | null;
|
||||||
|
|
||||||
|
@ManyToOne(type => UserGroupInvitation, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public userGroupInvitation: UserGroupInvitation | null;
|
||||||
|
|
||||||
@Column('varchar', {
|
@Column('varchar', {
|
||||||
length: 128, nullable: true,
|
length: 128, nullable: true,
|
||||||
})
|
})
|
||||||
|
46
packages/backend/src/models/entities/UserGroup.ts
Normal file
46
packages/backend/src/models/entities/UserGroup.ts
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm';
|
||||||
|
import { id } from '../id.js';
|
||||||
|
import { User } from './User.js';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class UserGroup {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
comment: 'The created date of the UserGroup.',
|
||||||
|
})
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
@Column('varchar', {
|
||||||
|
length: 256,
|
||||||
|
})
|
||||||
|
public name: string;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
comment: 'The ID of owner.',
|
||||||
|
})
|
||||||
|
public userId: User['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => User, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user: User | null;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public isPrivate: boolean;
|
||||||
|
|
||||||
|
constructor(data: Partial<UserGroup>) {
|
||||||
|
if (data == null) return;
|
||||||
|
|
||||||
|
for (const [k, v] of Object.entries(data)) {
|
||||||
|
(this as any)[k] = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
packages/backend/src/models/entities/UserGroupInvitation.ts
Normal file
42
packages/backend/src/models/entities/UserGroupInvitation.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { id } from '../id.js';
|
||||||
|
import { User } from './User.js';
|
||||||
|
import { UserGroup } from './UserGroup.js';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
@Index(['userId', 'userGroupId'], { unique: true })
|
||||||
|
export class UserGroupInvitation {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
comment: 'The created date of the UserGroupInvitation.',
|
||||||
|
})
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
comment: 'The user ID.',
|
||||||
|
})
|
||||||
|
public userId: User['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => User, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user: User | null;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
comment: 'The group ID.',
|
||||||
|
})
|
||||||
|
public userGroupId: UserGroup['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => UserGroup, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public userGroup: UserGroup | null;
|
||||||
|
}
|
42
packages/backend/src/models/entities/UserGroupJoining.ts
Normal file
42
packages/backend/src/models/entities/UserGroupJoining.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { id } from '../id.js';
|
||||||
|
import { User } from './User.js';
|
||||||
|
import { UserGroup } from './UserGroup.js';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
@Index(['userId', 'userGroupId'], { unique: true })
|
||||||
|
export class UserGroupJoining {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
comment: 'The created date of the UserGroupJoining.',
|
||||||
|
})
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
comment: 'The user ID.',
|
||||||
|
})
|
||||||
|
public userId: User['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => User, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user: User | null;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
comment: 'The group ID.',
|
||||||
|
})
|
||||||
|
public userGroupId: UserGroup['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => UserGroup, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public userGroup: UserGroup | null;
|
||||||
|
}
|
@ -71,7 +71,7 @@ export class UserProfile {
|
|||||||
public emailVerified: boolean;
|
public emailVerified: boolean;
|
||||||
|
|
||||||
@Column('jsonb', {
|
@Column('jsonb', {
|
||||||
default: ['follow', 'receiveFollowRequest'],
|
default: ['follow', 'receiveFollowRequest', 'groupInvited'],
|
||||||
})
|
})
|
||||||
public emailNotificationTypes: string[];
|
public emailNotificationTypes: string[];
|
||||||
|
|
||||||
|
@ -46,6 +46,9 @@ import { Signin } from '@/models/entities/Signin.js';
|
|||||||
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
||||||
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { User } from '@/models/entities/User.js';
|
||||||
|
import { UserGroup } from '@/models/entities/UserGroup.js';
|
||||||
|
import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
|
||||||
|
import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js';
|
||||||
import { UserIp } from '@/models/entities/UserIp.js';
|
import { UserIp } from '@/models/entities/UserIp.js';
|
||||||
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import { UserList } from '@/models/entities/UserList.js';
|
import { UserList } from '@/models/entities/UserList.js';
|
||||||
@ -113,6 +116,9 @@ export {
|
|||||||
SwSubscription,
|
SwSubscription,
|
||||||
UsedUsername,
|
UsedUsername,
|
||||||
User,
|
User,
|
||||||
|
UserGroup,
|
||||||
|
UserGroupInvitation,
|
||||||
|
UserGroupJoining,
|
||||||
UserIp,
|
UserIp,
|
||||||
UserKeypair,
|
UserKeypair,
|
||||||
UserList,
|
UserList,
|
||||||
@ -179,6 +185,9 @@ export type SigninsRepository = Repository<Signin>;
|
|||||||
export type SwSubscriptionsRepository = Repository<SwSubscription>;
|
export type SwSubscriptionsRepository = Repository<SwSubscription>;
|
||||||
export type UsedUsernamesRepository = Repository<UsedUsername>;
|
export type UsedUsernamesRepository = Repository<UsedUsername>;
|
||||||
export type UsersRepository = Repository<User>;
|
export type UsersRepository = Repository<User>;
|
||||||
|
export type UserGroupsRepository = Repository<UserGroup>;
|
||||||
|
export type UserGroupInvitationsRepository = Repository<UserGroupInvitation>;
|
||||||
|
export type UserGroupJoiningsRepository = Repository<UserGroupJoining>;
|
||||||
export type UserIpsRepository = Repository<UserIp>;
|
export type UserIpsRepository = Repository<UserIp>;
|
||||||
export type UserKeypairsRepository = Repository<UserKeypair>;
|
export type UserKeypairsRepository = Repository<UserKeypair>;
|
||||||
export type UserListsRepository = Repository<UserList>;
|
export type UserListsRepository = Repository<UserList>;
|
||||||
|
@ -42,13 +42,18 @@ export const packedAntennaSchema = {
|
|||||||
src: {
|
src: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
enum: ['home', 'all', 'users', 'list'],
|
enum: ['home', 'all', 'users', 'list', 'group'],
|
||||||
},
|
},
|
||||||
userListId: {
|
userListId: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
format: 'id',
|
format: 'id',
|
||||||
},
|
},
|
||||||
|
userGroupId: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: true,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
users: {
|
users: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
34
packages/backend/src/models/schema/user-group.ts
Normal file
34
packages/backend/src/models/schema/user-group.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export const packedUserGroupSchema = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
example: 'xxxxxxxxxx',
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'date-time',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
},
|
||||||
|
ownerId: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
userIds: {
|
||||||
|
type: 'array',
|
||||||
|
nullable: false, optional: true,
|
||||||
|
items: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
@ -54,6 +54,9 @@ import { Signin } from '@/models/entities/Signin.js';
|
|||||||
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
import { SwSubscription } from '@/models/entities/SwSubscription.js';
|
||||||
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
import { UsedUsername } from '@/models/entities/UsedUsername.js';
|
||||||
import { User } from '@/models/entities/User.js';
|
import { User } from '@/models/entities/User.js';
|
||||||
|
import { UserGroup } from '@/models/entities/UserGroup.js';
|
||||||
|
import { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
|
||||||
|
import { UserGroupJoining } from '@/models/entities/UserGroupJoining.js';
|
||||||
import { UserIp } from '@/models/entities/UserIp.js';
|
import { UserIp } from '@/models/entities/UserIp.js';
|
||||||
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
import { UserKeypair } from '@/models/entities/UserKeypair.js';
|
||||||
import { UserList } from '@/models/entities/UserList.js';
|
import { UserList } from '@/models/entities/UserList.js';
|
||||||
@ -133,6 +136,9 @@ export const entities = [
|
|||||||
UserPublickey,
|
UserPublickey,
|
||||||
UserList,
|
UserList,
|
||||||
UserListJoining,
|
UserListJoining,
|
||||||
|
UserGroup,
|
||||||
|
UserGroupJoining,
|
||||||
|
UserGroupInvitation,
|
||||||
UserNotePining,
|
UserNotePining,
|
||||||
UserSecurityKey,
|
UserSecurityKey,
|
||||||
UsedUsername,
|
UsedUsername,
|
||||||
|
@ -211,6 +211,7 @@ import * as ep___i_signinHistory from './endpoints/i/signin-history.js';
|
|||||||
import * as ep___i_unpin from './endpoints/i/unpin.js';
|
import * as ep___i_unpin from './endpoints/i/unpin.js';
|
||||||
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
|
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
|
||||||
import * as ep___i_update from './endpoints/i/update.js';
|
import * as ep___i_update from './endpoints/i/update.js';
|
||||||
|
import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js';
|
||||||
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
|
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
|
||||||
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
|
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
|
||||||
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
|
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
|
||||||
@ -293,6 +294,18 @@ import * as ep___users_followers from './endpoints/users/followers.js';
|
|||||||
import * as ep___users_following from './endpoints/users/following.js';
|
import * as ep___users_following from './endpoints/users/following.js';
|
||||||
import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js';
|
import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js';
|
||||||
import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js';
|
import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js';
|
||||||
|
import * as ep___users_groups_create from './endpoints/users/groups/create.js';
|
||||||
|
import * as ep___users_groups_delete from './endpoints/users/groups/delete.js';
|
||||||
|
import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js';
|
||||||
|
import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js';
|
||||||
|
import * as ep___users_groups_invite from './endpoints/users/groups/invite.js';
|
||||||
|
import * as ep___users_groups_joined from './endpoints/users/groups/joined.js';
|
||||||
|
import * as ep___users_groups_leave from './endpoints/users/groups/leave.js';
|
||||||
|
import * as ep___users_groups_owned from './endpoints/users/groups/owned.js';
|
||||||
|
import * as ep___users_groups_pull from './endpoints/users/groups/pull.js';
|
||||||
|
import * as ep___users_groups_show from './endpoints/users/groups/show.js';
|
||||||
|
import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js';
|
||||||
|
import * as ep___users_groups_update from './endpoints/users/groups/update.js';
|
||||||
import * as ep___users_lists_create from './endpoints/users/lists/create.js';
|
import * as ep___users_lists_create from './endpoints/users/lists/create.js';
|
||||||
import * as ep___users_lists_delete from './endpoints/users/lists/delete.js';
|
import * as ep___users_lists_delete from './endpoints/users/lists/delete.js';
|
||||||
import * as ep___users_lists_list from './endpoints/users/lists/list.js';
|
import * as ep___users_lists_list from './endpoints/users/lists/list.js';
|
||||||
@ -527,6 +540,7 @@ const $i_signinHistory: Provider = { provide: 'ep:i/signin-history', useClass: e
|
|||||||
const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.default };
|
const $i_unpin: Provider = { provide: 'ep:i/unpin', useClass: ep___i_unpin.default };
|
||||||
const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default };
|
const $i_updateEmail: Provider = { provide: 'ep:i/update-email', useClass: ep___i_updateEmail.default };
|
||||||
const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default };
|
const $i_update: Provider = { provide: 'ep:i/update', useClass: ep___i_update.default };
|
||||||
|
const $i_userGroupInvites: Provider = { provide: 'ep:i/user-group-invites', useClass: ep___i_userGroupInvites.default };
|
||||||
const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default };
|
const $i_webhooks_create: Provider = { provide: 'ep:i/webhooks/create', useClass: ep___i_webhooks_create.default };
|
||||||
const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default };
|
const $i_webhooks_list: Provider = { provide: 'ep:i/webhooks/list', useClass: ep___i_webhooks_list.default };
|
||||||
const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default };
|
const $i_webhooks_show: Provider = { provide: 'ep:i/webhooks/show', useClass: ep___i_webhooks_show.default };
|
||||||
@ -609,6 +623,18 @@ const $users_followers: Provider = { provide: 'ep:users/followers', useClass: ep
|
|||||||
const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default };
|
const $users_following: Provider = { provide: 'ep:users/following', useClass: ep___users_following.default };
|
||||||
const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default };
|
const $users_gallery_posts: Provider = { provide: 'ep:users/gallery/posts', useClass: ep___users_gallery_posts.default };
|
||||||
const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default };
|
const $users_getFrequentlyRepliedUsers: Provider = { provide: 'ep:users/get-frequently-replied-users', useClass: ep___users_getFrequentlyRepliedUsers.default };
|
||||||
|
const $users_groups_create: Provider = { provide: 'ep:users/groups/create', useClass: ep___users_groups_create.default };
|
||||||
|
const $users_groups_delete: Provider = { provide: 'ep:users/groups/delete', useClass: ep___users_groups_delete.default };
|
||||||
|
const $users_groups_invitations_accept: Provider = { provide: 'ep:users/groups/invitations/accept', useClass: ep___users_groups_invitations_accept.default };
|
||||||
|
const $users_groups_invitations_reject: Provider = { provide: 'ep:users/groups/invitations/reject', useClass: ep___users_groups_invitations_reject.default };
|
||||||
|
const $users_groups_invite: Provider = { provide: 'ep:users/groups/invite', useClass: ep___users_groups_invite.default };
|
||||||
|
const $users_groups_joined: Provider = { provide: 'ep:users/groups/joined', useClass: ep___users_groups_joined.default };
|
||||||
|
const $users_groups_leave: Provider = { provide: 'ep:users/groups/leave', useClass: ep___users_groups_leave.default };
|
||||||
|
const $users_groups_owned: Provider = { provide: 'ep:users/groups/owned', useClass: ep___users_groups_owned.default };
|
||||||
|
const $users_groups_pull: Provider = { provide: 'ep:users/groups/pull', useClass: ep___users_groups_pull.default };
|
||||||
|
const $users_groups_show: Provider = { provide: 'ep:users/groups/show', useClass: ep___users_groups_show.default };
|
||||||
|
const $users_groups_transfer: Provider = { provide: 'ep:users/groups/transfer', useClass: ep___users_groups_transfer.default };
|
||||||
|
const $users_groups_update: Provider = { provide: 'ep:users/groups/update', useClass: ep___users_groups_update.default };
|
||||||
const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default };
|
const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default };
|
||||||
const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default };
|
const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default };
|
||||||
const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default };
|
const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default };
|
||||||
@ -847,6 +873,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$i_unpin,
|
$i_unpin,
|
||||||
$i_updateEmail,
|
$i_updateEmail,
|
||||||
$i_update,
|
$i_update,
|
||||||
|
$i_userGroupInvites,
|
||||||
$i_webhooks_create,
|
$i_webhooks_create,
|
||||||
$i_webhooks_list,
|
$i_webhooks_list,
|
||||||
$i_webhooks_show,
|
$i_webhooks_show,
|
||||||
@ -929,6 +956,18 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$users_following,
|
$users_following,
|
||||||
$users_gallery_posts,
|
$users_gallery_posts,
|
||||||
$users_getFrequentlyRepliedUsers,
|
$users_getFrequentlyRepliedUsers,
|
||||||
|
$users_groups_create,
|
||||||
|
$users_groups_delete,
|
||||||
|
$users_groups_invitations_accept,
|
||||||
|
$users_groups_invitations_reject,
|
||||||
|
$users_groups_invite,
|
||||||
|
$users_groups_joined,
|
||||||
|
$users_groups_leave,
|
||||||
|
$users_groups_owned,
|
||||||
|
$users_groups_pull,
|
||||||
|
$users_groups_show,
|
||||||
|
$users_groups_transfer,
|
||||||
|
$users_groups_update,
|
||||||
$users_lists_create,
|
$users_lists_create,
|
||||||
$users_lists_delete,
|
$users_lists_delete,
|
||||||
$users_lists_list,
|
$users_lists_list,
|
||||||
@ -1161,6 +1200,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$i_unpin,
|
$i_unpin,
|
||||||
$i_updateEmail,
|
$i_updateEmail,
|
||||||
$i_update,
|
$i_update,
|
||||||
|
$i_userGroupInvites,
|
||||||
$i_webhooks_create,
|
$i_webhooks_create,
|
||||||
$i_webhooks_list,
|
$i_webhooks_list,
|
||||||
$i_webhooks_show,
|
$i_webhooks_show,
|
||||||
@ -1241,6 +1281,18 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
|||||||
$users_following,
|
$users_following,
|
||||||
$users_gallery_posts,
|
$users_gallery_posts,
|
||||||
$users_getFrequentlyRepliedUsers,
|
$users_getFrequentlyRepliedUsers,
|
||||||
|
$users_groups_create,
|
||||||
|
$users_groups_delete,
|
||||||
|
$users_groups_invitations_accept,
|
||||||
|
$users_groups_invitations_reject,
|
||||||
|
$users_groups_invite,
|
||||||
|
$users_groups_joined,
|
||||||
|
$users_groups_leave,
|
||||||
|
$users_groups_owned,
|
||||||
|
$users_groups_pull,
|
||||||
|
$users_groups_show,
|
||||||
|
$users_groups_transfer,
|
||||||
|
$users_groups_update,
|
||||||
$users_lists_create,
|
$users_lists_create,
|
||||||
$users_lists_delete,
|
$users_lists_delete,
|
||||||
$users_lists_list,
|
$users_lists_list,
|
||||||
|
@ -210,6 +210,7 @@ import * as ep___i_signinHistory from './endpoints/i/signin-history.js';
|
|||||||
import * as ep___i_unpin from './endpoints/i/unpin.js';
|
import * as ep___i_unpin from './endpoints/i/unpin.js';
|
||||||
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
|
import * as ep___i_updateEmail from './endpoints/i/update-email.js';
|
||||||
import * as ep___i_update from './endpoints/i/update.js';
|
import * as ep___i_update from './endpoints/i/update.js';
|
||||||
|
import * as ep___i_userGroupInvites from './endpoints/i/user-group-invites.js';
|
||||||
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
|
import * as ep___i_webhooks_create from './endpoints/i/webhooks/create.js';
|
||||||
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
|
import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js';
|
||||||
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
|
import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js';
|
||||||
@ -292,6 +293,18 @@ import * as ep___users_followers from './endpoints/users/followers.js';
|
|||||||
import * as ep___users_following from './endpoints/users/following.js';
|
import * as ep___users_following from './endpoints/users/following.js';
|
||||||
import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js';
|
import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js';
|
||||||
import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js';
|
import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js';
|
||||||
|
import * as ep___users_groups_create from './endpoints/users/groups/create.js';
|
||||||
|
import * as ep___users_groups_delete from './endpoints/users/groups/delete.js';
|
||||||
|
import * as ep___users_groups_invitations_accept from './endpoints/users/groups/invitations/accept.js';
|
||||||
|
import * as ep___users_groups_invitations_reject from './endpoints/users/groups/invitations/reject.js';
|
||||||
|
import * as ep___users_groups_invite from './endpoints/users/groups/invite.js';
|
||||||
|
import * as ep___users_groups_joined from './endpoints/users/groups/joined.js';
|
||||||
|
import * as ep___users_groups_leave from './endpoints/users/groups/leave.js';
|
||||||
|
import * as ep___users_groups_owned from './endpoints/users/groups/owned.js';
|
||||||
|
import * as ep___users_groups_pull from './endpoints/users/groups/pull.js';
|
||||||
|
import * as ep___users_groups_show from './endpoints/users/groups/show.js';
|
||||||
|
import * as ep___users_groups_transfer from './endpoints/users/groups/transfer.js';
|
||||||
|
import * as ep___users_groups_update from './endpoints/users/groups/update.js';
|
||||||
import * as ep___users_lists_create from './endpoints/users/lists/create.js';
|
import * as ep___users_lists_create from './endpoints/users/lists/create.js';
|
||||||
import * as ep___users_lists_delete from './endpoints/users/lists/delete.js';
|
import * as ep___users_lists_delete from './endpoints/users/lists/delete.js';
|
||||||
import * as ep___users_lists_list from './endpoints/users/lists/list.js';
|
import * as ep___users_lists_list from './endpoints/users/lists/list.js';
|
||||||
@ -524,6 +537,7 @@ const eps = [
|
|||||||
['i/unpin', ep___i_unpin],
|
['i/unpin', ep___i_unpin],
|
||||||
['i/update-email', ep___i_updateEmail],
|
['i/update-email', ep___i_updateEmail],
|
||||||
['i/update', ep___i_update],
|
['i/update', ep___i_update],
|
||||||
|
['i/user-group-invites', ep___i_userGroupInvites],
|
||||||
['i/webhooks/create', ep___i_webhooks_create],
|
['i/webhooks/create', ep___i_webhooks_create],
|
||||||
['i/webhooks/list', ep___i_webhooks_list],
|
['i/webhooks/list', ep___i_webhooks_list],
|
||||||
['i/webhooks/show', ep___i_webhooks_show],
|
['i/webhooks/show', ep___i_webhooks_show],
|
||||||
@ -606,6 +620,18 @@ const eps = [
|
|||||||
['users/following', ep___users_following],
|
['users/following', ep___users_following],
|
||||||
['users/gallery/posts', ep___users_gallery_posts],
|
['users/gallery/posts', ep___users_gallery_posts],
|
||||||
['users/get-frequently-replied-users', ep___users_getFrequentlyRepliedUsers],
|
['users/get-frequently-replied-users', ep___users_getFrequentlyRepliedUsers],
|
||||||
|
['users/groups/create', ep___users_groups_create],
|
||||||
|
['users/groups/delete', ep___users_groups_delete],
|
||||||
|
['users/groups/invitations/accept', ep___users_groups_invitations_accept],
|
||||||
|
['users/groups/invitations/reject', ep___users_groups_invitations_reject],
|
||||||
|
['users/groups/invite', ep___users_groups_invite],
|
||||||
|
['users/groups/joined', ep___users_groups_joined],
|
||||||
|
['users/groups/leave', ep___users_groups_leave],
|
||||||
|
['users/groups/owned', ep___users_groups_owned],
|
||||||
|
['users/groups/pull', ep___users_groups_pull],
|
||||||
|
['users/groups/show', ep___users_groups_show],
|
||||||
|
['users/groups/transfer', ep___users_groups_transfer],
|
||||||
|
['users/groups/update', ep___users_groups_update],
|
||||||
['users/lists/create', ep___users_lists_create],
|
['users/lists/create', ep___users_lists_create],
|
||||||
['users/lists/delete', ep___users_lists_delete],
|
['users/lists/delete', ep___users_lists_delete],
|
||||||
['users/lists/list', ep___users_lists_list],
|
['users/lists/list', ep___users_lists_list],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import { IdService } from '@/core/IdService.js';
|
import { IdService } from '@/core/IdService.js';
|
||||||
import type { UserListsRepository, AntennasRepository } from '@/models/index.js';
|
import type { UserListsRepository, UserGroupJoiningsRepository, AntennasRepository } from '@/models/index.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
|
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
@ -22,6 +22,12 @@ export const meta = {
|
|||||||
id: '95063e93-a283-4b8b-9aa5-bcdb8df69a7f',
|
id: '95063e93-a283-4b8b-9aa5-bcdb8df69a7f',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
noSuchUserGroup: {
|
||||||
|
message: 'No such user group.',
|
||||||
|
code: 'NO_SUCH_USER_GROUP',
|
||||||
|
id: 'aa3c0b9a-8cae-47c0-92ac-202ce5906682',
|
||||||
|
},
|
||||||
|
|
||||||
tooManyAntennas: {
|
tooManyAntennas: {
|
||||||
message: 'You cannot create antenna any more.',
|
message: 'You cannot create antenna any more.',
|
||||||
code: 'TOO_MANY_ANTENNAS',
|
code: 'TOO_MANY_ANTENNAS',
|
||||||
@ -40,8 +46,9 @@ export const paramDef = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: 'string', minLength: 1, maxLength: 100 },
|
name: { type: 'string', minLength: 1, maxLength: 100 },
|
||||||
src: { type: 'string', enum: ['home', 'all', 'users', 'list'] },
|
src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'group'] },
|
||||||
userListId: { type: 'string', format: 'misskey:id', nullable: true },
|
userListId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
userGroupId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
keywords: { type: 'array', items: {
|
keywords: { type: 'array', items: {
|
||||||
type: 'array', items: {
|
type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -73,6 +80,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
@Inject(DI.userListsRepository)
|
@Inject(DI.userListsRepository)
|
||||||
private userListsRepository: UserListsRepository,
|
private userListsRepository: UserListsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
private antennaEntityService: AntennaEntityService,
|
private antennaEntityService: AntennaEntityService,
|
||||||
private roleService: RoleService,
|
private roleService: RoleService,
|
||||||
private idService: IdService,
|
private idService: IdService,
|
||||||
@ -87,6 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let userList;
|
let userList;
|
||||||
|
let userGroupJoining;
|
||||||
|
|
||||||
if (ps.src === 'list' && ps.userListId) {
|
if (ps.src === 'list' && ps.userListId) {
|
||||||
userList = await this.userListsRepository.findOneBy({
|
userList = await this.userListsRepository.findOneBy({
|
||||||
@ -97,6 +108,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
if (userList == null) {
|
if (userList == null) {
|
||||||
throw new ApiError(meta.errors.noSuchUserList);
|
throw new ApiError(meta.errors.noSuchUserList);
|
||||||
}
|
}
|
||||||
|
} else if (ps.src === 'group' && ps.userGroupId) {
|
||||||
|
userGroupJoining = await this.userGroupJoiningsRepository.findOneBy({
|
||||||
|
userGroupId: ps.userGroupId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroupJoining == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchUserGroup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const antenna = await this.antennasRepository.insert({
|
const antenna = await this.antennasRepository.insert({
|
||||||
@ -106,6 +126,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
name: ps.name,
|
name: ps.name,
|
||||||
src: ps.src,
|
src: ps.src,
|
||||||
userListId: userList ? userList.id : null,
|
userListId: userList ? userList.id : null,
|
||||||
|
userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null,
|
||||||
keywords: ps.keywords,
|
keywords: ps.keywords,
|
||||||
excludeKeywords: ps.excludeKeywords,
|
excludeKeywords: ps.excludeKeywords,
|
||||||
users: ps.users,
|
users: ps.users,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
import type { AntennasRepository, UserListsRepository } from '@/models/index.js';
|
import type { AntennasRepository, UserListsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
|
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
@ -25,6 +25,12 @@ export const meta = {
|
|||||||
code: 'NO_SUCH_USER_LIST',
|
code: 'NO_SUCH_USER_LIST',
|
||||||
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
noSuchUserGroup: {
|
||||||
|
message: 'No such user group.',
|
||||||
|
code: 'NO_SUCH_USER_GROUP',
|
||||||
|
id: '109ed789-b6eb-456e-b8a9-6059d567d385',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
@ -39,8 +45,9 @@ export const paramDef = {
|
|||||||
properties: {
|
properties: {
|
||||||
antennaId: { type: 'string', format: 'misskey:id' },
|
antennaId: { type: 'string', format: 'misskey:id' },
|
||||||
name: { type: 'string', minLength: 1, maxLength: 100 },
|
name: { type: 'string', minLength: 1, maxLength: 100 },
|
||||||
src: { type: 'string', enum: ['home', 'all', 'users', 'list'] },
|
src: { type: 'string', enum: ['home', 'all', 'users', 'list', 'group'] },
|
||||||
userListId: { type: 'string', format: 'misskey:id', nullable: true },
|
userListId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
|
userGroupId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
keywords: { type: 'array', items: {
|
keywords: { type: 'array', items: {
|
||||||
type: 'array', items: {
|
type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -71,6 +78,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
|
|
||||||
@Inject(DI.userListsRepository)
|
@Inject(DI.userListsRepository)
|
||||||
private userListsRepository: UserListsRepository,
|
private userListsRepository: UserListsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
private antennaEntityService: AntennaEntityService,
|
private antennaEntityService: AntennaEntityService,
|
||||||
private globalEventService: GlobalEventService,
|
private globalEventService: GlobalEventService,
|
||||||
@ -87,6 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let userList;
|
let userList;
|
||||||
|
let userGroupJoining;
|
||||||
|
|
||||||
if (ps.src === 'list' && ps.userListId) {
|
if (ps.src === 'list' && ps.userListId) {
|
||||||
userList = await this.userListsRepository.findOneBy({
|
userList = await this.userListsRepository.findOneBy({
|
||||||
@ -97,12 +108,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
|||||||
if (userList == null) {
|
if (userList == null) {
|
||||||
throw new ApiError(meta.errors.noSuchUserList);
|
throw new ApiError(meta.errors.noSuchUserList);
|
||||||
}
|
}
|
||||||
|
} else if (ps.src === 'group' && ps.userGroupId) {
|
||||||
|
userGroupJoining = await this.userGroupJoiningsRepository.findOneBy({
|
||||||
|
userGroupId: ps.userGroupId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroupJoining == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchUserGroup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.antennasRepository.update(antenna.id, {
|
await this.antennasRepository.update(antenna.id, {
|
||||||
name: ps.name,
|
name: ps.name,
|
||||||
src: ps.src,
|
src: ps.src,
|
||||||
userListId: userList ? userList.id : null,
|
userListId: userList ? userList.id : null,
|
||||||
|
userGroupJoiningId: userGroupJoining ? userGroupJoining.id : null,
|
||||||
keywords: ps.keywords,
|
keywords: ps.keywords,
|
||||||
excludeKeywords: ps.excludeKeywords,
|
excludeKeywords: ps.excludeKeywords,
|
||||||
users: ps.users,
|
users: ps.users,
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import type { UserGroupInvitationsRepository } from '@/models/index.js';
|
||||||
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
|
import { UserGroupInvitationEntityService } from '@/core/entities/UserGroupInvitationEntityService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['account', 'groups'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:user-groups',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
properties: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
format: 'id',
|
||||||
|
},
|
||||||
|
group: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserGroup',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupInvitationsRepository)
|
||||||
|
private userGroupInvitationsRepository: UserGroupInvitationsRepository,
|
||||||
|
|
||||||
|
private userGroupInvitationEntityService: UserGroupInvitationEntityService,
|
||||||
|
private queryService: QueryService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const query = this.queryService.makePaginationQuery(this.userGroupInvitationsRepository.createQueryBuilder('invitation'), ps.sinceId, ps.untilId)
|
||||||
|
.andWhere('invitation.userId = :meId', { meId: me.id })
|
||||||
|
.leftJoinAndSelect('invitation.userGroup', 'user_group');
|
||||||
|
|
||||||
|
const invitations = await query
|
||||||
|
.take(ps.limit)
|
||||||
|
.getMany();
|
||||||
|
|
||||||
|
return await this.userGroupInvitationEntityService.packMany(invitations);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import ms from 'ms';
|
||||||
|
import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
||||||
|
import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Create a new group.',
|
||||||
|
|
||||||
|
limit: {
|
||||||
|
duration: ms('1hour'),
|
||||||
|
max: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserGroup',
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
name: { type: 'string', minLength: 1, maxLength: 100 },
|
||||||
|
},
|
||||||
|
required: ['name'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private userGroupEntityService: UserGroupEntityService,
|
||||||
|
private idService: IdService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const userGroup = await this.userGroupsRepository.insert({
|
||||||
|
id: this.idService.genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
userId: me.id,
|
||||||
|
name: ps.name,
|
||||||
|
} as UserGroup).then(x => this.userGroupsRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
|
|
||||||
|
// Push the owner
|
||||||
|
await this.userGroupJoiningsRepository.insert({
|
||||||
|
id: this.idService.genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
userId: me.id,
|
||||||
|
userGroupId: userGroup.id,
|
||||||
|
} as UserGroupJoining);
|
||||||
|
|
||||||
|
return await this.userGroupEntityService.pack(userGroup);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Delete an existing group.',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchGroup: {
|
||||||
|
message: 'No such group.',
|
||||||
|
code: 'NO_SUCH_GROUP',
|
||||||
|
id: '63dbd64c-cd77-413f-8e08-61781e210b38',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const userGroup = await this.userGroupsRepository.findOneBy({
|
||||||
|
id: ps.groupId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.userGroupsRepository.delete(userGroup.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupInvitationsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import type { UserGroupJoining } from '@/models/entities/UserGroupJoining.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'users'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Join a group the authenticated user has been invited to.',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchInvitation: {
|
||||||
|
message: 'No such invitation.',
|
||||||
|
code: 'NO_SUCH_INVITATION',
|
||||||
|
id: '98c11eca-c890-4f42-9806-c8c8303ebb5e',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
invitationId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['invitationId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupInvitationsRepository)
|
||||||
|
private userGroupInvitationsRepository: UserGroupInvitationsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private idService: IdService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the invitation
|
||||||
|
const invitation = await this.userGroupInvitationsRepository.findOneBy({
|
||||||
|
id: ps.invitationId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invitation == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchInvitation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invitation.userId !== me.id) {
|
||||||
|
throw new ApiError(meta.errors.noSuchInvitation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the user
|
||||||
|
await this.userGroupJoiningsRepository.insert({
|
||||||
|
id: this.idService.genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
userId: me.id,
|
||||||
|
userGroupId: invitation.userGroupId,
|
||||||
|
} as UserGroupJoining);
|
||||||
|
|
||||||
|
this.userGroupInvitationsRepository.delete(invitation.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupInvitationsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'users'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Delete an existing group invitation for the authenticated user without joining the group.',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchInvitation: {
|
||||||
|
message: 'No such invitation.',
|
||||||
|
code: 'NO_SUCH_INVITATION',
|
||||||
|
id: 'ad7471d4-2cd9-44b4-ac68-e7136b4ce656',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
invitationId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['invitationId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupInvitationsRepository)
|
||||||
|
private userGroupInvitationsRepository: UserGroupInvitationsRepository,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the invitation
|
||||||
|
const invitation = await this.userGroupInvitationsRepository.findOneBy({
|
||||||
|
id: ps.invitationId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invitation == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchInvitation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invitation.userId !== me.id) {
|
||||||
|
throw new ApiError(meta.errors.noSuchInvitation);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.userGroupInvitationsRepository.delete(invitation.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
122
packages/backend/src/server/api/endpoints/users/groups/invite.ts
Normal file
122
packages/backend/src/server/api/endpoints/users/groups/invite.ts
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository, UserGroupJoiningsRepository, UserGroupInvitationsRepository } from '@/models/index.js';
|
||||||
|
import { IdService } from '@/core/IdService.js';
|
||||||
|
import type { UserGroupInvitation } from '@/models/entities/UserGroupInvitation.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { CreateNotificationService } from '@/core/CreateNotificationService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'users'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Invite a user to an existing group.',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchGroup: {
|
||||||
|
message: 'No such group.',
|
||||||
|
code: 'NO_SUCH_GROUP',
|
||||||
|
id: '583f8bc0-8eee-4b78-9299-1e14fc91e409',
|
||||||
|
},
|
||||||
|
|
||||||
|
noSuchUser: {
|
||||||
|
message: 'No such user.',
|
||||||
|
code: 'NO_SUCH_USER',
|
||||||
|
id: 'da52de61-002c-475b-90e1-ba64f9cf13a8',
|
||||||
|
},
|
||||||
|
|
||||||
|
alreadyAdded: {
|
||||||
|
message: 'That user has already been added to that group.',
|
||||||
|
code: 'ALREADY_ADDED',
|
||||||
|
id: '7e35c6a0-39b2-4488-aea6-6ee20bd5da2c',
|
||||||
|
},
|
||||||
|
|
||||||
|
alreadyInvited: {
|
||||||
|
message: 'That user has already been invited to that group.',
|
||||||
|
code: 'ALREADY_INVITED',
|
||||||
|
id: 'ee0f58b4-b529-4d13-b761-b9a3e69f97e6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId', 'userId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupInvitationsRepository)
|
||||||
|
private userGroupInvitationsRepository: UserGroupInvitationsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private idService: IdService,
|
||||||
|
private getterService: GetterService,
|
||||||
|
private createNotificationService: CreateNotificationService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the group
|
||||||
|
const userGroup = await this.userGroupsRepository.findOneBy({
|
||||||
|
id: ps.groupId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the user
|
||||||
|
const user = await this.getterService.getUser(ps.userId).catch(err => {
|
||||||
|
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
const joining = await this.userGroupJoiningsRepository.findOneBy({
|
||||||
|
userGroupId: userGroup.id,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (joining) {
|
||||||
|
throw new ApiError(meta.errors.alreadyAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existInvitation = await this.userGroupInvitationsRepository.findOneBy({
|
||||||
|
userGroupId: userGroup.id,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existInvitation) {
|
||||||
|
throw new ApiError(meta.errors.alreadyInvited);
|
||||||
|
}
|
||||||
|
|
||||||
|
const invitation = await this.userGroupInvitationsRepository.insert({
|
||||||
|
id: this.idService.genId(),
|
||||||
|
createdAt: new Date(),
|
||||||
|
userId: user.id,
|
||||||
|
userGroupId: userGroup.id,
|
||||||
|
} as UserGroupInvitation).then(x => this.userGroupInvitationsRepository.findOneByOrFail(x.identifiers[0]));
|
||||||
|
|
||||||
|
// 通知を作成
|
||||||
|
this.createNotificationService.createNotification(user.id, 'groupInvited', {
|
||||||
|
notifierId: me.id,
|
||||||
|
userGroupInvitationId: invitation.id,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
import { Not, In } from 'typeorm';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'account'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:user-groups',
|
||||||
|
|
||||||
|
description: 'List the groups that the authenticated user is a member of.',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserGroup',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private userGroupEntityService: UserGroupEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const ownedGroups = await this.userGroupsRepository.findBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
const joinings = await this.userGroupJoiningsRepository.findBy({
|
||||||
|
userId: me.id,
|
||||||
|
...(ownedGroups.length > 0 ? {
|
||||||
|
userGroupId: Not(In(ownedGroups.map(x => x.id))),
|
||||||
|
} : {}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return await Promise.all(joinings.map(x => this.userGroupEntityService.pack(x.userGroupId)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'users'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Leave a group. The owner of a group can not leave. They must transfer ownership or delete the group instead.',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchGroup: {
|
||||||
|
message: 'No such group.',
|
||||||
|
code: 'NO_SUCH_GROUP',
|
||||||
|
id: '62780270-1f67-5dc0-daca-3eb510612e31',
|
||||||
|
},
|
||||||
|
|
||||||
|
youAreOwner: {
|
||||||
|
message: 'Your are the owner.',
|
||||||
|
code: 'YOU_ARE_OWNER',
|
||||||
|
id: 'b6d6e0c2-ef8a-9bb8-653d-79f4a3107c69',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the group
|
||||||
|
const userGroup = await this.userGroupsRepository.findOneBy({
|
||||||
|
id: ps.groupId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (me.id === userGroup.userId) {
|
||||||
|
throw new ApiError(meta.errors.youAreOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.userGroupJoiningsRepository.delete({ userGroupId: userGroup.id, userId: me.id });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'account'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:user-groups',
|
||||||
|
|
||||||
|
description: 'List the groups that the authenticated user is the owner of.',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserGroup',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {},
|
||||||
|
required: [],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
private userGroupEntityService: UserGroupEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const userGroups = await this.userGroupsRepository.findBy({
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
return await Promise.all(userGroups.map(x => this.userGroupEntityService.pack(x)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'users'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Removes a specified user from a group. The owner can not be removed.',
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchGroup: {
|
||||||
|
message: 'No such group.',
|
||||||
|
code: 'NO_SUCH_GROUP',
|
||||||
|
id: '4662487c-05b1-4b78-86e5-fd46998aba74',
|
||||||
|
},
|
||||||
|
|
||||||
|
noSuchUser: {
|
||||||
|
message: 'No such user.',
|
||||||
|
code: 'NO_SUCH_USER',
|
||||||
|
id: '0b5cc374-3681-41da-861e-8bc1146f7a55',
|
||||||
|
},
|
||||||
|
|
||||||
|
isOwner: {
|
||||||
|
message: 'The user is the owner.',
|
||||||
|
code: 'IS_OWNER',
|
||||||
|
id: '1546eed5-4414-4dea-81c1-b0aec4f6d2af',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId', 'userId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private getterService: GetterService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the group
|
||||||
|
const userGroup = await this.userGroupsRepository.findOneBy({
|
||||||
|
id: ps.groupId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the user
|
||||||
|
const user = await this.getterService.getUser(ps.userId).catch(err => {
|
||||||
|
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (user.id === userGroup.userId) {
|
||||||
|
throw new ApiError(meta.errors.isOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull the user
|
||||||
|
await this.userGroupJoiningsRepository.delete({ userGroupId: userGroup.id, userId: user.id });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'account'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:user-groups',
|
||||||
|
|
||||||
|
description: 'Show the properties of a group.',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserGroup',
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchGroup: {
|
||||||
|
message: 'No such group.',
|
||||||
|
code: 'NO_SUCH_GROUP',
|
||||||
|
id: 'ea04751e-9b7e-487b-a509-330fb6bd6b9b',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private userGroupEntityService: UserGroupEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the group
|
||||||
|
const userGroup = await this.userGroupsRepository.findOneBy({
|
||||||
|
id: ps.groupId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
const joining = await this.userGroupJoiningsRepository.findOneBy({
|
||||||
|
userId: me.id,
|
||||||
|
userGroupId: userGroup.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (joining == null && userGroup.userId !== me.id) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.userGroupEntityService.pack(userGroup);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository, UserGroupJoiningsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups', 'users'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Transfer ownership of a group from the authenticated user to another user.',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserGroup',
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchGroup: {
|
||||||
|
message: 'No such group.',
|
||||||
|
code: 'NO_SUCH_GROUP',
|
||||||
|
id: '8e31d36b-2f88-4ccd-a438-e2d78a9162db',
|
||||||
|
},
|
||||||
|
|
||||||
|
noSuchUser: {
|
||||||
|
message: 'No such user.',
|
||||||
|
code: 'NO_SUCH_USER',
|
||||||
|
id: '711f7ebb-bbb9-4dfa-b540-b27809fed5e9',
|
||||||
|
},
|
||||||
|
|
||||||
|
noSuchGroupMember: {
|
||||||
|
message: 'No such group member.',
|
||||||
|
code: 'NO_SUCH_GROUP_MEMBER',
|
||||||
|
id: 'd31bebee-196d-42c2-9a3e-9474d4be6cc4',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['groupId', 'userId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.userGroupJoiningsRepository)
|
||||||
|
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
|
||||||
|
|
||||||
|
private userGroupEntityService: UserGroupEntityService,
|
||||||
|
private getterService: GetterService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the group
|
||||||
|
const userGroup = await this.userGroupsRepository.findOneBy({
|
||||||
|
id: ps.groupId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the user
|
||||||
|
const user = await this.getterService.getUser(ps.userId).catch(err => {
|
||||||
|
if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
const joining = await this.userGroupJoiningsRepository.findOneBy({
|
||||||
|
userGroupId: userGroup.id,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (joining == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroupMember);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.userGroupsRepository.update(userGroup.id, {
|
||||||
|
userId: ps.userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return await this.userGroupEntityService.pack(userGroup.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { UserGroupsRepository } from '@/models/index.js';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { UserGroupEntityService } from '@/core/entities/UserGroupEntityService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '../../../error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['groups'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'write:user-groups',
|
||||||
|
|
||||||
|
description: 'Update the properties of a group.',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'UserGroup',
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchGroup: {
|
||||||
|
message: 'No such group.',
|
||||||
|
code: 'NO_SUCH_GROUP',
|
||||||
|
id: '9081cda3-7a9e-4fac-a6ce-908d70f282f6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
groupId: { type: 'string', format: 'misskey:id' },
|
||||||
|
name: { type: 'string', minLength: 1, maxLength: 100 },
|
||||||
|
},
|
||||||
|
required: ['groupId', 'name'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.userGroupsRepository)
|
||||||
|
private userGroupsRepository: UserGroupsRepository,
|
||||||
|
|
||||||
|
private userGroupEntityService: UserGroupEntityService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
// Fetch the group
|
||||||
|
const userGroup = await this.userGroupsRepository.findOneBy({
|
||||||
|
id: ps.groupId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (userGroup == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchGroup);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.userGroupsRepository.update(userGroup.id, {
|
||||||
|
name: ps.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
return await this.userGroupEntityService.pack(userGroup.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ import type { Channel as ChannelModel } from '@/models/entities/Channel.js';
|
|||||||
import type { FollowingsRepository, MutingsRepository, UserProfilesRepository, ChannelFollowingsRepository, BlockingsRepository } from '@/models/index.js';
|
import type { FollowingsRepository, MutingsRepository, UserProfilesRepository, ChannelFollowingsRepository, BlockingsRepository } from '@/models/index.js';
|
||||||
import type { AccessToken } from '@/models/entities/AccessToken.js';
|
import type { AccessToken } from '@/models/entities/AccessToken.js';
|
||||||
import type { UserProfile } from '@/models/entities/UserProfile.js';
|
import type { UserProfile } from '@/models/entities/UserProfile.js';
|
||||||
|
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
||||||
import type { Packed } from '@/misc/schema.js';
|
import type { Packed } from '@/misc/schema.js';
|
||||||
import type { GlobalEventService } from '@/core/GlobalEventService.js';
|
import type { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||||
import type { NoteReadService } from '@/core/NoteReadService.js';
|
import type { NoteReadService } from '@/core/NoteReadService.js';
|
||||||
|
@ -6,6 +6,7 @@ import type { Antenna } from '@/models/entities/Antenna.js';
|
|||||||
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
import type { DriveFile } from '@/models/entities/DriveFile.js';
|
||||||
import type { DriveFolder } from '@/models/entities/DriveFolder.js';
|
import type { DriveFolder } from '@/models/entities/DriveFolder.js';
|
||||||
import type { UserList } from '@/models/entities/UserList.js';
|
import type { UserList } from '@/models/entities/UserList.js';
|
||||||
|
import type { UserGroup } from '@/models/entities/UserGroup.js';
|
||||||
import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js';
|
import type { AbuseUserReport } from '@/models/entities/AbuseUserReport.js';
|
||||||
import type { Signin } from '@/models/entities/Signin.js';
|
import type { Signin } from '@/models/entities/Signin.js';
|
||||||
import type { Page } from '@/models/entities/Page.js';
|
import type { Page } from '@/models/entities/Page.js';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] as const;
|
export const notificationTypes = ['follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollVote', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'groupInvited', 'achievementEarned', 'app'] as const;
|
||||||
|
|
||||||
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
|
export const noteVisibilities = ['public', 'home', 'followers', 'specified'] as const;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
<i v-if="notification.type === 'follow'" class="ti ti-plus"></i>
|
<i v-if="notification.type === 'follow'" class="ti ti-plus"></i>
|
||||||
<i v-else-if="notification.type === 'receiveFollowRequest'" class="ti ti-clock"></i>
|
<i v-else-if="notification.type === 'receiveFollowRequest'" class="ti ti-clock"></i>
|
||||||
<i v-else-if="notification.type === 'followRequestAccepted'" class="ti ti-check"></i>
|
<i v-else-if="notification.type === 'followRequestAccepted'" class="ti ti-check"></i>
|
||||||
|
<i v-else-if="notification.type === 'groupInvited'" class="ti ti-certificate-2"></i>
|
||||||
<i v-else-if="notification.type === 'renote'" class="ti ti-repeat"></i>
|
<i v-else-if="notification.type === 'renote'" class="ti ti-repeat"></i>
|
||||||
<i v-else-if="notification.type === 'reply'" class="ti ti-arrow-back-up"></i>
|
<i v-else-if="notification.type === 'reply'" class="ti ti-arrow-back-up"></i>
|
||||||
<i v-else-if="notification.type === 'mention'" class="ti ti-at"></i>
|
<i v-else-if="notification.type === 'mention'" class="ti ti-at"></i>
|
||||||
@ -73,6 +74,12 @@
|
|||||||
<button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button>
|
<button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="notification.type === 'groupInvited'">
|
||||||
|
<span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b></span>
|
||||||
|
<div v-if="full && !groupInviteDone">
|
||||||
|
<button class="_textButton" @click="acceptGroupInvitation()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ i18n.ts.reject }}</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<span v-else-if="notification.type === 'app'" :class="$style.text">
|
<span v-else-if="notification.type === 'app'" :class="$style.text">
|
||||||
<Mfm :text="notification.body" :nowrap="false"/>
|
<Mfm :text="notification.body" :nowrap="false"/>
|
||||||
</span>
|
</span>
|
||||||
@ -138,6 +145,7 @@ onUnmounted(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const followRequestDone = ref(false);
|
const followRequestDone = ref(false);
|
||||||
|
const groupInviteDone = ref(false);
|
||||||
|
|
||||||
const acceptFollowRequest = () => {
|
const acceptFollowRequest = () => {
|
||||||
followRequestDone.value = true;
|
followRequestDone.value = true;
|
||||||
@ -149,6 +157,16 @@ const rejectFollowRequest = () => {
|
|||||||
os.api('following/requests/reject', { userId: props.notification.user.id });
|
os.api('following/requests/reject', { userId: props.notification.user.id });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const acceptGroupInvitation = () => {
|
||||||
|
groupInviteDone.value = true;
|
||||||
|
os.apiWithDialog('users/groups/invitations/accept', { invitationId: props.notification.invitation.id });
|
||||||
|
};
|
||||||
|
|
||||||
|
const rejectGroupInvitation = () => {
|
||||||
|
groupInviteDone.value = true;
|
||||||
|
os.api('users/groups/invitations/reject', { invitationId: props.notification.invitation.id });
|
||||||
|
};
|
||||||
|
|
||||||
useTooltip(reactionRef, (showing) => {
|
useTooltip(reactionRef, (showing) => {
|
||||||
os.popup(XReactionTooltip, {
|
os.popup(XReactionTooltip, {
|
||||||
showing,
|
showing,
|
||||||
@ -206,7 +224,7 @@ useTooltip(reactionRef, (showing) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest {
|
.t_follow, .t_followRequestAccepted, .t_receiveFollowRequest, .t_groupInvited {
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
background: #36aed2;
|
background: #36aed2;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
@ -50,6 +50,14 @@ export const navbarItemDef = reactive({
|
|||||||
show: computed(() => $i != null),
|
show: computed(() => $i != null),
|
||||||
to: '/my/lists',
|
to: '/my/lists',
|
||||||
},
|
},
|
||||||
|
/*
|
||||||
|
groups: {
|
||||||
|
title: i18n.ts.groups,
|
||||||
|
icon: 'ti ti-users',
|
||||||
|
show: computed(() => $i != null),
|
||||||
|
to: '/my/groups',
|
||||||
|
},
|
||||||
|
*/
|
||||||
antennas: {
|
antennas: {
|
||||||
title: i18n.ts.antennas,
|
title: i18n.ts.antennas,
|
||||||
icon: 'ti ti-antenna',
|
icon: 'ti ti-antenna',
|
||||||
|
@ -17,6 +17,7 @@ let draft = $ref({
|
|||||||
name: '',
|
name: '',
|
||||||
src: 'all',
|
src: 'all',
|
||||||
userListId: null,
|
userListId: null,
|
||||||
|
userGroupId: null,
|
||||||
users: [],
|
users: [],
|
||||||
keywords: [],
|
keywords: [],
|
||||||
excludeKeywords: [],
|
excludeKeywords: [],
|
||||||
|
@ -11,11 +11,16 @@
|
|||||||
<!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>-->
|
<!--<option value="home">{{ i18n.ts._antennaSources.homeTimeline }}</option>-->
|
||||||
<option value="users">{{ i18n.ts._antennaSources.users }}</option>
|
<option value="users">{{ i18n.ts._antennaSources.users }}</option>
|
||||||
<!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>-->
|
<!--<option value="list">{{ i18n.ts._antennaSources.userList }}</option>-->
|
||||||
|
<!--<option value="group">{{ i18n.ts._antennaSources.userGroup }}</option>-->
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
<MkSelect v-if="src === 'list'" v-model="userListId">
|
<MkSelect v-if="src === 'list'" v-model="userListId">
|
||||||
<template #label>{{ i18n.ts.userList }}</template>
|
<template #label>{{ i18n.ts.userList }}</template>
|
||||||
<option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
|
<option v-for="list in userLists" :key="list.id" :value="list.id">{{ list.name }}</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
<MkSelect v-else-if="src === 'group'" v-model="userGroupId">
|
||||||
|
<template #label>{{ i18n.ts.userGroup }}</template>
|
||||||
|
<option v-for="group in userGroups" :key="group.id" :value="group.id">{{ group.name }}</option>
|
||||||
|
</MkSelect>
|
||||||
<MkTextarea v-else-if="src === 'users'" v-model="users">
|
<MkTextarea v-else-if="src === 'users'" v-model="users">
|
||||||
<template #label>{{ i18n.ts.users }}</template>
|
<template #label>{{ i18n.ts.users }}</template>
|
||||||
<template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template>
|
<template #caption>{{ i18n.ts.antennaUsersDescription }} <button class="_textButton" @click="addUser">{{ i18n.ts.addUser }}</button></template>
|
||||||
@ -65,6 +70,7 @@ const emit = defineEmits<{
|
|||||||
let name: string = $ref(props.antenna.name);
|
let name: string = $ref(props.antenna.name);
|
||||||
let src: string = $ref(props.antenna.src);
|
let src: string = $ref(props.antenna.src);
|
||||||
let userListId: any = $ref(props.antenna.userListId);
|
let userListId: any = $ref(props.antenna.userListId);
|
||||||
|
let userGroupId: any = $ref(props.antenna.userGroupId);
|
||||||
let users: string = $ref(props.antenna.users.join('\n'));
|
let users: string = $ref(props.antenna.users.join('\n'));
|
||||||
let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
|
let keywords: string = $ref(props.antenna.keywords.map(x => x.join(' ')).join('\n'));
|
||||||
let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
|
let excludeKeywords: string = $ref(props.antenna.excludeKeywords.map(x => x.join(' ')).join('\n'));
|
||||||
@ -73,11 +79,19 @@ let withReplies: boolean = $ref(props.antenna.withReplies);
|
|||||||
let withFile: boolean = $ref(props.antenna.withFile);
|
let withFile: boolean = $ref(props.antenna.withFile);
|
||||||
let notify: boolean = $ref(props.antenna.notify);
|
let notify: boolean = $ref(props.antenna.notify);
|
||||||
let userLists: any = $ref(null);
|
let userLists: any = $ref(null);
|
||||||
|
let userGroups: any = $ref(null);
|
||||||
|
|
||||||
watch(() => src, async () => {
|
watch(() => src, async () => {
|
||||||
if (src === 'list' && userLists === null) {
|
if (src === 'list' && userLists === null) {
|
||||||
userLists = await os.api('users/lists/list');
|
userLists = await os.api('users/lists/list');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src === 'group' && userGroups === null) {
|
||||||
|
const groups1 = await os.api('users/groups/owned');
|
||||||
|
const groups2 = await os.api('users/groups/joined');
|
||||||
|
|
||||||
|
userGroups = [...groups1, ...groups2];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
async function saveAntenna() {
|
async function saveAntenna() {
|
||||||
@ -85,6 +99,7 @@ async function saveAntenna() {
|
|||||||
name,
|
name,
|
||||||
src,
|
src,
|
||||||
userListId,
|
userListId,
|
||||||
|
userGroupId,
|
||||||
withReplies,
|
withReplies,
|
||||||
withFile,
|
withFile,
|
||||||
notify,
|
notify,
|
||||||
|
@ -34,6 +34,9 @@
|
|||||||
<MkSwitch v-model="emailNotification_receiveFollowRequest">
|
<MkSwitch v-model="emailNotification_receiveFollowRequest">
|
||||||
{{ i18n.ts._notification._types.receiveFollowRequest }}
|
{{ i18n.ts._notification._types.receiveFollowRequest }}
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
<MkSwitch v-model="emailNotification_groupInvited">
|
||||||
|
{{ i18n.ts._notification._types.groupInvited }}
|
||||||
|
</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</div>
|
</div>
|
||||||
@ -75,6 +78,7 @@ const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply')
|
|||||||
const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote'));
|
const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote'));
|
||||||
const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow'));
|
const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow'));
|
||||||
const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest'));
|
const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest'));
|
||||||
|
const emailNotification_groupInvited = ref($i!.emailNotificationTypes.includes('groupInvited'));
|
||||||
|
|
||||||
const saveNotificationSettings = () => {
|
const saveNotificationSettings = () => {
|
||||||
os.api('i/update', {
|
os.api('i/update', {
|
||||||
@ -84,11 +88,12 @@ const saveNotificationSettings = () => {
|
|||||||
...[emailNotification_quote.value ? 'quote' : null],
|
...[emailNotification_quote.value ? 'quote' : null],
|
||||||
...[emailNotification_follow.value ? 'follow' : null],
|
...[emailNotification_follow.value ? 'follow' : null],
|
||||||
...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null],
|
...[emailNotification_receiveFollowRequest.value ? 'receiveFollowRequest' : null],
|
||||||
|
...[emailNotification_groupInvited.value ? 'groupInvited' : null],
|
||||||
].filter(x => x != null),
|
].filter(x => x != null),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest], () => {
|
watch([emailNotification_mention, emailNotification_reply, emailNotification_quote, emailNotification_follow, emailNotification_receiveFollowRequest, emailNotification_groupInvited], () => {
|
||||||
saveNotificationSettings();
|
saveNotificationSettings();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,6 +35,28 @@ export function getUserMenu(user, router: Router = mainRouter) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function inviteGroup() {
|
||||||
|
const groups = await os.api('users/groups/owned');
|
||||||
|
if (groups.length === 0) {
|
||||||
|
os.alert({
|
||||||
|
type: 'error',
|
||||||
|
text: i18n.ts.youHaveNoGroups,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { canceled, result: groupId } = await os.select({
|
||||||
|
title: i18n.ts.group,
|
||||||
|
items: groups.map(group => ({
|
||||||
|
value: group.id, text: group.name,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
os.apiWithDialog('users/groups/invite', {
|
||||||
|
groupId: groupId,
|
||||||
|
userId: user.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function toggleMute() {
|
async function toggleMute() {
|
||||||
if (user.isMuted) {
|
if (user.isMuted) {
|
||||||
os.apiWithDialog('mute/delete', {
|
os.apiWithDialog('mute/delete', {
|
||||||
@ -134,11 +156,20 @@ export function getUserMenu(user, router: Router = mainRouter) {
|
|||||||
action: () => {
|
action: () => {
|
||||||
os.post({ specified: user });
|
os.post({ specified: user });
|
||||||
},
|
},
|
||||||
}, null, {
|
}, meId !== user.id ? {
|
||||||
|
type: 'link',
|
||||||
|
icon: 'ti ti-messages',
|
||||||
|
text: i18n.ts.startMessaging,
|
||||||
|
to: '/my/messaging/' + Acct.toString(user),
|
||||||
|
} : undefined, null, {
|
||||||
icon: 'ti ti-list',
|
icon: 'ti ti-list',
|
||||||
text: i18n.ts.addToList,
|
text: i18n.ts.addToList,
|
||||||
action: pushList,
|
action: pushList,
|
||||||
}] as any;
|
}, meId !== user.id ? {
|
||||||
|
icon: 'ti ti-users',
|
||||||
|
text: i18n.ts.inviteToGroup,
|
||||||
|
action: inviteGroup,
|
||||||
|
} : undefined] as any;
|
||||||
|
|
||||||
if ($i && meId !== user.id) {
|
if ($i && meId !== user.id) {
|
||||||
menu = menu.concat([null, {
|
menu = menu.concat([null, {
|
||||||
|
@ -209,6 +209,23 @@ async function composeNotification(data: pushNotificationDataMap[keyof pushNotif
|
|||||||
data,
|
data,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
case 'groupInvited':
|
||||||
|
return [t('_notification.youWereInvitedToGroup', { userName: getUserName(data.body.user) }), {
|
||||||
|
body: data.body.invitation.group.name,
|
||||||
|
badge: iconUrl('users'),
|
||||||
|
data,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
action: 'accept',
|
||||||
|
title: t('accept'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
action: 'reject',
|
||||||
|
title: t('reject'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}];
|
||||||
|
|
||||||
case 'app':
|
case 'app':
|
||||||
return [data.body.header ?? data.body.body, {
|
return [data.body.header ?? data.body.body, {
|
||||||
body: data.body.header ? data.body.body : '',
|
body: data.body.header ? data.body.body : '',
|
||||||
|
@ -32,10 +32,18 @@ export function openAntenna(antennaId: string, loginId: string) {
|
|||||||
return openClient('push', `/timeline/antenna/${antennaId}`, loginId, { antennaId });
|
return openClient('push', `/timeline/antenna/${antennaId}`, loginId, { antennaId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function openChat(body: any, loginId: string) {
|
||||||
|
if (body.groupId === null) {
|
||||||
|
return openClient('push', `/my/messaging/${getAcct(body.user)}`, loginId, { body });
|
||||||
|
} else {
|
||||||
|
return openClient('push', `/my/messaging/group/${body.groupId}`, loginId, { body });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// post-formのオプションから投稿フォームを開く
|
// post-formのオプションから投稿フォームを開く
|
||||||
export async function openPost(options: any, loginId: string) {
|
export async function openPost(options: any, loginId: string) {
|
||||||
// クエリを作成しておく
|
// クエリを作成しておく
|
||||||
let url = '/share?';
|
let url = `/share?`;
|
||||||
if (options.initialText) url += `text=${options.initialText}&`;
|
if (options.initialText) url += `text=${options.initialText}&`;
|
||||||
if (options.reply) url += `replyId=${options.reply.id}&`;
|
if (options.reply) url += `replyId=${options.reply.id}&`;
|
||||||
if (options.renote) url += `renoteId=${options.renote.id}&`;
|
if (options.renote) url += `renoteId=${options.renote.id}&`;
|
||||||
@ -56,7 +64,7 @@ export async function openClient(order: swMessageOrderType, url: string, loginId
|
|||||||
|
|
||||||
export async function findClient() {
|
export async function findClient() {
|
||||||
const clients = await self.clients.matchAll({
|
const clients = await self.clients.matchAll({
|
||||||
type: 'window',
|
type: 'window'
|
||||||
});
|
});
|
||||||
for (const c of clients) {
|
for (const c of clients) {
|
||||||
if (c.url.indexOf('?zen') < 0) return c;
|
if (c.url.indexOf('?zen') < 0) return c;
|
||||||
|
@ -118,6 +118,9 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv
|
|||||||
case 'receiveFollowRequest':
|
case 'receiveFollowRequest':
|
||||||
await swos.api('following/requests/accept', loginId, { userId: data.body.userId });
|
await swos.api('following/requests/accept', loginId, { userId: data.body.userId });
|
||||||
break;
|
break;
|
||||||
|
case 'groupInvited':
|
||||||
|
await swos.api('users/groups/invitations/accept', loginId, { invitationId: data.body.invitation.id });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'reject':
|
case 'reject':
|
||||||
@ -125,6 +128,9 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv
|
|||||||
case 'receiveFollowRequest':
|
case 'receiveFollowRequest':
|
||||||
await swos.api('following/requests/reject', loginId, { userId: data.body.userId });
|
await swos.api('following/requests/reject', loginId, { userId: data.body.userId });
|
||||||
break;
|
break;
|
||||||
|
case 'groupInvited':
|
||||||
|
await swos.api('users/groups/invitations/reject', loginId, { invitationId: data.body.invitation.id });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'showFollowRequests':
|
case 'showFollowRequests':
|
||||||
@ -135,6 +141,9 @@ globalThis.addEventListener('notificationclick', (ev: ServiceWorkerGlobalScopeEv
|
|||||||
case 'receiveFollowRequest':
|
case 'receiveFollowRequest':
|
||||||
client = await swos.openClient('push', '/my/follow-requests', loginId);
|
client = await swos.openClient('push', '/my/follow-requests', loginId);
|
||||||
break;
|
break;
|
||||||
|
case 'groupInvited':
|
||||||
|
client = await swos.openClient('push', '/my/groups', loginId);
|
||||||
|
break;
|
||||||
case 'reaction':
|
case 'reaction':
|
||||||
client = await swos.openNote(data.body.note.id, loginId);
|
client = await swos.openNote(data.body.note.id, loginId);
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user