wip
This commit is contained in:
48
locales/index.d.ts
vendored
48
locales/index.d.ts
vendored
@@ -5376,6 +5376,54 @@ export interface Locale extends ILocale {
|
|||||||
* また、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。
|
* また、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。
|
||||||
*/
|
*/
|
||||||
"roomChat_description": string;
|
"roomChat_description": string;
|
||||||
|
/**
|
||||||
|
* ルームを作成
|
||||||
|
*/
|
||||||
|
"createRoom": string;
|
||||||
|
/**
|
||||||
|
* ユーザーを招待してチャットを始めましょう
|
||||||
|
*/
|
||||||
|
"inviteUserToChat": string;
|
||||||
|
/**
|
||||||
|
* 作成したルーム
|
||||||
|
*/
|
||||||
|
"yourRooms": string;
|
||||||
|
/**
|
||||||
|
* 参加中のルーム
|
||||||
|
*/
|
||||||
|
"joiningRooms": string;
|
||||||
|
/**
|
||||||
|
* 招待
|
||||||
|
*/
|
||||||
|
"invitations": string;
|
||||||
|
/**
|
||||||
|
* 招待はありません
|
||||||
|
*/
|
||||||
|
"noInvitations": string;
|
||||||
|
/**
|
||||||
|
* 履歴
|
||||||
|
*/
|
||||||
|
"history": string;
|
||||||
|
/**
|
||||||
|
* 履歴はありません
|
||||||
|
*/
|
||||||
|
"noHistory": string;
|
||||||
|
/**
|
||||||
|
* ルームはありません
|
||||||
|
*/
|
||||||
|
"noRooms": string;
|
||||||
|
/**
|
||||||
|
* ユーザーを招待
|
||||||
|
*/
|
||||||
|
"inviteUser": string;
|
||||||
|
/**
|
||||||
|
* 参加
|
||||||
|
*/
|
||||||
|
"join": string;
|
||||||
|
/**
|
||||||
|
* 無視
|
||||||
|
*/
|
||||||
|
"ignore": string;
|
||||||
/**
|
/**
|
||||||
* このユーザーとのチャットを開始できません
|
* このユーザーとのチャットを開始できません
|
||||||
*/
|
*/
|
||||||
|
@@ -1341,6 +1341,18 @@ _chat:
|
|||||||
individualChat_description: "特定ユーザーとの一対一のチャットができます。"
|
individualChat_description: "特定ユーザーとの一対一のチャットができます。"
|
||||||
roomChat: "ルームチャット"
|
roomChat: "ルームチャット"
|
||||||
roomChat_description: "複数人でのチャットができます。\nまた、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。"
|
roomChat_description: "複数人でのチャットができます。\nまた、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。"
|
||||||
|
createRoom: "ルームを作成"
|
||||||
|
inviteUserToChat: "ユーザーを招待してチャットを始めましょう"
|
||||||
|
yourRooms: "作成したルーム"
|
||||||
|
joiningRooms: "参加中のルーム"
|
||||||
|
invitations: "招待"
|
||||||
|
noInvitations: "招待はありません"
|
||||||
|
history: "履歴"
|
||||||
|
noHistory: "履歴はありません"
|
||||||
|
noRooms: "ルームはありません"
|
||||||
|
inviteUser: "ユーザーを招待"
|
||||||
|
join: "参加"
|
||||||
|
ignore: "無視"
|
||||||
cannotChatWithTheUser: "このユーザーとのチャットを開始できません"
|
cannotChatWithTheUser: "このユーザーとのチャットを開始できません"
|
||||||
cannotChatWithTheUser_description: "チャットが使えない状態になっているか、相手がチャットを開放していません。"
|
cannotChatWithTheUser_description: "チャットが使えない状態になっているか、相手がチャットを開放していません。"
|
||||||
chatWithThisUser: "チャットする"
|
chatWithThisUser: "チャットする"
|
||||||
|
16
packages/backend/migration/1742721896936-chat-5.js
Normal file
16
packages/backend/migration/1742721896936-chat-5.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Chat51742721896936 {
|
||||||
|
name = 'Chat51742721896936'
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "chat_room_invitation" ADD "ignored" boolean NOT NULL DEFAULT false`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
await queryRunner.query(`ALTER TABLE "chat_room_invitation" DROP COLUMN "ignored"`);
|
||||||
|
}
|
||||||
|
}
|
@@ -22,6 +22,7 @@ import { QueryService } from '@/core/QueryService.js';
|
|||||||
import { RoleService } from '@/core/RoleService.js';
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||||
import { MiChatRoomInvitation } from '@/models/ChatRoomInvitation.js';
|
import { MiChatRoomInvitation } from '@/models/ChatRoomInvitation.js';
|
||||||
|
import { Packed } from '@/misc/json-schema.js';
|
||||||
|
|
||||||
const MAX_ROOM_MEMBERS = 30;
|
const MAX_ROOM_MEMBERS = 30;
|
||||||
|
|
||||||
@@ -74,7 +75,7 @@ export class ChatService {
|
|||||||
text?: string | null;
|
text?: string | null;
|
||||||
file?: MiDriveFile | null;
|
file?: MiDriveFile | null;
|
||||||
uri?: string | null;
|
uri?: string | null;
|
||||||
}) {
|
}): Promise<Packed<'ChatMessageLite'>> {
|
||||||
if (fromUser.id === toUser.id) {
|
if (fromUser.id === toUser.id) {
|
||||||
throw new Error('yourself');
|
throw new Error('yourself');
|
||||||
}
|
}
|
||||||
@@ -185,7 +186,7 @@ export class ChatService {
|
|||||||
text?: string | null;
|
text?: string | null;
|
||||||
file?: MiDriveFile | null;
|
file?: MiDriveFile | null;
|
||||||
uri?: string | null;
|
uri?: string | null;
|
||||||
}) {
|
}): Promise<Packed<'ChatMessageLite'>> {
|
||||||
const memberships = await this.chatRoomMembershipsRepository.findBy({ roomId: toRoom.id });
|
const memberships = await this.chatRoomMembershipsRepository.findBy({ roomId: toRoom.id });
|
||||||
|
|
||||||
if (toRoom.ownerId !== fromUser.id && !memberships.some(member => member.userId === fromUser.id)) {
|
if (toRoom.ownerId !== fromUser.id && !memberships.some(member => member.userId === fromUser.id)) {
|
||||||
@@ -294,7 +295,7 @@ export class ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async userTimeline(meId: MiUser['id'], otherId: MiUser['id'], sinceId: MiChatMessage['id'] | null, untilId: MiChatMessage['id'] | null, limit: number) {
|
public async userTimeline(meId: MiUser['id'], otherId: MiUser['id'], limit: number, sinceId?: MiChatMessage['id'] | null, untilId?: MiChatMessage['id'] | null) {
|
||||||
const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), sinceId, untilId)
|
const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), sinceId, untilId)
|
||||||
.andWhere(new Brackets(qb => {
|
.andWhere(new Brackets(qb => {
|
||||||
qb
|
qb
|
||||||
@@ -317,6 +318,16 @@ export class ChatService {
|
|||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async roomTimeline(roomId: MiChatRoom['id'], limit: number, sinceId?: MiChatMessage['id'] | null, untilId?: MiChatMessage['id'] | null) {
|
||||||
|
const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), sinceId, untilId)
|
||||||
|
.where('message.toRoomId = :roomId', { roomId });
|
||||||
|
|
||||||
|
const messages = await query.take(limit).getMany();
|
||||||
|
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async userHistory(meId: MiUser['id'], limit: number): Promise<MiChatMessage[]> {
|
public async userHistory(meId: MiUser['id'], limit: number): Promise<MiChatMessage[]> {
|
||||||
const history: MiChatMessage[] = [];
|
const history: MiChatMessage[] = [];
|
||||||
@@ -525,7 +536,8 @@ export class ChatService {
|
|||||||
@bindThis
|
@bindThis
|
||||||
public async getReceivedRoomInvitationsWithPagination(userId: MiUser['id'], limit: number, sinceId?: MiChatRoomInvitation['id'] | null, untilId?: MiChatRoomInvitation['id'] | null) {
|
public async getReceivedRoomInvitationsWithPagination(userId: MiUser['id'], limit: number, sinceId?: MiChatRoomInvitation['id'] | null, untilId?: MiChatRoomInvitation['id'] | null) {
|
||||||
const query = this.queryService.makePaginationQuery(this.chatRoomInvitationsRepository.createQueryBuilder('invitation'), sinceId, untilId)
|
const query = this.queryService.makePaginationQuery(this.chatRoomInvitationsRepository.createQueryBuilder('invitation'), sinceId, untilId)
|
||||||
.where('invitation.userId = :userId', { userId });
|
.where('invitation.userId = :userId', { userId })
|
||||||
|
.andWhere('invitation.ignored = FALSE');
|
||||||
|
|
||||||
const invitations = await query.take(limit).getMany();
|
const invitations = await query.take(limit).getMany();
|
||||||
|
|
||||||
@@ -553,9 +565,9 @@ export class ChatService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async rejectRoomInvitation(userId: MiUser['id'], roomId: MiChatRoom['id']) {
|
public async ignoreRoomInvitation(userId: MiUser['id'], roomId: MiChatRoom['id']) {
|
||||||
const invitation = await this.chatRoomInvitationsRepository.findOneByOrFail({ roomId, userId });
|
const invitation = await this.chatRoomInvitationsRepository.findOneByOrFail({ roomId, userId });
|
||||||
await this.chatRoomInvitationsRepository.delete(invitation.id);
|
await this.chatRoomInvitationsRepository.update(invitation.id, { ignored: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
|
@@ -154,8 +154,7 @@ export class ChatEntityService {
|
|||||||
createdAt: this.idService.parse(message.id).date.toISOString(),
|
createdAt: this.idService.parse(message.id).date.toISOString(),
|
||||||
text: message.text,
|
text: message.text,
|
||||||
fromUserId: message.fromUserId,
|
fromUserId: message.fromUserId,
|
||||||
toUserId: message.toUserId,
|
fromUser: packedUsers?.get(message.fromUserId) ?? await this.userEntityService.pack(message.fromUser ?? message.fromUserId),
|
||||||
toUser: packedUsers?.get(message.toUserId) ?? await this.userEntityService.pack(message.toUser ?? message.toUserId),
|
|
||||||
toRoomId: message.toRoomId,
|
toRoomId: message.toRoomId,
|
||||||
fileId: message.fileId,
|
fileId: message.fileId,
|
||||||
file: message.file ? (packedFiles?.get(message.fileId) ?? await this.driveFileEntityService.pack(message.file)) : null,
|
file: message.file ? (packedFiles?.get(message.fileId) ?? await this.driveFileEntityService.pack(message.file)) : null,
|
||||||
@@ -169,7 +168,7 @@ export class ChatEntityService {
|
|||||||
if (messages.length === 0) return [];
|
if (messages.length === 0) return [];
|
||||||
|
|
||||||
const [packedUsers, packedFiles] = await Promise.all([
|
const [packedUsers, packedFiles] = await Promise.all([
|
||||||
this.userEntityService.packMany(messages.map(x => x.fromUser))
|
this.userEntityService.packMany(messages.map(x => x.fromUser ?? x.fromUserId))
|
||||||
.then(users => new Map(users.map(u => [u.id, u]))),
|
.then(users => new Map(users.map(u => [u.id, u]))),
|
||||||
this.driveFileEntityService.packMany(messages.map(m => m.file).filter(x => x != null)),
|
this.driveFileEntityService.packMany(messages.map(m => m.file).filter(x => x != null)),
|
||||||
]);
|
]);
|
||||||
|
@@ -37,4 +37,9 @@ export class MiChatRoomInvitation {
|
|||||||
})
|
})
|
||||||
@JoinColumn()
|
@JoinColumn()
|
||||||
public room: MiChatRoom | null;
|
public room: MiChatRoom | null;
|
||||||
|
|
||||||
|
@Column('boolean', {
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
public ignored: boolean;
|
||||||
}
|
}
|
||||||
|
@@ -396,10 +396,12 @@ export * as 'users/search' from './endpoints/users/search.js';
|
|||||||
export * as 'users/search-by-username-and-host' from './endpoints/users/search-by-username-and-host.js';
|
export * as 'users/search-by-username-and-host' from './endpoints/users/search-by-username-and-host.js';
|
||||||
export * as 'users/show' from './endpoints/users/show.js';
|
export * as 'users/show' from './endpoints/users/show.js';
|
||||||
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
|
export * as 'users/update-memo' from './endpoints/users/update-memo.js';
|
||||||
export * as 'chat/messages/create' from './endpoints/chat/messages/create.js';
|
export * as 'chat/messages/create-to-user' from './endpoints/chat/messages/create-to-user.js';
|
||||||
|
export * as 'chat/messages/create-to-room' from './endpoints/chat/messages/create-to-room.js';
|
||||||
export * as 'chat/messages/delete' from './endpoints/chat/messages/delete.js';
|
export * as 'chat/messages/delete' from './endpoints/chat/messages/delete.js';
|
||||||
export * as 'chat/messages/show' from './endpoints/chat/messages/show.js';
|
export * as 'chat/messages/show' from './endpoints/chat/messages/show.js';
|
||||||
export * as 'chat/messages/timeline' from './endpoints/chat/messages/timeline.js';
|
export * as 'chat/messages/user-timeline' from './endpoints/chat/messages/user-timeline.js';
|
||||||
|
export * as 'chat/messages/room-timeline' from './endpoints/chat/messages/room-timeline.js';
|
||||||
export * as 'chat/rooms/create' from './endpoints/chat/rooms/create.js';
|
export * as 'chat/rooms/create' from './endpoints/chat/rooms/create.js';
|
||||||
export * as 'chat/rooms/delete' from './endpoints/chat/rooms/delete.js';
|
export * as 'chat/rooms/delete' from './endpoints/chat/rooms/delete.js';
|
||||||
export * as 'chat/rooms/join' from './endpoints/chat/rooms/join.js';
|
export * as 'chat/rooms/join' from './endpoints/chat/rooms/join.js';
|
||||||
@@ -409,7 +411,7 @@ export * as 'chat/rooms/owned' from './endpoints/chat/rooms/owned.js';
|
|||||||
export * as 'chat/rooms/update' from './endpoints/chat/rooms/update.js';
|
export * as 'chat/rooms/update' from './endpoints/chat/rooms/update.js';
|
||||||
export * as 'chat/rooms/members' from './endpoints/chat/rooms/members.js';
|
export * as 'chat/rooms/members' from './endpoints/chat/rooms/members.js';
|
||||||
export * as 'chat/rooms/invitations/create' from './endpoints/chat/rooms/invitations/create.js';
|
export * as 'chat/rooms/invitations/create' from './endpoints/chat/rooms/invitations/create.js';
|
||||||
export * as 'chat/rooms/invitations/reject' from './endpoints/chat/rooms/invitations/reject.js';
|
export * as 'chat/rooms/invitations/ignore' from './endpoints/chat/rooms/invitations/ignore.js';
|
||||||
export * as 'chat/rooms/invitations/inbox' from './endpoints/chat/rooms/invitations/inbox.js';
|
export * as 'chat/rooms/invitations/inbox' from './endpoints/chat/rooms/invitations/inbox.js';
|
||||||
export * as 'chat/history' from './endpoints/chat/history.js';
|
export * as 'chat/history' from './endpoints/chat/history.js';
|
||||||
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';
|
export * as 'v2/admin/emoji/list' from './endpoints/v2/admin/emoji/list.js';
|
||||||
|
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import ms from 'ms';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
import { ChatService } from '@/core/ChatService.js';
|
||||||
|
import type { DriveFilesRepository, MiUser } from '@/models/_.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['chat'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
requiredRolePolicy: 'canChat',
|
||||||
|
|
||||||
|
prohibitMoved: true,
|
||||||
|
|
||||||
|
kind: 'write:chat',
|
||||||
|
|
||||||
|
limit: {
|
||||||
|
duration: ms('1hour'),
|
||||||
|
max: 500,
|
||||||
|
},
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'ChatMessageLite',
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchRoom: {
|
||||||
|
message: 'No such room.',
|
||||||
|
code: 'NO_SUCH_ROOM',
|
||||||
|
id: '8098520d-2da5-4e8f-8ee1-df78b55a4ec6',
|
||||||
|
},
|
||||||
|
|
||||||
|
noSuchFile: {
|
||||||
|
message: 'No such file.',
|
||||||
|
code: 'NO_SUCH_FILE',
|
||||||
|
id: 'b6accbd3-1d7b-4d9f-bdb7-eb185bac06db',
|
||||||
|
},
|
||||||
|
|
||||||
|
contentRequired: {
|
||||||
|
message: 'Content required. You need to set text or fileId.',
|
||||||
|
code: 'CONTENT_REQUIRED',
|
||||||
|
id: '340517b7-6d04-42c0-bac1-37ee804e3594',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const paramDef = {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
text: { type: 'string', nullable: true, maxLength: 2000 },
|
||||||
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
toRoomId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['toRoomId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
@Inject(DI.driveFilesRepository)
|
||||||
|
private driveFilesRepository: DriveFilesRepository,
|
||||||
|
|
||||||
|
private getterService: GetterService,
|
||||||
|
private chatService: ChatService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const room = await this.chatService.findRoomById(ps.toRoomId);
|
||||||
|
if (room == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = null;
|
||||||
|
if (ps.fileId != null) {
|
||||||
|
file = await this.driveFilesRepository.findOneBy({
|
||||||
|
id: ps.fileId,
|
||||||
|
userId: me.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (file == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// テキストが無いかつ添付ファイルも無かったらエラー
|
||||||
|
if (ps.text == null && file == null) {
|
||||||
|
throw new ApiError(meta.errors.contentRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.chatService.createMessageToRoom(me, room, {
|
||||||
|
text: ps.text,
|
||||||
|
file: file,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -46,18 +46,6 @@ export const meta = {
|
|||||||
id: '11795c64-40ea-4198-b06e-3c873ed9039d',
|
id: '11795c64-40ea-4198-b06e-3c873ed9039d',
|
||||||
},
|
},
|
||||||
|
|
||||||
noSuchRoom: {
|
|
||||||
message: 'No such room.',
|
|
||||||
code: 'NO_SUCH_ROOM',
|
|
||||||
id: 'c94e2a5d-06aa-4914-8fa6-6a42e73d6537',
|
|
||||||
},
|
|
||||||
|
|
||||||
roomAccessDenied: {
|
|
||||||
message: 'You can not send messages to rooms that you have not joined.',
|
|
||||||
code: 'ROOM_ACCESS_DENIED',
|
|
||||||
id: 'd96b3cca-5ad1-438b-ad8b-02f931308fbd',
|
|
||||||
},
|
|
||||||
|
|
||||||
noSuchFile: {
|
noSuchFile: {
|
||||||
message: 'No such file.',
|
message: 'No such file.',
|
||||||
code: 'NO_SUCH_FILE',
|
code: 'NO_SUCH_FILE',
|
||||||
@@ -83,8 +71,9 @@ export const paramDef = {
|
|||||||
properties: {
|
properties: {
|
||||||
text: { type: 'string', nullable: true, maxLength: 2000 },
|
text: { type: 'string', nullable: true, maxLength: 2000 },
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
toUserId: { type: 'string', format: 'misskey:id', nullable: true },
|
toUserId: { type: 'string', format: 'misskey:id' },
|
||||||
},
|
},
|
||||||
|
required: ['toUserId'],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -114,7 +103,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
throw new ApiError(meta.errors.contentRequired);
|
throw new ApiError(meta.errors.contentRequired);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ps.toUserId != null) {
|
|
||||||
// Myself
|
// Myself
|
||||||
if (ps.toUserId === me.id) {
|
if (ps.toUserId === me.id) {
|
||||||
throw new ApiError(meta.errors.recipientIsYourself);
|
throw new ApiError(meta.errors.recipientIsYourself);
|
||||||
@@ -129,24 +117,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
text: ps.text,
|
text: ps.text,
|
||||||
file: file,
|
file: file,
|
||||||
});
|
});
|
||||||
}/* else if (ps.toRoomId != null) {
|
|
||||||
// Fetch recipient (room)
|
|
||||||
recipientRoom = await this.userRoomsRepository.findOneBy({ id: ps.toRoomId! });
|
|
||||||
|
|
||||||
if (recipientRoom == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchRoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check joined
|
|
||||||
const joining = await this.userRoomJoiningsRepository.findOneBy({
|
|
||||||
userId: me.id,
|
|
||||||
userRoomId: recipientRoom.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (joining == null) {
|
|
||||||
throw new ApiError(meta.errors.roomAccessDenied);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { ChatService } from '@/core/ChatService.js';
|
||||||
|
import { ChatEntityService } from '@/core/entities/ChatEntityService.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['chat'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:chat',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'ChatMessageLite',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchRoom: {
|
||||||
|
message: 'No such room.',
|
||||||
|
code: 'NO_SUCH_ROOM',
|
||||||
|
id: 'c4d9f88c-9270-4632-b032-6ed8cee36f7f',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} 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' },
|
||||||
|
roomId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['roomId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private chatEntityService: ChatEntityService,
|
||||||
|
private chatService: ChatService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const room = await this.chatService.findRoomById(ps.roomId);
|
||||||
|
if (room == null) {
|
||||||
|
throw new ApiError(meta.errors.noSuchRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await this.chatService.isRoomMember(room.id, me.id)) && room.ownerId !== me.id) {
|
||||||
|
throw new ApiError(meta.errors.noSuchRoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = await this.chatService.roomTimeline(room.id, ps.limit, ps.sinceId, ps.untilId);
|
||||||
|
|
||||||
|
this.chatService.readRoomChatMessage(me.id, room.id);
|
||||||
|
|
||||||
|
return await this.chatEntityService.packMessagesLiteForRoom(messages);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
|
||||||
import { DI } from '@/di-symbols.js';
|
|
||||||
import { GetterService } from '@/server/api/GetterService.js';
|
|
||||||
import { ChatService } from '@/core/ChatService.js';
|
|
||||||
import { ChatEntityService } from '@/core/entities/ChatEntityService.js';
|
|
||||||
import { ApiError } from '@/server/api/error.js';
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ['chat'],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: 'read:chat',
|
|
||||||
|
|
||||||
res: {
|
|
||||||
type: 'array',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
ref: 'ChatMessageLite',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
errors: {
|
|
||||||
noSuchUser: {
|
|
||||||
message: 'No such user.',
|
|
||||||
code: 'NO_SUCH_USER',
|
|
||||||
id: '11795c64-40ea-4198-b06e-3c873ed9039d',
|
|
||||||
},
|
|
||||||
|
|
||||||
noSuchRoom: {
|
|
||||||
message: 'No such room.',
|
|
||||||
code: 'NO_SUCH_ROOM',
|
|
||||||
id: 'c4d9f88c-9270-4632-b032-6ed8cee36f7f',
|
|
||||||
},
|
|
||||||
|
|
||||||
roomAccessDenied: {
|
|
||||||
message: 'You can not read messages of rooms that you have not joined.',
|
|
||||||
code: 'ROOM_ACCESS_DENIED',
|
|
||||||
id: 'a053a8dd-a491-4718-8f87-50775aad9284',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} 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' },
|
|
||||||
userId: { type: 'string', format: 'misskey:id', nullable: true },
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
|
||||||
constructor(
|
|
||||||
private chatEntityService: ChatEntityService,
|
|
||||||
private chatService: ChatService,
|
|
||||||
private getterService: GetterService,
|
|
||||||
) {
|
|
||||||
super(meta, paramDef, async (ps, me) => {
|
|
||||||
if (ps.userId != null) {
|
|
||||||
const other = 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 messages = await this.chatService.userTimeline(me.id, other.id, ps.sinceId, ps.untilId, ps.limit);
|
|
||||||
|
|
||||||
this.chatService.readUserChatMessage(me.id, other.id);
|
|
||||||
|
|
||||||
return await this.chatEntityService.packMessagesLite(messages);
|
|
||||||
}/* else if (ps.roomId != null) {
|
|
||||||
// Fetch recipient (room)
|
|
||||||
const recipientRoom = await this.userRoomRepository.findOneBy({ id: ps.roomId });
|
|
||||||
|
|
||||||
if (recipientRoom == null) {
|
|
||||||
throw new ApiError(meta.errors.noSuchRoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check joined
|
|
||||||
const joining = await this.userRoomJoiningsRepository.findOneBy({
|
|
||||||
userId: me.id,
|
|
||||||
userRoomId: recipientRoom.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (joining == null) {
|
|
||||||
throw new ApiError(meta.errors.roomAccessDenied);
|
|
||||||
}
|
|
||||||
|
|
||||||
const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), ps.sinceId, ps.untilId)
|
|
||||||
.andWhere('message.roomId = :roomId', { roomId: recipientRoom.id });
|
|
||||||
|
|
||||||
const messages = await query.take(ps.limit).getMany();
|
|
||||||
|
|
||||||
// Mark all as read
|
|
||||||
if (ps.markAsRead) {
|
|
||||||
this.chatService.readRoomMessagingMessage(me.id, recipientRoom.id, messages.map(x => x.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
return await Promise.all(messages.map(message => this.chatMessageEntityService.pack(message, me, {
|
|
||||||
populateRoom: false,
|
|
||||||
})));
|
|
||||||
}*/
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||||
|
import { DI } from '@/di-symbols.js';
|
||||||
|
import { GetterService } from '@/server/api/GetterService.js';
|
||||||
|
import { ChatService } from '@/core/ChatService.js';
|
||||||
|
import { ChatEntityService } from '@/core/entities/ChatEntityService.js';
|
||||||
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
tags: ['chat'],
|
||||||
|
|
||||||
|
requireCredential: true,
|
||||||
|
|
||||||
|
kind: 'read:chat',
|
||||||
|
|
||||||
|
res: {
|
||||||
|
type: 'array',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
optional: false, nullable: false,
|
||||||
|
ref: 'ChatMessageLite',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchUser: {
|
||||||
|
message: 'No such user.',
|
||||||
|
code: 'NO_SUCH_USER',
|
||||||
|
id: '11795c64-40ea-4198-b06e-3c873ed9039d',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} 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' },
|
||||||
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
|
||||||
|
constructor(
|
||||||
|
private chatEntityService: ChatEntityService,
|
||||||
|
private chatService: ChatService,
|
||||||
|
private getterService: GetterService,
|
||||||
|
) {
|
||||||
|
super(meta, paramDef, async (ps, me) => {
|
||||||
|
const other = 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 messages = await this.chatService.userTimeline(me.id, other.id, ps.limit, ps.sinceId, ps.untilId);
|
||||||
|
|
||||||
|
this.chatService.readUserChatMessage(me.id, other.id);
|
||||||
|
|
||||||
|
return await this.chatEntityService.packMessagesLite(messages);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -61,7 +61,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
if (room == null) {
|
if (room == null) {
|
||||||
throw new ApiError(meta.errors.noSuchRoom);
|
throw new ApiError(meta.errors.noSuchRoom);
|
||||||
}
|
}
|
||||||
const invitation = await this.chatService.createRoomInvitation(ps.userId, room.id, ps.userId);
|
const invitation = await this.chatService.createRoomInvitation(me.id, room.id, ps.userId);
|
||||||
return await this.chatEntityService.packRoomInvitation(invitation, me);
|
return await this.chatEntityService.packRoomInvitation(invitation, me);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -42,7 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
|
|||||||
private chatService: ChatService,
|
private chatService: ChatService,
|
||||||
) {
|
) {
|
||||||
super(meta, paramDef, async (ps, me) => {
|
super(meta, paramDef, async (ps, me) => {
|
||||||
await this.chatService.rejectRoomInvitation(me.id, ps.roomId);
|
await this.chatService.ignoreRoomInvitation(me.id, ps.roomId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
204
packages/frontend/src/pages/chat/home.history.vue
Normal file
204
packages/frontend/src/pages/chat/home.history.vue
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="_gaps">
|
||||||
|
<MkButton primary gradate :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
||||||
|
|
||||||
|
<div v-if="history.length > 0" class="_gaps_s">
|
||||||
|
<MkA
|
||||||
|
v-for="item in history"
|
||||||
|
:key="item.id"
|
||||||
|
:class="[$style.message, { [$style.isMe]: item.isMe, [$style.isRead]: item.message.isRead }]"
|
||||||
|
class="_panel"
|
||||||
|
:to="item.message.toRoomId ? `/chat/room/${item.message.toRoomId}` : `/chat/user/${item.other!.id}`"
|
||||||
|
>
|
||||||
|
<MkAvatar v-if="item.other" :class="$style.messageAvatar" :user="item.other" indicator :preview="false"/>
|
||||||
|
<div :class="$style.messageBody">
|
||||||
|
<header v-if="item.message.toRoom" :class="$style.messageHeader">
|
||||||
|
<span :class="$style.messageHeaderName">{{ item.message.toRoom.name }}</span>
|
||||||
|
<MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/>
|
||||||
|
</header>
|
||||||
|
<header v-else :class="$style.messageHeader">
|
||||||
|
<MkUserName :class="$style.messageHeaderName" :user="item.other!"/>
|
||||||
|
<MkAcct :class="$style.messageHeaderUsername" :user="item.other!"/>
|
||||||
|
<MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/>
|
||||||
|
</header>
|
||||||
|
<div :class="$style.messageBodyText"><span v-if="item.isMe" :class="$style.youSaid">{{ i18n.ts.you }}:</span>{{ item.message.text }}</div>
|
||||||
|
</div>
|
||||||
|
</MkA>
|
||||||
|
</div>
|
||||||
|
<div v-if="!fetching && history.length == 0" class="_fullinfo">
|
||||||
|
<div>{{ i18n.ts._chat.noHistory }}</div>
|
||||||
|
</div>
|
||||||
|
<MkLoading v-if="fetching"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
import { ensureSignin } from '@/i.js';
|
||||||
|
import { useRouter } from '@/router.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import { updateCurrentAccountPartial } from '@/accounts.js';
|
||||||
|
|
||||||
|
const $i = ensureSignin();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const fetching = ref(true);
|
||||||
|
const history = ref<{
|
||||||
|
id: string;
|
||||||
|
message: Misskey.entities.ChatMessage;
|
||||||
|
other: Misskey.entities.ChatMessage['fromUser'] | Misskey.entities.ChatMessage['toUser'] | null;
|
||||||
|
isMe: boolean;
|
||||||
|
}[]>([]);
|
||||||
|
|
||||||
|
function start(ev: MouseEvent) {
|
||||||
|
os.popupMenu([{
|
||||||
|
text: i18n.ts._chat.individualChat,
|
||||||
|
caption: i18n.ts._chat.individualChat_description,
|
||||||
|
icon: 'ti ti-user',
|
||||||
|
action: () => { startUser(); },
|
||||||
|
}, { type: 'divider' }, {
|
||||||
|
type: 'parent',
|
||||||
|
text: i18n.ts._chat.roomChat,
|
||||||
|
caption: i18n.ts._chat.roomChat_description,
|
||||||
|
icon: 'ti ti-users-group',
|
||||||
|
children: [{
|
||||||
|
text: i18n.ts._chat.createRoom,
|
||||||
|
icon: 'ti ti-plus',
|
||||||
|
action: () => { createRoom(); },
|
||||||
|
}],
|
||||||
|
}], ev.currentTarget ?? ev.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startUser() {
|
||||||
|
os.selectUser().then(user => {
|
||||||
|
router.push(`/chat/user/${user.id}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createRoom() {
|
||||||
|
const { canceled, result } = await os.inputText({
|
||||||
|
title: i18n.ts.name,
|
||||||
|
minLength: 1,
|
||||||
|
});
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
const room = await misskeyApi('chat/rooms/create', {
|
||||||
|
name: result,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push(`/chat/room/${room.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchHistory() {
|
||||||
|
fetching.value = true;
|
||||||
|
|
||||||
|
const [userMessages, roomMessages] = await Promise.all([
|
||||||
|
misskeyApi('chat/history', { room: false }),
|
||||||
|
misskeyApi('chat/history', { room: true }),
|
||||||
|
]);
|
||||||
|
|
||||||
|
history.value = [...userMessages, ...roomMessages]
|
||||||
|
.toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
||||||
|
.map(m => ({
|
||||||
|
id: m.id,
|
||||||
|
message: m,
|
||||||
|
other: m.room == null ? (m.fromUserId === $i.id ? m.toUser : m.fromUser) : null,
|
||||||
|
isMe: m.fromUserId === $i.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
fetching.value = false;
|
||||||
|
|
||||||
|
updateCurrentAccountPartial({ hasUnreadChatMessages: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchHistory();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.start {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
padding: 16px 24px;
|
||||||
|
|
||||||
|
&.isRead,
|
||||||
|
&.isMe {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.isMe):not(.isRead) {
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
right: 8px;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 100%;
|
||||||
|
background-color: var(--MI_THEME-accent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageAvatar {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
margin: 0 16px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageBody {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageHeader {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageHeaderName {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageHeaderUsername {
|
||||||
|
margin: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageHeaderTime {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.messageBodyText {
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.youSaid {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 0.5em;
|
||||||
|
}
|
||||||
|
</style>
|
96
packages/frontend/src/pages/chat/home.invitations.vue
Normal file
96
packages/frontend/src/pages/chat/home.invitations.vue
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="_gaps">
|
||||||
|
<div v-if="invitations.length > 0" class="_gaps_s">
|
||||||
|
<MkFolder v-for="invitation in invitations" :key="invitation.id" :defaultOpen="true">
|
||||||
|
<template #icon><i class="ti ti-users-group"></i></template>
|
||||||
|
<template #label>{{ invitation.room.name }}</template>
|
||||||
|
<template #suffix><MkTime :time="invitation.createdAt"/></template>
|
||||||
|
<template #footer>
|
||||||
|
<div class="_buttons">
|
||||||
|
<MkButton primary @click="join(invitation)"><i class="ti ti-plus"></i> {{ i18n.ts._chat.join }}</MkButton>
|
||||||
|
<MkButton danger @click="ignore(invitation)"><i class="ti ti-x"></i> {{ i18n.ts._chat.ignore }}</MkButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div :class="$style.invitationBody">
|
||||||
|
<MkAvatar :user="invitation.room.owner" :class="$style.invitationBodyAvatar" link/>
|
||||||
|
<div>
|
||||||
|
<MkUserName :user="invitation.room.owner"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MkFolder>
|
||||||
|
</div>
|
||||||
|
<div v-if="!fetching && invitations.length == 0" class="_fullinfo">
|
||||||
|
<div>{{ i18n.ts._chat.noInvitations }}</div>
|
||||||
|
</div>
|
||||||
|
<MkLoading v-if="fetching"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
import { ensureSignin } from '@/i.js';
|
||||||
|
import { useRouter } from '@/router.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
import MkFolder from '@/components/MkFolder.vue';
|
||||||
|
|
||||||
|
const $i = ensureSignin();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const fetching = ref(true);
|
||||||
|
const invitations = ref<Misskey.entities.ChatRoomInvitation[]>([]);
|
||||||
|
|
||||||
|
async function fetchInvitations() {
|
||||||
|
fetching.value = true;
|
||||||
|
|
||||||
|
const res = await misskeyApi('chat/rooms/invitations/inbox', {
|
||||||
|
});
|
||||||
|
|
||||||
|
invitations.value = res;
|
||||||
|
|
||||||
|
fetching.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function join(invitation: Misskey.entities.ChatRoomInvitation) {
|
||||||
|
await misskeyApi('chat/rooms/join', {
|
||||||
|
roomId: invitation.room.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
router.push(`/chat/room/${invitation.room.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ignore(invitation: Misskey.entities.ChatRoomInvitation) {
|
||||||
|
await misskeyApi('chat/rooms/invitations/ignore', {
|
||||||
|
roomId: invitation.room.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
invitations.value = invitations.value.filter(i => i.id !== invitation.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchInvitations();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.invitationBody {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invitationBodyAvatar {
|
||||||
|
margin-right: 12px;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
57
packages/frontend/src/pages/chat/home.ownedRooms.vue
Normal file
57
packages/frontend/src/pages/chat/home.ownedRooms.vue
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="_gaps">
|
||||||
|
<div v-if="rooms.length > 0" class="_gaps_s">
|
||||||
|
<MkA v-for="room in rooms" :key="room.id" :to="`/chat/room/${room.id}`" class="_panel" :class="$style.room">
|
||||||
|
<div>{{ room.name }}</div>
|
||||||
|
</MkA>
|
||||||
|
</div>
|
||||||
|
<div v-if="!fetching && rooms.length == 0" class="_fullinfo">
|
||||||
|
<div>{{ i18n.ts._chat.noRooms }}</div>
|
||||||
|
</div>
|
||||||
|
<MkLoading v-if="fetching"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue';
|
||||||
|
import * as Misskey from 'misskey-js';
|
||||||
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
import { i18n } from '@/i18n.js';
|
||||||
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
import { ensureSignin } from '@/i.js';
|
||||||
|
import { useRouter } from '@/router.js';
|
||||||
|
import * as os from '@/os.js';
|
||||||
|
|
||||||
|
const $i = ensureSignin();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const fetching = ref(true);
|
||||||
|
const rooms = ref<Misskey.entities.ChatRoom[]>([]);
|
||||||
|
|
||||||
|
async function fetchRooms() {
|
||||||
|
fetching.value = true;
|
||||||
|
|
||||||
|
const res = await misskeyApi('chat/rooms/owned', {
|
||||||
|
});
|
||||||
|
|
||||||
|
rooms.value = res;
|
||||||
|
|
||||||
|
fetching.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchRooms();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.room {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
@@ -4,139 +4,47 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="_gaps">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<MkButton primary gradate :class="$style.start" @click="start"><i class="ti ti-plus"></i> {{ i18n.ts.startChat }}</MkButton>
|
<XHistory v-if="tab === 'home'"/>
|
||||||
|
<XInvitations v-else-if="tab === 'invitations'"/>
|
||||||
<div v-if="history.length > 0" :class="$style.history">
|
<XOwnedRooms v-else-if="tab === 'ownedRooms'"/>
|
||||||
<MkA
|
</MkHorizontalSwipe>
|
||||||
v-for="item in history"
|
|
||||||
:key="item.id"
|
|
||||||
:class="[$style.message, { [$style.isMe]: item.isMe, [$style.isRead]: item.message.isRead }]"
|
|
||||||
class="_panel"
|
|
||||||
:to="item.message.toRoomId ? `/chat/room/${item.message.toRoomId}` : `/chat/user/${item.other!.id}`"
|
|
||||||
>
|
|
||||||
<MkAvatar v-if="item.other" :class="$style.messageAvatar" :user="item.other" indicator :preview="false"/>
|
|
||||||
<div :class="$style.messageBody">
|
|
||||||
<header v-if="item.message.room" :class="$style.messageHeader">
|
|
||||||
<span :class="$style.messageHeaderName">{{ item.message.room.name }}</span>
|
|
||||||
<MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/>
|
|
||||||
</header>
|
|
||||||
<header v-else :class="$style.messageHeader">
|
|
||||||
<MkUserName :class="$style.messageHeaderName" :user="item.other!"/>
|
|
||||||
<MkAcct :class="$style.messageHeaderUsername" :user="item.other!"/>
|
|
||||||
<MkTime :time="item.message.createdAt" :class="$style.messageHeaderTime"/>
|
|
||||||
</header>
|
|
||||||
<div :class="$style.messageBodyText"><span v-if="item.isMe" :class="$style.iSaid">{{ i18n.ts.you }}:</span>{{ item.message.text }}</div>
|
|
||||||
</div>
|
|
||||||
</MkA>
|
|
||||||
</div>
|
|
||||||
<div v-if="!fetching && history.length == 0" class="_fullinfo">
|
|
||||||
<div>{{ i18n.ts.noHistory }}</div>
|
|
||||||
</div>
|
|
||||||
<MkLoading v-if="fetching"/>
|
|
||||||
</div>
|
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</PageWithHeader>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, onMounted, ref } from 'vue';
|
import { computed, onMounted, ref } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import XHistory from './home.history.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import XInvitations from './home.invitations.vue';
|
||||||
|
import XOwnedRooms from './home.ownedRooms.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
||||||
import { ensureSignin } from '@/i.js';
|
|
||||||
import { useRouter } from '@/router.js';
|
|
||||||
import * as os from '@/os.js';
|
|
||||||
import { updateCurrentAccountPartial } from '@/accounts.js';
|
|
||||||
|
|
||||||
const $i = ensureSignin();
|
const tab = ref('home');
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const fetching = ref(true);
|
|
||||||
const history = ref<{
|
|
||||||
id: string;
|
|
||||||
message: Misskey.entities.ChatMessage;
|
|
||||||
other: Misskey.entities.ChatMessage['fromUser'] | Misskey.entities.ChatMessage['toUser'] | null;
|
|
||||||
isMe: boolean;
|
|
||||||
}[]>([]);
|
|
||||||
|
|
||||||
function start(ev: MouseEvent) {
|
|
||||||
os.popupMenu([{
|
|
||||||
text: i18n.ts._chat.individualChat,
|
|
||||||
caption: i18n.ts._chat.individualChat_description,
|
|
||||||
icon: 'ti ti-user',
|
|
||||||
action: () => { startUser(); },
|
|
||||||
}, { type: 'divider' }, {
|
|
||||||
text: i18n.ts._chat.roomChat,
|
|
||||||
caption: i18n.ts._chat.roomChat_description,
|
|
||||||
icon: 'ti ti-users',
|
|
||||||
action: () => { startRoom(); },
|
|
||||||
}], ev.currentTarget ?? ev.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startUser() {
|
|
||||||
os.selectUser().then(user => {
|
|
||||||
router.push(`/chat/user/${user.id}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function startRoom() {
|
|
||||||
/*
|
|
||||||
const rooms1 = await os.api('users/rooms/owned');
|
|
||||||
const rooms2 = await os.api('users/rooms/joined');
|
|
||||||
if (rooms1.length === 0 && rooms2.length === 0) {
|
|
||||||
os.alert({
|
|
||||||
type: 'warning',
|
|
||||||
title: i18n.ts.youHaveNoGroups,
|
|
||||||
text: i18n.ts.joinOrCreateGroup,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { canceled, result: room } = await os.select({
|
|
||||||
title: i18n.ts.room,
|
|
||||||
items: rooms1.concat(rooms2).map(room => ({
|
|
||||||
value: room, text: room.name,
|
|
||||||
})),
|
|
||||||
});
|
|
||||||
if (canceled) return;
|
|
||||||
router.push(`/chat/room/${room.id}`);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetchHistory() {
|
|
||||||
fetching.value = true;
|
|
||||||
|
|
||||||
const [userMessages, roomMessages] = await Promise.all([
|
|
||||||
misskeyApi('chat/history', { room: false }),
|
|
||||||
misskeyApi('chat/history', { room: true }),
|
|
||||||
]);
|
|
||||||
|
|
||||||
history.value = [...userMessages, ...roomMessages]
|
|
||||||
.toSorted((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
||||||
.map(m => ({
|
|
||||||
id: m.id,
|
|
||||||
message: m,
|
|
||||||
other: m.room == null ? (m.fromUserId === $i.id ? m.toUser : m.fromUser) : null,
|
|
||||||
isMe: m.fromUserId === $i.id,
|
|
||||||
}));
|
|
||||||
|
|
||||||
fetching.value = false;
|
|
||||||
|
|
||||||
updateCurrentAccountPartial({ hasUnreadChatMessages: false });
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
fetchHistory();
|
|
||||||
});
|
|
||||||
|
|
||||||
const headerActions = computed(() => []);
|
const headerActions = computed(() => []);
|
||||||
|
|
||||||
const headerTabs = computed(() => []);
|
const headerTabs = computed(() => [{
|
||||||
|
key: 'home',
|
||||||
|
title: i18n.ts._chat.history,
|
||||||
|
icon: 'ti ti-home',
|
||||||
|
}, {
|
||||||
|
key: 'invitations',
|
||||||
|
title: i18n.ts._chat.invitations,
|
||||||
|
icon: 'ti ti-ticket',
|
||||||
|
}, {
|
||||||
|
key: 'joiningRooms',
|
||||||
|
title: i18n.ts._chat.joiningRooms,
|
||||||
|
icon: 'ti ti-users-group',
|
||||||
|
}, {
|
||||||
|
key: 'ownedRooms',
|
||||||
|
title: i18n.ts._chat.yourRooms,
|
||||||
|
icon: 'ti ti-settings',
|
||||||
|
}]);
|
||||||
|
|
||||||
definePage(() => ({
|
definePage(() => ({
|
||||||
title: i18n.ts.chat,
|
title: i18n.ts.chat,
|
||||||
@@ -145,78 +53,4 @@ definePage(() => ({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.start {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
padding: 16px 24px;
|
|
||||||
|
|
||||||
&.isRead,
|
|
||||||
&.isMe {
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:not(.isMe):not(.isRead) {
|
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 8px;
|
|
||||||
right: 8px;
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 100%;
|
|
||||||
background-color: var(--MI_THEME-accent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageAvatar {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
margin: 0 16px 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageBody {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageHeader {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: clip;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageHeaderName {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
font-size: 1em;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageHeaderUsername {
|
|
||||||
margin: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageHeaderTime {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.messageBodyText {
|
|
||||||
overflow: hidden;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.iSaid {
|
|
||||||
font-weight: bold;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@@ -168,9 +168,10 @@ function send() {
|
|||||||
if (!canSend.value) return;
|
if (!canSend.value) return;
|
||||||
|
|
||||||
sending.value = true;
|
sending.value = true;
|
||||||
misskeyApi('chat/messages/create', {
|
|
||||||
toUserId: props.user ? props.user.id : undefined,
|
if (props.user) {
|
||||||
toRoomId: props.room ? props.room.id : undefined,
|
misskeyApi('chat/messages/create-to-user', {
|
||||||
|
toUserId: props.user.id,
|
||||||
text: text.value ? text.value : undefined,
|
text: text.value ? text.value : undefined,
|
||||||
fileId: file.value ? file.value.id : undefined,
|
fileId: file.value ? file.value.id : undefined,
|
||||||
}).then(message => {
|
}).then(message => {
|
||||||
@@ -180,6 +181,19 @@ function send() {
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
sending.value = false;
|
sending.value = false;
|
||||||
});
|
});
|
||||||
|
} else if (props.room) {
|
||||||
|
misskeyApi('chat/messages/create-to-room', {
|
||||||
|
toRoomId: props.room.id,
|
||||||
|
text: text.value ? text.value : undefined,
|
||||||
|
fileId: file.value ? file.value.id : undefined,
|
||||||
|
}).then(message => {
|
||||||
|
clear();
|
||||||
|
}).catch(err => {
|
||||||
|
console.error(err);
|
||||||
|
}).then(() => {
|
||||||
|
sending.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
|
@@ -4,20 +4,23 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageWithHeader reversed>
|
<PageWithHeader reversed :actions="headerActions">
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div v-if="initializing">
|
<div v-if="initializing">
|
||||||
<MkLoading/>
|
<MkLoading/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="messages.length === 0">
|
<div v-else-if="messages.length === 0">
|
||||||
<div class="_gaps" style="text-align: center;">
|
<div class="_gaps" style="text-align: center;">
|
||||||
<div>{{ i18n.ts.noMessagesYet }}</div>
|
<div>{{ i18n.ts._chat.noMessagesYet }}</div>
|
||||||
<template v-if="user">
|
<template v-if="user">
|
||||||
<div v-if="user.chatScope === 'followers'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowers }}</div>
|
<div v-if="user.chatScope === 'followers'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowers }}</div>
|
||||||
<div v-else-if="user.chatScope === 'following'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowing }}</div>
|
<div v-else-if="user.chatScope === 'following'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromFollowing }}</div>
|
||||||
<div v-else-if="user.chatScope === 'mutual'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromMutualFollowing }}</div>
|
<div v-else-if="user.chatScope === 'mutual'">{{ i18n.ts._chat.thisUserAllowsChatOnlyFromMutualFollowing }}</div>
|
||||||
<div v-else>{{ i18n.ts._chat.thisUserNotAllowedChatAnyone }}</div>
|
<div v-else>{{ i18n.ts._chat.thisUserNotAllowedChatAnyone }}</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else-if="room">
|
||||||
|
<div>{{ i18n.ts._chat.inviteUserToChat }}</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="_gaps">
|
<div v-else class="_gaps">
|
||||||
@@ -33,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
:moveClass="prefer.s.animation ? $style.transition_x_move : ''"
|
:moveClass="prefer.s.animation ? $style.transition_x_move : ''"
|
||||||
tag="div" class="_gaps"
|
tag="div" class="_gaps"
|
||||||
>
|
>
|
||||||
<XMessage v-for="message in messages.toReversed()" :key="message.id" :message="message" :user="message.fromUserId === $i.id ? $i : user" :isRoom="room != null"/>
|
<XMessage v-for="message in messages.toReversed()" :key="message.id" :message="message" :user="room != null ? message.fromUser : (message.fromUserId === $i.id ? $i : user)" :isRoom="room != null"/>
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
@@ -61,6 +64,7 @@ import * as Misskey from 'misskey-js';
|
|||||||
import { isTailVisible } from '@@/js/scroll.js';
|
import { isTailVisible } from '@@/js/scroll.js';
|
||||||
import XMessage from './room.message.vue';
|
import XMessage from './room.message.vue';
|
||||||
import XForm from './room.form.vue';
|
import XForm from './room.form.vue';
|
||||||
|
import type { MenuItem } from '@/types/menu.js';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { useStream } from '@/stream.js';
|
import { useStream } from '@/stream.js';
|
||||||
import * as sound from '@/utility/sound.js';
|
import * as sound from '@/utility/sound.js';
|
||||||
@@ -84,7 +88,7 @@ const messages = ref<Misskey.entities.ChatMessage[]>([]);
|
|||||||
const canFetchMore = ref(false);
|
const canFetchMore = ref(false);
|
||||||
const user = ref<Misskey.entities.UserDetailed | null>(null);
|
const user = ref<Misskey.entities.UserDetailed | null>(null);
|
||||||
const room = ref<Misskey.entities.ChatRoom | null>(null);
|
const room = ref<Misskey.entities.ChatRoom | null>(null);
|
||||||
const connection = ref<Misskey.ChannelConnection<Misskey.Channels['chat']> | null>(null);
|
const connection = ref<Misskey.ChannelConnection<Misskey.Channels['chatUser'] | Misskey.Channels['chatRoom']> | null>(null);
|
||||||
const showIndicator = ref(false);
|
const showIndicator = ref(false);
|
||||||
|
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
@@ -95,7 +99,7 @@ async function initialize() {
|
|||||||
if (props.userId) {
|
if (props.userId) {
|
||||||
const [u, m] = await Promise.all([
|
const [u, m] = await Promise.all([
|
||||||
misskeyApi('users/show', { userId: props.userId }),
|
misskeyApi('users/show', { userId: props.userId }),
|
||||||
misskeyApi('chat/messages/timeline', { userId: props.userId, limit: LIMIT }),
|
misskeyApi('chat/messages/user-timeline', { userId: props.userId, limit: LIMIT }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
user.value = u;
|
user.value = u;
|
||||||
@@ -113,7 +117,7 @@ async function initialize() {
|
|||||||
} else {
|
} else {
|
||||||
const [r, m] = await Promise.all([
|
const [r, m] = await Promise.all([
|
||||||
misskeyApi('chat/rooms/show', { roomId: props.roomId }),
|
misskeyApi('chat/rooms/show', { roomId: props.roomId }),
|
||||||
misskeyApi('chat/messages/timeline', { roomId: props.roomId, limit: LIMIT }),
|
misskeyApi('chat/messages/room-timeline', { roomId: props.roomId, limit: LIMIT }),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
room.value = r;
|
room.value = r;
|
||||||
@@ -124,7 +128,7 @@ async function initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
connection.value = useStream().useChannel('chatRoom', {
|
connection.value = useStream().useChannel('chatRoom', {
|
||||||
otherId: user.value.id,
|
roomId: room.value.id,
|
||||||
});
|
});
|
||||||
connection.value.on('message', onMessage);
|
connection.value.on('message', onMessage);
|
||||||
connection.value.on('deleted', onDeleted);
|
connection.value.on('deleted', onDeleted);
|
||||||
@@ -145,21 +149,25 @@ onDeactivated(() => {
|
|||||||
isActivated = false;
|
isActivated = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
function fetchMore() {
|
async function fetchMore() {
|
||||||
const LIMIT = 30;
|
const LIMIT = 30;
|
||||||
|
|
||||||
moreFetching.value = true;
|
moreFetching.value = true;
|
||||||
|
|
||||||
misskeyApi('chat/messages/timeline', {
|
const newMessages = props.userId ? await misskeyApi('chat/messages/user-timeline', {
|
||||||
userId: user.value.id,
|
userId: user.value.id,
|
||||||
limit: LIMIT,
|
limit: LIMIT,
|
||||||
untilId: messages.value[messages.value.length - 1].id,
|
untilId: messages.value[messages.value.length - 1].id,
|
||||||
}).then(newMessages => {
|
}) : await misskeyApi('chat/messages/room-timeline', {
|
||||||
|
roomId: room.value.id,
|
||||||
|
limit: LIMIT,
|
||||||
|
untilId: messages.value[messages.value.length - 1].id,
|
||||||
|
});
|
||||||
|
|
||||||
messages.value.push(...newMessages);
|
messages.value.push(...newMessages);
|
||||||
|
|
||||||
canFetchMore.value = newMessages.length === LIMIT;
|
canFetchMore.value = newMessages.length === LIMIT;
|
||||||
moreFetching.value = false;
|
moreFetching.value = false;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onMessage(message: Misskey.entities.ChatMessage) {
|
function onMessage(message: Misskey.entities.ChatMessage) {
|
||||||
@@ -208,6 +216,37 @@ onBeforeUnmount(() => {
|
|||||||
window.document.removeEventListener('visibilitychange', onVisibilitychange);
|
window.document.removeEventListener('visibilitychange', onVisibilitychange);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function inviteUser() {
|
||||||
|
const invitee = await os.selectUser({ includeSelf: false, localOnly: true });
|
||||||
|
os.apiWithDialog('chat/rooms/invitations/create', {
|
||||||
|
roomId: room.value?.id,
|
||||||
|
userId: invitee.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showMenu(ev: MouseEvent) {
|
||||||
|
const menuItems: MenuItem[] = [];
|
||||||
|
|
||||||
|
if (room.value) {
|
||||||
|
if (room.value.ownerId === $i.id) {
|
||||||
|
menuItems.push({
|
||||||
|
text: i18n.ts._chat.inviteUser,
|
||||||
|
icon: 'ti ti-user-plus',
|
||||||
|
action: () => {
|
||||||
|
inviteUser();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.popupMenu(menuItems, ev.currentTarget ?? ev.target);
|
||||||
|
}
|
||||||
|
|
||||||
|
const headerActions = computed(() => [{
|
||||||
|
icon: 'ti ti-dots',
|
||||||
|
handler: showMenu,
|
||||||
|
}]);
|
||||||
|
|
||||||
definePage(computed(() => !initializing.value ? user.value ? {
|
definePage(computed(() => !initializing.value ? user.value ? {
|
||||||
userName: user,
|
userName: user,
|
||||||
avatar: user,
|
avatar: user,
|
||||||
|
@@ -960,10 +960,16 @@ type ChatMessage = components['schemas']['ChatMessage'];
|
|||||||
type ChatMessageLite = components['schemas']['ChatMessageLite'];
|
type ChatMessageLite = components['schemas']['ChatMessageLite'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatMessagesCreateRequest = operations['chat___messages___create']['requestBody']['content']['application/json'];
|
type ChatMessagesCreateToRoomRequest = operations['chat___messages___create-to-room']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatMessagesCreateResponse = operations['chat___messages___create']['responses']['200']['content']['application/json'];
|
type ChatMessagesCreateToRoomResponse = operations['chat___messages___create-to-room']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type ChatMessagesCreateToUserRequest = operations['chat___messages___create-to-user']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-user']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
|
type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
|
||||||
@@ -971,6 +977,12 @@ type ChatMessagesDeleteRequest = operations['chat___messages___delete']['request
|
|||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
|
type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type ChatMessagesRoomTimelineResponse = operations['chat___messages___room-timeline']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json'];
|
type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
@@ -978,10 +990,10 @@ type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody
|
|||||||
type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json'];
|
type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json'];
|
type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatMessagesTimelineResponse = operations['chat___messages___timeline']['responses']['200']['content']['application/json'];
|
type ChatMessagesUserTimelineResponse = operations['chat___messages___user-timeline']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatRoom = components['schemas']['ChatRoom'];
|
type ChatRoom = components['schemas']['ChatRoom'];
|
||||||
@@ -1010,18 +1022,18 @@ type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations_
|
|||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json'];
|
type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
|
// @public (undocumented)
|
||||||
|
type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json'];
|
type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json'];
|
type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json'];
|
||||||
|
|
||||||
// @public (undocumented)
|
|
||||||
type ChatRoomsInvitationsRejectRequest = operations['chat___rooms___invitations___reject']['requestBody']['content']['application/json'];
|
|
||||||
|
|
||||||
// @public (undocumented)
|
|
||||||
type ChatRoomsInvitationsRejectResponse = operations['chat___rooms___invitations___reject']['responses']['200']['content']['application/json'];
|
|
||||||
|
|
||||||
// @public (undocumented)
|
// @public (undocumented)
|
||||||
type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json'];
|
type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json'];
|
||||||
|
|
||||||
@@ -1557,24 +1569,28 @@ declare namespace entities {
|
|||||||
ChartsUsersResponse,
|
ChartsUsersResponse,
|
||||||
ChatHistoryRequest,
|
ChatHistoryRequest,
|
||||||
ChatHistoryResponse,
|
ChatHistoryResponse,
|
||||||
ChatMessagesCreateRequest,
|
ChatMessagesCreateToRoomRequest,
|
||||||
ChatMessagesCreateResponse,
|
ChatMessagesCreateToRoomResponse,
|
||||||
|
ChatMessagesCreateToUserRequest,
|
||||||
|
ChatMessagesCreateToUserResponse,
|
||||||
ChatMessagesDeleteRequest,
|
ChatMessagesDeleteRequest,
|
||||||
ChatMessagesDeleteResponse,
|
ChatMessagesDeleteResponse,
|
||||||
|
ChatMessagesRoomTimelineRequest,
|
||||||
|
ChatMessagesRoomTimelineResponse,
|
||||||
ChatMessagesShowRequest,
|
ChatMessagesShowRequest,
|
||||||
ChatMessagesShowResponse,
|
ChatMessagesShowResponse,
|
||||||
ChatMessagesTimelineRequest,
|
ChatMessagesUserTimelineRequest,
|
||||||
ChatMessagesTimelineResponse,
|
ChatMessagesUserTimelineResponse,
|
||||||
ChatRoomsCreateRequest,
|
ChatRoomsCreateRequest,
|
||||||
ChatRoomsCreateResponse,
|
ChatRoomsCreateResponse,
|
||||||
ChatRoomsDeleteRequest,
|
ChatRoomsDeleteRequest,
|
||||||
ChatRoomsDeleteResponse,
|
ChatRoomsDeleteResponse,
|
||||||
ChatRoomsInvitationsCreateRequest,
|
ChatRoomsInvitationsCreateRequest,
|
||||||
ChatRoomsInvitationsCreateResponse,
|
ChatRoomsInvitationsCreateResponse,
|
||||||
|
ChatRoomsInvitationsIgnoreRequest,
|
||||||
|
ChatRoomsInvitationsIgnoreResponse,
|
||||||
ChatRoomsInvitationsInboxRequest,
|
ChatRoomsInvitationsInboxRequest,
|
||||||
ChatRoomsInvitationsInboxResponse,
|
ChatRoomsInvitationsInboxResponse,
|
||||||
ChatRoomsInvitationsRejectRequest,
|
|
||||||
ChatRoomsInvitationsRejectResponse,
|
|
||||||
ChatRoomsJoinRequest,
|
ChatRoomsJoinRequest,
|
||||||
ChatRoomsJoinResponse,
|
ChatRoomsJoinResponse,
|
||||||
ChatRoomsLeaveRequest,
|
ChatRoomsLeaveRequest,
|
||||||
|
@@ -1550,7 +1550,18 @@ declare module '../api.js' {
|
|||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
*/
|
*/
|
||||||
request<E extends 'chat/messages/create', P extends Endpoints[E]['req']>(
|
request<E extends 'chat/messages/create-to-room', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
|
*/
|
||||||
|
request<E extends 'chat/messages/create-to-user', P extends Endpoints[E]['req']>(
|
||||||
endpoint: E,
|
endpoint: E,
|
||||||
params: P,
|
params: P,
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
@@ -1567,6 +1578,17 @@ declare module '../api.js' {
|
|||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
): Promise<SwitchCaseResponseType<E, P>>;
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
|
*/
|
||||||
|
request<E extends 'chat/messages/room-timeline', P extends Endpoints[E]['req']>(
|
||||||
|
endpoint: E,
|
||||||
|
params: P,
|
||||||
|
credential?: string | null,
|
||||||
|
): Promise<SwitchCaseResponseType<E, P>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* No description provided.
|
* No description provided.
|
||||||
*
|
*
|
||||||
@@ -1583,7 +1605,7 @@ declare module '../api.js' {
|
|||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
*/
|
*/
|
||||||
request<E extends 'chat/messages/timeline', P extends Endpoints[E]['req']>(
|
request<E extends 'chat/messages/user-timeline', P extends Endpoints[E]['req']>(
|
||||||
endpoint: E,
|
endpoint: E,
|
||||||
params: P,
|
params: P,
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
@@ -1625,9 +1647,9 @@ declare module '../api.js' {
|
|||||||
/**
|
/**
|
||||||
* No description provided.
|
* No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
*/
|
*/
|
||||||
request<E extends 'chat/rooms/invitations/inbox', P extends Endpoints[E]['req']>(
|
request<E extends 'chat/rooms/invitations/ignore', P extends Endpoints[E]['req']>(
|
||||||
endpoint: E,
|
endpoint: E,
|
||||||
params: P,
|
params: P,
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
@@ -1636,9 +1658,9 @@ declare module '../api.js' {
|
|||||||
/**
|
/**
|
||||||
* No description provided.
|
* No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
*/
|
*/
|
||||||
request<E extends 'chat/rooms/invitations/reject', P extends Endpoints[E]['req']>(
|
request<E extends 'chat/rooms/invitations/inbox', P extends Endpoints[E]['req']>(
|
||||||
endpoint: E,
|
endpoint: E,
|
||||||
params: P,
|
params: P,
|
||||||
credential?: string | null,
|
credential?: string | null,
|
||||||
|
@@ -209,24 +209,28 @@ import type {
|
|||||||
ChartsUsersResponse,
|
ChartsUsersResponse,
|
||||||
ChatHistoryRequest,
|
ChatHistoryRequest,
|
||||||
ChatHistoryResponse,
|
ChatHistoryResponse,
|
||||||
ChatMessagesCreateRequest,
|
ChatMessagesCreateToRoomRequest,
|
||||||
ChatMessagesCreateResponse,
|
ChatMessagesCreateToRoomResponse,
|
||||||
|
ChatMessagesCreateToUserRequest,
|
||||||
|
ChatMessagesCreateToUserResponse,
|
||||||
ChatMessagesDeleteRequest,
|
ChatMessagesDeleteRequest,
|
||||||
ChatMessagesDeleteResponse,
|
ChatMessagesDeleteResponse,
|
||||||
|
ChatMessagesRoomTimelineRequest,
|
||||||
|
ChatMessagesRoomTimelineResponse,
|
||||||
ChatMessagesShowRequest,
|
ChatMessagesShowRequest,
|
||||||
ChatMessagesShowResponse,
|
ChatMessagesShowResponse,
|
||||||
ChatMessagesTimelineRequest,
|
ChatMessagesUserTimelineRequest,
|
||||||
ChatMessagesTimelineResponse,
|
ChatMessagesUserTimelineResponse,
|
||||||
ChatRoomsCreateRequest,
|
ChatRoomsCreateRequest,
|
||||||
ChatRoomsCreateResponse,
|
ChatRoomsCreateResponse,
|
||||||
ChatRoomsDeleteRequest,
|
ChatRoomsDeleteRequest,
|
||||||
ChatRoomsDeleteResponse,
|
ChatRoomsDeleteResponse,
|
||||||
ChatRoomsInvitationsCreateRequest,
|
ChatRoomsInvitationsCreateRequest,
|
||||||
ChatRoomsInvitationsCreateResponse,
|
ChatRoomsInvitationsCreateResponse,
|
||||||
|
ChatRoomsInvitationsIgnoreRequest,
|
||||||
|
ChatRoomsInvitationsIgnoreResponse,
|
||||||
ChatRoomsInvitationsInboxRequest,
|
ChatRoomsInvitationsInboxRequest,
|
||||||
ChatRoomsInvitationsInboxResponse,
|
ChatRoomsInvitationsInboxResponse,
|
||||||
ChatRoomsInvitationsRejectRequest,
|
|
||||||
ChatRoomsInvitationsRejectResponse,
|
|
||||||
ChatRoomsJoinRequest,
|
ChatRoomsJoinRequest,
|
||||||
ChatRoomsJoinResponse,
|
ChatRoomsJoinResponse,
|
||||||
ChatRoomsLeaveRequest,
|
ChatRoomsLeaveRequest,
|
||||||
@@ -759,15 +763,17 @@ export type Endpoints = {
|
|||||||
'charts/user/reactions': { req: ChartsUserReactionsRequest; res: ChartsUserReactionsResponse };
|
'charts/user/reactions': { req: ChartsUserReactionsRequest; res: ChartsUserReactionsResponse };
|
||||||
'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse };
|
'charts/users': { req: ChartsUsersRequest; res: ChartsUsersResponse };
|
||||||
'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse };
|
'chat/history': { req: ChatHistoryRequest; res: ChatHistoryResponse };
|
||||||
'chat/messages/create': { req: ChatMessagesCreateRequest; res: ChatMessagesCreateResponse };
|
'chat/messages/create-to-room': { req: ChatMessagesCreateToRoomRequest; res: ChatMessagesCreateToRoomResponse };
|
||||||
|
'chat/messages/create-to-user': { req: ChatMessagesCreateToUserRequest; res: ChatMessagesCreateToUserResponse };
|
||||||
'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse };
|
'chat/messages/delete': { req: ChatMessagesDeleteRequest; res: ChatMessagesDeleteResponse };
|
||||||
|
'chat/messages/room-timeline': { req: ChatMessagesRoomTimelineRequest; res: ChatMessagesRoomTimelineResponse };
|
||||||
'chat/messages/show': { req: ChatMessagesShowRequest; res: ChatMessagesShowResponse };
|
'chat/messages/show': { req: ChatMessagesShowRequest; res: ChatMessagesShowResponse };
|
||||||
'chat/messages/timeline': { req: ChatMessagesTimelineRequest; res: ChatMessagesTimelineResponse };
|
'chat/messages/user-timeline': { req: ChatMessagesUserTimelineRequest; res: ChatMessagesUserTimelineResponse };
|
||||||
'chat/rooms/create': { req: ChatRoomsCreateRequest; res: ChatRoomsCreateResponse };
|
'chat/rooms/create': { req: ChatRoomsCreateRequest; res: ChatRoomsCreateResponse };
|
||||||
'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: ChatRoomsDeleteResponse };
|
'chat/rooms/delete': { req: ChatRoomsDeleteRequest; res: ChatRoomsDeleteResponse };
|
||||||
'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse };
|
'chat/rooms/invitations/create': { req: ChatRoomsInvitationsCreateRequest; res: ChatRoomsInvitationsCreateResponse };
|
||||||
|
'chat/rooms/invitations/ignore': { req: ChatRoomsInvitationsIgnoreRequest; res: ChatRoomsInvitationsIgnoreResponse };
|
||||||
'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse };
|
'chat/rooms/invitations/inbox': { req: ChatRoomsInvitationsInboxRequest; res: ChatRoomsInvitationsInboxResponse };
|
||||||
'chat/rooms/invitations/reject': { req: ChatRoomsInvitationsRejectRequest; res: ChatRoomsInvitationsRejectResponse };
|
|
||||||
'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse };
|
'chat/rooms/join': { req: ChatRoomsJoinRequest; res: ChatRoomsJoinResponse };
|
||||||
'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse };
|
'chat/rooms/leave': { req: ChatRoomsLeaveRequest; res: ChatRoomsLeaveResponse };
|
||||||
'chat/rooms/members': { req: ChatRoomsMembersRequest; res: ChatRoomsMembersResponse };
|
'chat/rooms/members': { req: ChatRoomsMembersRequest; res: ChatRoomsMembersResponse };
|
||||||
|
@@ -212,24 +212,28 @@ export type ChartsUsersRequest = operations['charts___users']['requestBody']['co
|
|||||||
export type ChartsUsersResponse = operations['charts___users']['responses']['200']['content']['application/json'];
|
export type ChartsUsersResponse = operations['charts___users']['responses']['200']['content']['application/json'];
|
||||||
export type ChatHistoryRequest = operations['chat___history']['requestBody']['content']['application/json'];
|
export type ChatHistoryRequest = operations['chat___history']['requestBody']['content']['application/json'];
|
||||||
export type ChatHistoryResponse = operations['chat___history']['responses']['200']['content']['application/json'];
|
export type ChatHistoryResponse = operations['chat___history']['responses']['200']['content']['application/json'];
|
||||||
export type ChatMessagesCreateRequest = operations['chat___messages___create']['requestBody']['content']['application/json'];
|
export type ChatMessagesCreateToRoomRequest = operations['chat___messages___create-to-room']['requestBody']['content']['application/json'];
|
||||||
export type ChatMessagesCreateResponse = operations['chat___messages___create']['responses']['200']['content']['application/json'];
|
export type ChatMessagesCreateToRoomResponse = operations['chat___messages___create-to-room']['responses']['200']['content']['application/json'];
|
||||||
|
export type ChatMessagesCreateToUserRequest = operations['chat___messages___create-to-user']['requestBody']['content']['application/json'];
|
||||||
|
export type ChatMessagesCreateToUserResponse = operations['chat___messages___create-to-user']['responses']['200']['content']['application/json'];
|
||||||
export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
|
export type ChatMessagesDeleteRequest = operations['chat___messages___delete']['requestBody']['content']['application/json'];
|
||||||
export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
|
export type ChatMessagesDeleteResponse = operations['chat___messages___delete']['responses']['200']['content']['application/json'];
|
||||||
|
export type ChatMessagesRoomTimelineRequest = operations['chat___messages___room-timeline']['requestBody']['content']['application/json'];
|
||||||
|
export type ChatMessagesRoomTimelineResponse = operations['chat___messages___room-timeline']['responses']['200']['content']['application/json'];
|
||||||
export type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json'];
|
export type ChatMessagesShowRequest = operations['chat___messages___show']['requestBody']['content']['application/json'];
|
||||||
export type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json'];
|
export type ChatMessagesShowResponse = operations['chat___messages___show']['responses']['200']['content']['application/json'];
|
||||||
export type ChatMessagesTimelineRequest = operations['chat___messages___timeline']['requestBody']['content']['application/json'];
|
export type ChatMessagesUserTimelineRequest = operations['chat___messages___user-timeline']['requestBody']['content']['application/json'];
|
||||||
export type ChatMessagesTimelineResponse = operations['chat___messages___timeline']['responses']['200']['content']['application/json'];
|
export type ChatMessagesUserTimelineResponse = operations['chat___messages___user-timeline']['responses']['200']['content']['application/json'];
|
||||||
export type ChatRoomsCreateRequest = operations['chat___rooms___create']['requestBody']['content']['application/json'];
|
export type ChatRoomsCreateRequest = operations['chat___rooms___create']['requestBody']['content']['application/json'];
|
||||||
export type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses']['200']['content']['application/json'];
|
export type ChatRoomsCreateResponse = operations['chat___rooms___create']['responses']['200']['content']['application/json'];
|
||||||
export type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json'];
|
export type ChatRoomsDeleteRequest = operations['chat___rooms___delete']['requestBody']['content']['application/json'];
|
||||||
export type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json'];
|
export type ChatRoomsDeleteResponse = operations['chat___rooms___delete']['responses']['200']['content']['application/json'];
|
||||||
export type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json'];
|
export type ChatRoomsInvitationsCreateRequest = operations['chat___rooms___invitations___create']['requestBody']['content']['application/json'];
|
||||||
export type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json'];
|
export type ChatRoomsInvitationsCreateResponse = operations['chat___rooms___invitations___create']['responses']['200']['content']['application/json'];
|
||||||
|
export type ChatRoomsInvitationsIgnoreRequest = operations['chat___rooms___invitations___ignore']['requestBody']['content']['application/json'];
|
||||||
|
export type ChatRoomsInvitationsIgnoreResponse = operations['chat___rooms___invitations___ignore']['responses']['200']['content']['application/json'];
|
||||||
export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json'];
|
export type ChatRoomsInvitationsInboxRequest = operations['chat___rooms___invitations___inbox']['requestBody']['content']['application/json'];
|
||||||
export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json'];
|
export type ChatRoomsInvitationsInboxResponse = operations['chat___rooms___invitations___inbox']['responses']['200']['content']['application/json'];
|
||||||
export type ChatRoomsInvitationsRejectRequest = operations['chat___rooms___invitations___reject']['requestBody']['content']['application/json'];
|
|
||||||
export type ChatRoomsInvitationsRejectResponse = operations['chat___rooms___invitations___reject']['responses']['200']['content']['application/json'];
|
|
||||||
export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json'];
|
export type ChatRoomsJoinRequest = operations['chat___rooms___join']['requestBody']['content']['application/json'];
|
||||||
export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json'];
|
export type ChatRoomsJoinResponse = operations['chat___rooms___join']['responses']['200']['content']['application/json'];
|
||||||
export type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json'];
|
export type ChatRoomsLeaveRequest = operations['chat___rooms___leave']['requestBody']['content']['application/json'];
|
||||||
|
@@ -1367,14 +1367,23 @@ export type paths = {
|
|||||||
*/
|
*/
|
||||||
post: operations['chat___history'];
|
post: operations['chat___history'];
|
||||||
};
|
};
|
||||||
'/chat/messages/create': {
|
'/chat/messages/create-to-room': {
|
||||||
/**
|
/**
|
||||||
* chat/messages/create
|
* chat/messages/create-to-room
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
*/
|
*/
|
||||||
post: operations['chat___messages___create'];
|
post: operations['chat___messages___create-to-room'];
|
||||||
|
};
|
||||||
|
'/chat/messages/create-to-user': {
|
||||||
|
/**
|
||||||
|
* chat/messages/create-to-user
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
|
*/
|
||||||
|
post: operations['chat___messages___create-to-user'];
|
||||||
};
|
};
|
||||||
'/chat/messages/delete': {
|
'/chat/messages/delete': {
|
||||||
/**
|
/**
|
||||||
@@ -1385,6 +1394,15 @@ export type paths = {
|
|||||||
*/
|
*/
|
||||||
post: operations['chat___messages___delete'];
|
post: operations['chat___messages___delete'];
|
||||||
};
|
};
|
||||||
|
'/chat/messages/room-timeline': {
|
||||||
|
/**
|
||||||
|
* chat/messages/room-timeline
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
|
*/
|
||||||
|
post: operations['chat___messages___room-timeline'];
|
||||||
|
};
|
||||||
'/chat/messages/show': {
|
'/chat/messages/show': {
|
||||||
/**
|
/**
|
||||||
* chat/messages/show
|
* chat/messages/show
|
||||||
@@ -1394,14 +1412,14 @@ export type paths = {
|
|||||||
*/
|
*/
|
||||||
post: operations['chat___messages___show'];
|
post: operations['chat___messages___show'];
|
||||||
};
|
};
|
||||||
'/chat/messages/timeline': {
|
'/chat/messages/user-timeline': {
|
||||||
/**
|
/**
|
||||||
* chat/messages/timeline
|
* chat/messages/user-timeline
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
*/
|
*/
|
||||||
post: operations['chat___messages___timeline'];
|
post: operations['chat___messages___user-timeline'];
|
||||||
};
|
};
|
||||||
'/chat/rooms/create': {
|
'/chat/rooms/create': {
|
||||||
/**
|
/**
|
||||||
@@ -1430,6 +1448,15 @@ export type paths = {
|
|||||||
*/
|
*/
|
||||||
post: operations['chat___rooms___invitations___create'];
|
post: operations['chat___rooms___invitations___create'];
|
||||||
};
|
};
|
||||||
|
'/chat/rooms/invitations/ignore': {
|
||||||
|
/**
|
||||||
|
* chat/rooms/invitations/ignore
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
|
*/
|
||||||
|
post: operations['chat___rooms___invitations___ignore'];
|
||||||
|
};
|
||||||
'/chat/rooms/invitations/inbox': {
|
'/chat/rooms/invitations/inbox': {
|
||||||
/**
|
/**
|
||||||
* chat/rooms/invitations/inbox
|
* chat/rooms/invitations/inbox
|
||||||
@@ -1439,15 +1466,6 @@ export type paths = {
|
|||||||
*/
|
*/
|
||||||
post: operations['chat___rooms___invitations___inbox'];
|
post: operations['chat___rooms___invitations___inbox'];
|
||||||
};
|
};
|
||||||
'/chat/rooms/invitations/reject': {
|
|
||||||
/**
|
|
||||||
* chat/rooms/invitations/reject
|
|
||||||
* @description No description provided.
|
|
||||||
*
|
|
||||||
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
|
||||||
*/
|
|
||||||
post: operations['chat___rooms___invitations___reject'];
|
|
||||||
};
|
|
||||||
'/chat/rooms/join': {
|
'/chat/rooms/join': {
|
||||||
/**
|
/**
|
||||||
* chat/rooms/join
|
* chat/rooms/join
|
||||||
@@ -13915,12 +13933,12 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* chat/messages/create
|
* chat/messages/create-to-room
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
*/
|
*/
|
||||||
chat___messages___create: {
|
'chat___messages___create-to-room': {
|
||||||
requestBody: {
|
requestBody: {
|
||||||
content: {
|
content: {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
@@ -13928,7 +13946,70 @@ export type operations = {
|
|||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
fileId?: string;
|
fileId?: string;
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
toUserId?: string | null;
|
toRoomId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (with results) */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['ChatMessageLite'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Too many requests */
|
||||||
|
429: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* chat/messages/create-to-user
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
|
*/
|
||||||
|
'chat___messages___create-to-user': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
text?: string | null;
|
||||||
|
/** Format: misskey:id */
|
||||||
|
fileId?: string;
|
||||||
|
/** Format: misskey:id */
|
||||||
|
toUserId: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -14031,6 +14112,66 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* chat/messages/room-timeline
|
||||||
|
* @description No description provided.
|
||||||
|
*
|
||||||
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
|
*/
|
||||||
|
'chat___messages___room-timeline': {
|
||||||
|
requestBody: {
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
/** @default 10 */
|
||||||
|
limit?: number;
|
||||||
|
/** Format: misskey:id */
|
||||||
|
sinceId?: string;
|
||||||
|
/** Format: misskey:id */
|
||||||
|
untilId?: string;
|
||||||
|
/** Format: misskey:id */
|
||||||
|
roomId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
responses: {
|
||||||
|
/** @description OK (with results) */
|
||||||
|
200: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['ChatMessageLite'][];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Client error */
|
||||||
|
400: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Authentication error */
|
||||||
|
401: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Forbidden error */
|
||||||
|
403: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description I'm Ai */
|
||||||
|
418: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Internal server error */
|
||||||
|
500: {
|
||||||
|
content: {
|
||||||
|
'application/json': components['schemas']['Error'];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* chat/messages/show
|
* chat/messages/show
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
@@ -14086,12 +14227,12 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* chat/messages/timeline
|
* chat/messages/user-timeline
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
*/
|
*/
|
||||||
chat___messages___timeline: {
|
'chat___messages___user-timeline': {
|
||||||
requestBody: {
|
requestBody: {
|
||||||
content: {
|
content: {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
@@ -14102,7 +14243,7 @@ export type operations = {
|
|||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
untilId?: string;
|
untilId?: string;
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
userId?: string | null;
|
userId: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -14321,21 +14462,17 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* chat/rooms/invitations/inbox
|
* chat/rooms/invitations/ignore
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
||||||
*/
|
*/
|
||||||
chat___rooms___invitations___inbox: {
|
chat___rooms___invitations___ignore: {
|
||||||
requestBody: {
|
requestBody: {
|
||||||
content: {
|
content: {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
/** @default 30 */
|
|
||||||
limit?: number;
|
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
sinceId?: string;
|
roomId: string;
|
||||||
/** Format: misskey:id */
|
|
||||||
untilId?: string;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -14343,7 +14480,7 @@ export type operations = {
|
|||||||
/** @description OK (with results) */
|
/** @description OK (with results) */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
content: {
|
||||||
'application/json': components['schemas']['ChatRoomInvitation'][];
|
'application/json': unknown;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** @description Client error */
|
/** @description Client error */
|
||||||
@@ -14379,17 +14516,21 @@ export type operations = {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* chat/rooms/invitations/reject
|
* chat/rooms/invitations/inbox
|
||||||
* @description No description provided.
|
* @description No description provided.
|
||||||
*
|
*
|
||||||
* **Credential required**: *Yes* / **Permission**: *write:chat*
|
* **Credential required**: *Yes* / **Permission**: *read:chat*
|
||||||
*/
|
*/
|
||||||
chat___rooms___invitations___reject: {
|
chat___rooms___invitations___inbox: {
|
||||||
requestBody: {
|
requestBody: {
|
||||||
content: {
|
content: {
|
||||||
'application/json': {
|
'application/json': {
|
||||||
|
/** @default 30 */
|
||||||
|
limit?: number;
|
||||||
/** Format: misskey:id */
|
/** Format: misskey:id */
|
||||||
roomId: string;
|
sinceId?: string;
|
||||||
|
/** Format: misskey:id */
|
||||||
|
untilId?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -14397,7 +14538,7 @@ export type operations = {
|
|||||||
/** @description OK (with results) */
|
/** @description OK (with results) */
|
||||||
200: {
|
200: {
|
||||||
content: {
|
content: {
|
||||||
'application/json': unknown;
|
'application/json': components['schemas']['ChatRoomInvitation'][];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
/** @description Client error */
|
/** @description Client error */
|
||||||
|
Reference in New Issue
Block a user