wip
This commit is contained in:
82
locales/index.d.ts
vendored
82
locales/index.d.ts
vendored
@@ -5358,31 +5358,63 @@ export interface Locale extends ILocale {
|
|||||||
* チャット
|
* チャット
|
||||||
*/
|
*/
|
||||||
"chat": string;
|
"chat": string;
|
||||||
/**
|
"_chat": {
|
||||||
* 個人チャット
|
/**
|
||||||
*/
|
* 個人チャット
|
||||||
"individualChat": string;
|
*/
|
||||||
/**
|
"individualChat": string;
|
||||||
* 特定ユーザーとの一対一のチャットができます。
|
/**
|
||||||
*/
|
* 特定ユーザーとの一対一のチャットができます。
|
||||||
"individualChat_description": string;
|
*/
|
||||||
/**
|
"individualChat_description": string;
|
||||||
* ルームチャット
|
/**
|
||||||
*/
|
* ルームチャット
|
||||||
"roomChat": string;
|
*/
|
||||||
/**
|
"roomChat": string;
|
||||||
* 複数人でのチャットができます。
|
/**
|
||||||
* また、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。
|
* 複数人でのチャットができます。
|
||||||
*/
|
* また、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。
|
||||||
"roomChat_description": string;
|
*/
|
||||||
/**
|
"roomChat_description": string;
|
||||||
* このユーザーとのチャットを開始できません
|
/**
|
||||||
*/
|
* このユーザーとのチャットを開始できません
|
||||||
"cannotChatWithTheUser": string;
|
*/
|
||||||
/**
|
"cannotChatWithTheUser": string;
|
||||||
* チャットが使えない状態になっているか、相手がチャットを開放していません。
|
/**
|
||||||
*/
|
* チャットが使えない状態になっているか、相手がチャットを開放していません。
|
||||||
"cannotChatWithTheUser_description": string;
|
*/
|
||||||
|
"cannotChatWithTheUser_description": string;
|
||||||
|
/**
|
||||||
|
* チャットを許可する相手
|
||||||
|
*/
|
||||||
|
"chatAllowedUsers": string;
|
||||||
|
/**
|
||||||
|
* 自分からチャットメッセージを送った相手とはこの設定に関わらずチャットが可能です。
|
||||||
|
*/
|
||||||
|
"chatAllowedUsers_note": string;
|
||||||
|
"_chatAllowedUsers": {
|
||||||
|
/**
|
||||||
|
* 誰でも
|
||||||
|
*/
|
||||||
|
"everyone": string;
|
||||||
|
/**
|
||||||
|
* 自分のフォロワーのみ
|
||||||
|
*/
|
||||||
|
"followers": string;
|
||||||
|
/**
|
||||||
|
* 自分がフォローしているユーザーのみ
|
||||||
|
*/
|
||||||
|
"following": string;
|
||||||
|
/**
|
||||||
|
* 相互フォローのユーザーのみ
|
||||||
|
*/
|
||||||
|
"mutual": string;
|
||||||
|
/**
|
||||||
|
* 誰も許可しない
|
||||||
|
*/
|
||||||
|
"none": string;
|
||||||
|
};
|
||||||
|
};
|
||||||
"_emojiPalette": {
|
"_emojiPalette": {
|
||||||
/**
|
/**
|
||||||
* パレット
|
* パレット
|
||||||
|
@@ -1335,12 +1335,23 @@ postForm: "投稿フォーム"
|
|||||||
textCount: "文字数"
|
textCount: "文字数"
|
||||||
information: "情報"
|
information: "情報"
|
||||||
chat: "チャット"
|
chat: "チャット"
|
||||||
individualChat: "個人チャット"
|
|
||||||
individualChat_description: "特定ユーザーとの一対一のチャットができます。"
|
_chat:
|
||||||
roomChat: "ルームチャット"
|
individualChat: "個人チャット"
|
||||||
roomChat_description: "複数人でのチャットができます。\nまた、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。"
|
individualChat_description: "特定ユーザーとの一対一のチャットができます。"
|
||||||
cannotChatWithTheUser: "このユーザーとのチャットを開始できません"
|
roomChat: "ルームチャット"
|
||||||
cannotChatWithTheUser_description: "チャットが使えない状態になっているか、相手がチャットを開放していません。"
|
roomChat_description: "複数人でのチャットができます。\nまた、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。"
|
||||||
|
cannotChatWithTheUser: "このユーザーとのチャットを開始できません"
|
||||||
|
cannotChatWithTheUser_description: "チャットが使えない状態になっているか、相手がチャットを開放していません。"
|
||||||
|
|
||||||
|
chatAllowedUsers: "チャットを許可する相手"
|
||||||
|
chatAllowedUsers_note: "自分からチャットメッセージを送った相手とはこの設定に関わらずチャットが可能です。"
|
||||||
|
_chatAllowedUsers:
|
||||||
|
everyone: "誰でも"
|
||||||
|
followers: "自分のフォロワーのみ"
|
||||||
|
following: "自分がフォローしているユーザーのみ"
|
||||||
|
mutual: "相互フォローのユーザーのみ"
|
||||||
|
none: "誰も許可しない"
|
||||||
|
|
||||||
_emojiPalette:
|
_emojiPalette:
|
||||||
palettes: "パレット"
|
palettes: "パレット"
|
||||||
|
26
packages/backend/migration/1742617546147-chat-3.js
Normal file
26
packages/backend/migration/1742617546147-chat-3.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Chat31742617546147 {
|
||||||
|
name = 'Chat31742617546147'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`CREATE TABLE "chat_approval" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "otherId" character varying(32) NOT NULL, CONSTRAINT "PK_fbbb95d60acf5c85388345b5f5d" PRIMARY KEY ("id"))`);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_530257863e1381a7f2f1d3282f" ON "chat_approval" ("userId") `);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_b1d46037f23d170da5c05fdf75" ON "chat_approval" ("otherId") `);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_12c4768a2f706fc267f2078903" ON "chat_approval" ("userId", "otherId") `);
|
||||||
|
await queryRunner.query(`ALTER TABLE "chat_approval" ADD CONSTRAINT "FK_530257863e1381a7f2f1d3282fe" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "chat_approval" ADD CONSTRAINT "FK_b1d46037f23d170da5c05fdf755" FOREIGN KEY ("otherId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "chat_approval" DROP CONSTRAINT "FK_b1d46037f23d170da5c05fdf755"`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "chat_approval" DROP CONSTRAINT "FK_530257863e1381a7f2f1d3282fe"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_12c4768a2f706fc267f2078903"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_b1d46037f23d170da5c05fdf75"`);
|
||||||
|
await queryRunner.query(`DROP INDEX "public"."IDX_530257863e1381a7f2f1d3282f"`);
|
||||||
|
await queryRunner.query(`DROP TABLE "chat_approval"`);
|
||||||
|
}
|
||||||
|
}
|
@@ -16,7 +16,7 @@ import { ChatMessageEntityService } from '@/core/entities/ChatMessageEntityServi
|
|||||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||||
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import type { ChatMessagesRepository, MiChatMessage, MiChatRoom, MiDriveFile, MiUser, MutingsRepository, UsersRepository } from '@/models/_.js';
|
import type { ChatApprovalsRepository, ChatMessagesRepository, ChatRoomMembershipsRepository, ChatRoomsRepository, MiChatMessage, MiChatRoom, MiDriveFile, MiUser, MutingsRepository, UsersRepository } from '@/models/_.js';
|
||||||
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||||
import { QueryService } from '@/core/QueryService.js';
|
import { QueryService } from '@/core/QueryService.js';
|
||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
@@ -37,6 +37,15 @@ export class ChatService {
|
|||||||
@Inject(DI.chatMessagesRepository)
|
@Inject(DI.chatMessagesRepository)
|
||||||
private chatMessagesRepository: ChatMessagesRepository,
|
private chatMessagesRepository: ChatMessagesRepository,
|
||||||
|
|
||||||
|
@Inject(DI.chatApprovalsRepository)
|
||||||
|
private chatApprovalsRepository: ChatApprovalsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.chatRoomsRepository)
|
||||||
|
private chatRoomsRepository: ChatRoomsRepository,
|
||||||
|
|
||||||
|
@Inject(DI.chatRoomMembershipsRepository)
|
||||||
|
private chatRoomMembershipsRepository: ChatRoomMembershipsRepository,
|
||||||
|
|
||||||
@Inject(DI.mutingsRepository)
|
@Inject(DI.mutingsRepository)
|
||||||
private mutingsRepository: MutingsRepository,
|
private mutingsRepository: MutingsRepository,
|
||||||
|
|
||||||
@@ -64,22 +73,39 @@ export class ChatService {
|
|||||||
throw new Error('yourself');
|
throw new Error('yourself');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toUser.chatScope === 'none') {
|
const approvals = await this.chatApprovalsRepository.createQueryBuilder('approval')
|
||||||
throw new Error('recipient is cannot chat');
|
.where(new Brackets(qb => { // 自分が相手を許可しているか
|
||||||
} else if (toUser.chatScope === 'followers') {
|
qb.where('approval.userId = :fromUserId', { fromUserId: fromUser.id })
|
||||||
const isFollower = await this.userFollowingService.isFollowing(fromUser.id, toUser.id);
|
.andWhere('approval.otherId = :toUserId', { toUserId: toUser.id });
|
||||||
if (!isFollower) {
|
}))
|
||||||
throw new Error('recipient is cannot chat');
|
.orWhere(new Brackets(qb => { // 相手が自分を許可しているか
|
||||||
}
|
qb.where('approval.userId = :toUserId', { toUserId: toUser.id })
|
||||||
} else if (toUser.chatScope === 'following') {
|
.andWhere('approval.otherId = :fromUserId', { fromUserId: fromUser.id });
|
||||||
const isFollowing = await this.userFollowingService.isFollowing(toUser.id, fromUser.id);
|
}))
|
||||||
if (!isFollowing) {
|
.take(2)
|
||||||
throw new Error('recipient is cannot chat');
|
.getMany();
|
||||||
}
|
|
||||||
} else if (toUser.chatScope === 'mutual') {
|
const otherApprovedMe = approvals.some(approval => approval.userId === toUser.id);
|
||||||
const isMutual = await this.userFollowingService.isMutual(fromUser.id, toUser.id);
|
const iApprovedOther = approvals.some(approval => approval.userId === fromUser.id);
|
||||||
if (!isMutual) {
|
|
||||||
|
if (!otherApprovedMe) {
|
||||||
|
if (toUser.chatScope === 'none') {
|
||||||
throw new Error('recipient is cannot chat');
|
throw new Error('recipient is cannot chat');
|
||||||
|
} else if (toUser.chatScope === 'followers') {
|
||||||
|
const isFollower = await this.userFollowingService.isFollowing(fromUser.id, toUser.id);
|
||||||
|
if (!isFollower) {
|
||||||
|
throw new Error('recipient is cannot chat');
|
||||||
|
}
|
||||||
|
} else if (toUser.chatScope === 'following') {
|
||||||
|
const isFollowing = await this.userFollowingService.isFollowing(toUser.id, fromUser.id);
|
||||||
|
if (!isFollowing) {
|
||||||
|
throw new Error('recipient is cannot chat');
|
||||||
|
}
|
||||||
|
} else if (toUser.chatScope === 'mutual') {
|
||||||
|
const isMutual = await this.userFollowingService.isMutual(fromUser.id, toUser.id);
|
||||||
|
if (!isMutual) {
|
||||||
|
throw new Error('recipient is cannot chat');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +130,15 @@ export class ChatService {
|
|||||||
|
|
||||||
const inserted = await this.chatMessagesRepository.insertOne(message);
|
const inserted = await this.chatMessagesRepository.insertOne(message);
|
||||||
|
|
||||||
|
// 相手を許可しておく
|
||||||
|
if (!iApprovedOther) {
|
||||||
|
this.chatApprovalsRepository.insertOne({
|
||||||
|
id: this.idService.gen(),
|
||||||
|
userId: fromUser.id,
|
||||||
|
otherId: toUser.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const packedMessage = await this.chatMessageEntityService.packLite(inserted);
|
const packedMessage = await this.chatMessageEntityService.packLite(inserted);
|
||||||
|
|
||||||
if (this.userEntityService.isLocalUser(toUser)) {
|
if (this.userEntityService.isLocalUser(toUser)) {
|
||||||
|
@@ -558,6 +558,7 @@ export class UserEntityService implements OnModuleInit {
|
|||||||
publicReactions: this.isLocalUser(user) ? profile!.publicReactions : false, // https://github.com/misskey-dev/misskey/issues/12964
|
publicReactions: this.isLocalUser(user) ? profile!.publicReactions : false, // https://github.com/misskey-dev/misskey/issues/12964
|
||||||
followersVisibility: profile!.followersVisibility,
|
followersVisibility: profile!.followersVisibility,
|
||||||
followingVisibility: profile!.followingVisibility,
|
followingVisibility: profile!.followingVisibility,
|
||||||
|
chatScope: user.chatScope,
|
||||||
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
|
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
|
||||||
id: role.id,
|
id: role.id,
|
||||||
name: role.name,
|
name: role.name,
|
||||||
|
@@ -84,6 +84,7 @@ export const DI = {
|
|||||||
flashLikesRepository: Symbol('flashLikesRepository'),
|
flashLikesRepository: Symbol('flashLikesRepository'),
|
||||||
userMemosRepository: Symbol('userMemosRepository'),
|
userMemosRepository: Symbol('userMemosRepository'),
|
||||||
chatMessagesRepository: Symbol('chatMessagesRepository'),
|
chatMessagesRepository: Symbol('chatMessagesRepository'),
|
||||||
|
chatApprovalsRepository: Symbol('chatApprovalsRepository'),
|
||||||
chatRoomsRepository: Symbol('chatRoomsRepository'),
|
chatRoomsRepository: Symbol('chatRoomsRepository'),
|
||||||
chatRoomMembershipsRepository: Symbol('chatRoomMembershipsRepository'),
|
chatRoomMembershipsRepository: Symbol('chatRoomMembershipsRepository'),
|
||||||
bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'),
|
bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'),
|
||||||
|
39
packages/backend/src/models/ChatApproval.ts
Normal file
39
packages/backend/src/models/ChatApproval.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { id } from './util/id.js';
|
||||||
|
import { MiUser } from './User.js';
|
||||||
|
|
||||||
|
@Entity('chat_approval')
|
||||||
|
@Index(['userId', 'otherId'], { unique: true })
|
||||||
|
export class MiChatApproval {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
})
|
||||||
|
public userId: MiUser['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => MiUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user: MiUser | null;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
})
|
||||||
|
public otherId: MiUser['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => MiUser, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public other: MiUser | null;
|
||||||
|
}
|
@@ -81,6 +81,7 @@ import {
|
|||||||
MiChatMessage,
|
MiChatMessage,
|
||||||
MiChatRoom,
|
MiChatRoom,
|
||||||
MiChatRoomMembership,
|
MiChatRoomMembership,
|
||||||
|
MiChatApproval,
|
||||||
} from './_.js';
|
} from './_.js';
|
||||||
import type { Provider } from '@nestjs/common';
|
import type { Provider } from '@nestjs/common';
|
||||||
import type { DataSource } from 'typeorm';
|
import type { DataSource } from 'typeorm';
|
||||||
@@ -511,6 +512,12 @@ const $chatRoomMembershipsRepository: Provider = {
|
|||||||
inject: [DI.db],
|
inject: [DI.db],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const $chatApprovalsRepository: Provider = {
|
||||||
|
provide: DI.chatApprovalsRepository,
|
||||||
|
useFactory: (db: DataSource) => db.getRepository(MiChatApproval),
|
||||||
|
inject: [DI.db],
|
||||||
|
};
|
||||||
|
|
||||||
const $bubbleGameRecordsRepository: Provider = {
|
const $bubbleGameRecordsRepository: Provider = {
|
||||||
provide: DI.bubbleGameRecordsRepository,
|
provide: DI.bubbleGameRecordsRepository,
|
||||||
useFactory: (db: DataSource) => db.getRepository(MiBubbleGameRecord).extend(miRepository as MiRepository<MiBubbleGameRecord>),
|
useFactory: (db: DataSource) => db.getRepository(MiBubbleGameRecord).extend(miRepository as MiRepository<MiBubbleGameRecord>),
|
||||||
@@ -597,6 +604,7 @@ const $reversiGamesRepository: Provider = {
|
|||||||
$chatMessagesRepository,
|
$chatMessagesRepository,
|
||||||
$chatRoomsRepository,
|
$chatRoomsRepository,
|
||||||
$chatRoomMembershipsRepository,
|
$chatRoomMembershipsRepository,
|
||||||
|
$chatApprovalsRepository,
|
||||||
$bubbleGameRecordsRepository,
|
$bubbleGameRecordsRepository,
|
||||||
$reversiGamesRepository,
|
$reversiGamesRepository,
|
||||||
],
|
],
|
||||||
@@ -672,6 +680,7 @@ const $reversiGamesRepository: Provider = {
|
|||||||
$chatMessagesRepository,
|
$chatMessagesRepository,
|
||||||
$chatRoomsRepository,
|
$chatRoomsRepository,
|
||||||
$chatRoomMembershipsRepository,
|
$chatRoomMembershipsRepository,
|
||||||
|
$chatApprovalsRepository,
|
||||||
$bubbleGameRecordsRepository,
|
$bubbleGameRecordsRepository,
|
||||||
$reversiGamesRepository,
|
$reversiGamesRepository,
|
||||||
],
|
],
|
||||||
|
@@ -78,6 +78,7 @@ import { MiUserListFavorite } from '@/models/UserListFavorite.js';
|
|||||||
import { MiChatMessage } from '@/models/ChatMessage.js';
|
import { MiChatMessage } from '@/models/ChatMessage.js';
|
||||||
import { MiChatRoom } from '@/models/ChatRoom.js';
|
import { MiChatRoom } from '@/models/ChatRoom.js';
|
||||||
import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js';
|
import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js';
|
||||||
|
import { MiChatApproval } from '@/models/ChatApproval.js';
|
||||||
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
||||||
import { MiReversiGame } from '@/models/ReversiGame.js';
|
import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||||
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
||||||
@@ -197,6 +198,7 @@ export {
|
|||||||
MiChatMessage,
|
MiChatMessage,
|
||||||
MiChatRoom,
|
MiChatRoom,
|
||||||
MiChatRoomMembership,
|
MiChatRoomMembership,
|
||||||
|
MiChatApproval,
|
||||||
MiBubbleGameRecord,
|
MiBubbleGameRecord,
|
||||||
MiReversiGame,
|
MiReversiGame,
|
||||||
};
|
};
|
||||||
@@ -272,5 +274,6 @@ export type UserMemoRepository = Repository<MiUserMemo> & MiRepository<MiUserMem
|
|||||||
export type ChatMessagesRepository = Repository<MiChatMessage> & MiRepository<MiChatMessage>;
|
export type ChatMessagesRepository = Repository<MiChatMessage> & MiRepository<MiChatMessage>;
|
||||||
export type ChatRoomsRepository = Repository<MiChatRoom> & MiRepository<MiChatRoom>;
|
export type ChatRoomsRepository = Repository<MiChatRoom> & MiRepository<MiChatRoom>;
|
||||||
export type ChatRoomMembershipsRepository = Repository<MiChatRoomMembership> & MiRepository<MiChatRoomMembership>;
|
export type ChatRoomMembershipsRepository = Repository<MiChatRoomMembership> & MiRepository<MiChatRoomMembership>;
|
||||||
|
export type ChatApprovalsRepository = Repository<MiChatApproval> & MiRepository<MiChatApproval>;
|
||||||
export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord> & MiRepository<MiBubbleGameRecord>;
|
export type BubbleGameRecordsRepository = Repository<MiBubbleGameRecord> & MiRepository<MiBubbleGameRecord>;
|
||||||
export type ReversiGamesRepository = Repository<MiReversiGame> & MiRepository<MiReversiGame>;
|
export type ReversiGamesRepository = Repository<MiReversiGame> & MiRepository<MiReversiGame>;
|
||||||
|
@@ -358,6 +358,11 @@ export const packedUserDetailedNotMeOnlySchema = {
|
|||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
enum: ['public', 'followers', 'private'],
|
enum: ['public', 'followers', 'private'],
|
||||||
},
|
},
|
||||||
|
chatScope: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: false, optional: false,
|
||||||
|
enum: ['everyone', 'following', 'followers', 'mutual', 'none'],
|
||||||
|
},
|
||||||
roles: {
|
roles: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
nullable: false, optional: false,
|
nullable: false, optional: false,
|
||||||
|
@@ -8,6 +8,9 @@ import pg from 'pg';
|
|||||||
import { DataSource, Logger } from 'typeorm';
|
import { DataSource, Logger } from 'typeorm';
|
||||||
import * as highlight from 'cli-highlight';
|
import * as highlight from 'cli-highlight';
|
||||||
import { entities as charts } from '@/core/chart/entities.js';
|
import { entities as charts } from '@/core/chart/entities.js';
|
||||||
|
import { Config } from '@/config.js';
|
||||||
|
import MisskeyLogger from '@/logger.js';
|
||||||
|
import { bindThis } from '@/decorators.js';
|
||||||
|
|
||||||
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
||||||
import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
|
import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
|
||||||
@@ -81,11 +84,8 @@ import { MiChatRoom } from '@/models/ChatRoom.js';
|
|||||||
import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js';
|
import { MiChatRoomMembership } from '@/models/ChatRoomMembership.js';
|
||||||
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js';
|
||||||
import { MiReversiGame } from '@/models/ReversiGame.js';
|
import { MiReversiGame } from '@/models/ReversiGame.js';
|
||||||
|
import { MiChatApproval } from '@/models/ChatApproval.js';
|
||||||
import { Config } from '@/config.js';
|
import { MiSystemAccount } from '@/models/SystemAccount.js';
|
||||||
import MisskeyLogger from '@/logger.js';
|
|
||||||
import { bindThis } from '@/decorators.js';
|
|
||||||
import { MiSystemAccount } from './models/SystemAccount.js';
|
|
||||||
|
|
||||||
pg.types.setTypeParser(20, Number);
|
pg.types.setTypeParser(20, Number);
|
||||||
|
|
||||||
@@ -242,6 +242,7 @@ export const entities = [
|
|||||||
MiChatMessage,
|
MiChatMessage,
|
||||||
MiChatRoom,
|
MiChatRoom,
|
||||||
MiChatRoomMembership,
|
MiChatRoomMembership,
|
||||||
|
MiChatApproval,
|
||||||
MiBubbleGameRecord,
|
MiBubbleGameRecord,
|
||||||
MiReversiGame,
|
MiReversiGame,
|
||||||
...charts,
|
...charts,
|
||||||
|
@@ -190,6 +190,7 @@ export const paramDef = {
|
|||||||
autoSensitive: { type: 'boolean' },
|
autoSensitive: { type: 'boolean' },
|
||||||
followingVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
|
followingVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
|
||||||
followersVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
|
followersVisibility: { type: 'string', enum: ['public', 'followers', 'private'] },
|
||||||
|
chatScope: { type: 'string', enum: ['everyone', 'followers', 'following', 'mutual', 'none'] },
|
||||||
pinnedPageId: { type: 'string', format: 'misskey:id', nullable: true },
|
pinnedPageId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
mutedWords: muteWords,
|
mutedWords: muteWords,
|
||||||
hardMutedWords: muteWords,
|
hardMutedWords: muteWords,
|
||||||
@@ -288,6 +289,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
|
if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday;
|
||||||
if (ps.followingVisibility !== undefined) profileUpdates.followingVisibility = ps.followingVisibility;
|
if (ps.followingVisibility !== undefined) profileUpdates.followingVisibility = ps.followingVisibility;
|
||||||
if (ps.followersVisibility !== undefined) profileUpdates.followersVisibility = ps.followersVisibility;
|
if (ps.followersVisibility !== undefined) profileUpdates.followersVisibility = ps.followersVisibility;
|
||||||
|
if (ps.chatScope !== undefined) updates.chatScope = ps.chatScope;
|
||||||
|
|
||||||
function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) {
|
function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) {
|
||||||
// TODO: ちゃんと数える
|
// TODO: ちゃんと数える
|
||||||
|
@@ -15,7 +15,7 @@ describe('ユーザー', () => {
|
|||||||
// エンティティとしてのユーザーを主眼においたテストを記述する
|
// エンティティとしてのユーザーを主眼においたテストを記述する
|
||||||
// (Userを返すエンドポイントとUserエンティティを書き換えるエンドポイントをテストする)
|
// (Userを返すエンドポイントとUserエンティティを書き換えるエンドポイントをテストする)
|
||||||
|
|
||||||
const stripUndefined = <T extends { [key: string]: any }, >(orig: T): Partial<T> => {
|
const stripUndefined = <T extends { [key: string]: any } >(orig: T): Partial<T> => {
|
||||||
return Object.entries({ ...orig })
|
return Object.entries({ ...orig })
|
||||||
.filter(([, value]) => value !== undefined)
|
.filter(([, value]) => value !== undefined)
|
||||||
.reduce((obj: Partial<T>, [key, value]) => {
|
.reduce((obj: Partial<T>, [key, value]) => {
|
||||||
@@ -83,6 +83,7 @@ describe('ユーザー', () => {
|
|||||||
publicReactions: user.publicReactions,
|
publicReactions: user.publicReactions,
|
||||||
followingVisibility: user.followingVisibility,
|
followingVisibility: user.followingVisibility,
|
||||||
followersVisibility: user.followersVisibility,
|
followersVisibility: user.followersVisibility,
|
||||||
|
chatScope: user.chatScope,
|
||||||
roles: user.roles,
|
roles: user.roles,
|
||||||
memo: user.memo,
|
memo: user.memo,
|
||||||
});
|
});
|
||||||
@@ -343,6 +344,7 @@ describe('ユーザー', () => {
|
|||||||
assert.strictEqual(response.publicReactions, true);
|
assert.strictEqual(response.publicReactions, true);
|
||||||
assert.strictEqual(response.followingVisibility, 'public');
|
assert.strictEqual(response.followingVisibility, 'public');
|
||||||
assert.strictEqual(response.followersVisibility, 'public');
|
assert.strictEqual(response.followersVisibility, 'public');
|
||||||
|
assert.strictEqual(response.chatScope, 'mutual');
|
||||||
assert.deepStrictEqual(response.roles, []);
|
assert.deepStrictEqual(response.roles, []);
|
||||||
assert.strictEqual(response.memo, null);
|
assert.strictEqual(response.memo, null);
|
||||||
|
|
||||||
|
@@ -66,13 +66,13 @@ const history = ref<{
|
|||||||
|
|
||||||
function start(ev: MouseEvent) {
|
function start(ev: MouseEvent) {
|
||||||
os.popupMenu([{
|
os.popupMenu([{
|
||||||
text: i18n.ts.individualChat,
|
text: i18n.ts._chat.individualChat,
|
||||||
caption: i18n.ts.individualChat_description,
|
caption: i18n.ts._chat.individualChat_description,
|
||||||
icon: 'ti ti-user',
|
icon: 'ti ti-user',
|
||||||
action: () => { startUser(); },
|
action: () => { startUser(); },
|
||||||
}, { type: 'divider' }, {
|
}, { type: 'divider' }, {
|
||||||
text: i18n.ts.roomChat,
|
text: i18n.ts._chat.roomChat,
|
||||||
caption: i18n.ts.roomChat_description,
|
caption: i18n.ts._chat.roomChat_description,
|
||||||
icon: 'ti ti-users',
|
icon: 'ti ti-users',
|
||||||
action: () => { startRoom(); },
|
action: () => { startRoom(); },
|
||||||
}], ev.currentTarget ?? ev.target);
|
}], ev.currentTarget ?? ev.target);
|
||||||
|
@@ -78,6 +78,20 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</SearchMarker>
|
</SearchMarker>
|
||||||
|
|
||||||
|
<FormSection>
|
||||||
|
<SearchMarker :keywords="['chat']">
|
||||||
|
<MkSelect v-model="chatScope" @update:modelValue="save()">
|
||||||
|
<template #label><SearchLabel>{{ i18n.ts._chat.chatAllowedUsers }}</SearchLabel></template>
|
||||||
|
<option value="everyone">{{ i18n.ts._chat._chatAllowedUsers.everyone }}</option>
|
||||||
|
<option value="followers">{{ i18n.ts._chat._chatAllowedUsers.followers }}</option>
|
||||||
|
<option value="following">{{ i18n.ts._chat._chatAllowedUsers.following }}</option>
|
||||||
|
<option value="mutual">{{ i18n.ts._chat._chatAllowedUsers.mutual }}</option>
|
||||||
|
<option value="none">{{ i18n.ts._chat._chatAllowedUsers.none }}</option>
|
||||||
|
<template #caption>{{ i18n.ts._chat.chatAllowedUsers_note }}</template>
|
||||||
|
</MkSelect>
|
||||||
|
</SearchMarker>
|
||||||
|
</FormSection>
|
||||||
|
|
||||||
<SearchMarker :keywords="['lockdown']">
|
<SearchMarker :keywords="['lockdown']">
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label><SearchLabel>{{ i18n.ts.lockdown }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
<template #label><SearchLabel>{{ i18n.ts.lockdown }}</SearchLabel><span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||||
@@ -208,6 +222,7 @@ const hideOnlineStatus = ref($i.hideOnlineStatus);
|
|||||||
const publicReactions = ref($i.publicReactions);
|
const publicReactions = ref($i.publicReactions);
|
||||||
const followingVisibility = ref($i.followingVisibility);
|
const followingVisibility = ref($i.followingVisibility);
|
||||||
const followersVisibility = ref($i.followersVisibility);
|
const followersVisibility = ref($i.followersVisibility);
|
||||||
|
const chatScope = ref($i.chatScope);
|
||||||
|
|
||||||
const makeNotesFollowersOnlyBefore_type = computed(() => {
|
const makeNotesFollowersOnlyBefore_type = computed(() => {
|
||||||
if (makeNotesFollowersOnlyBefore.value == null) {
|
if (makeNotesFollowersOnlyBefore.value == null) {
|
||||||
@@ -260,6 +275,7 @@ function save() {
|
|||||||
publicReactions: !!publicReactions.value,
|
publicReactions: !!publicReactions.value,
|
||||||
followingVisibility: followingVisibility.value,
|
followingVisibility: followingVisibility.value,
|
||||||
followersVisibility: followersVisibility.value,
|
followersVisibility: followersVisibility.value,
|
||||||
|
chatScope: chatScope.value,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -240,20 +240,25 @@ export const searchIndexes: SearchIndexItem[] = [
|
|||||||
keywords: ['explore', i18n.ts.makeExplorableDescription],
|
keywords: ['explore', i18n.ts.makeExplorableDescription],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '7vr04wKol',
|
id: 'xEYlOghao',
|
||||||
|
label: i18n.ts._chat.chatAllowedUsers,
|
||||||
|
keywords: ['chat'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'BnOtlyaAh',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
id: 'Av7fAaHv8',
|
id: 'BzMIVBpL0',
|
||||||
label: i18n.ts._accountSettings.requireSigninToViewContents,
|
label: i18n.ts._accountSettings.requireSigninToViewContents,
|
||||||
keywords: ['login', 'signin'],
|
keywords: ['login', 'signin'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5RbESWefG',
|
id: 'jJUqPqBAv',
|
||||||
label: i18n.ts._accountSettings.makeNotesFollowersOnlyBefore,
|
label: i18n.ts._accountSettings.makeNotesFollowersOnlyBefore,
|
||||||
keywords: ['follower', i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription],
|
keywords: ['follower', i18n.ts._accountSettings.makeNotesFollowersOnlyBeforeDescription],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'hdzwDs3qd',
|
id: 'ra10txIFV',
|
||||||
label: i18n.ts._accountSettings.makeNotesHiddenBefore,
|
label: i18n.ts._accountSettings.makeNotesHiddenBefore,
|
||||||
keywords: ['hidden', i18n.ts._accountSettings.makeNotesHiddenBeforeDescription],
|
keywords: ['hidden', i18n.ts._accountSettings.makeNotesHiddenBeforeDescription],
|
||||||
},
|
},
|
||||||
|
@@ -3875,6 +3875,8 @@ export type components = {
|
|||||||
followingVisibility: 'public' | 'followers' | 'private';
|
followingVisibility: 'public' | 'followers' | 'private';
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
followersVisibility: 'public' | 'followers' | 'private';
|
followersVisibility: 'public' | 'followers' | 'private';
|
||||||
|
/** @enum {string} */
|
||||||
|
chatScope: 'everyone' | 'following' | 'followers' | 'mutual' | 'none';
|
||||||
roles: components['schemas']['RoleLite'][];
|
roles: components['schemas']['RoleLite'][];
|
||||||
followedMessage?: string | null;
|
followedMessage?: string | null;
|
||||||
memo: string | null;
|
memo: string | null;
|
||||||
@@ -21330,6 +21332,8 @@ export type operations = {
|
|||||||
followingVisibility?: 'public' | 'followers' | 'private';
|
followingVisibility?: 'public' | 'followers' | 'private';
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
followersVisibility?: 'public' | 'followers' | 'private';
|
followersVisibility?: 'public' | 'followers' | 'private';
|
||||||
|
/** @enum {string} */
|
||||||
|
chatScope?: 'everyone' | 'followers' | 'following' | 'mutual' | 'none';
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
pinnedPageId?: string | null;
|
pinnedPageId?: string | null;
|
||||||
mutedWords?: (string[] | string)[];
|
mutedWords?: (string[] | string)[];
|
||||||
|
Reference in New Issue
Block a user