Editing and error correction (in progress)
This commit is contained in:
@@ -14,6 +14,7 @@ import {
|
||||
AddConversationRoomMemberDto,
|
||||
RemoveConversationRoomMemberDto,
|
||||
UpdateConversationRoomMemberRoleDto,
|
||||
ConversationTypingDto,
|
||||
} from './dto';
|
||||
import { ConversationsService } from './conversations.service';
|
||||
|
||||
@@ -43,6 +44,32 @@ export class ConversationsController {
|
||||
return this.conversations.listRooms(user.userId, user.organizationId, user.role);
|
||||
}
|
||||
|
||||
@Post('rooms/typing')
|
||||
signalTyping(
|
||||
@CurrentUser() user: { userId: string; organizationId: string; role: string },
|
||||
@Body() dto: ConversationTypingDto,
|
||||
) {
|
||||
return this.conversations.signalTyping(
|
||||
user.userId,
|
||||
user.organizationId,
|
||||
user.role,
|
||||
dto.externalRoomId,
|
||||
);
|
||||
}
|
||||
|
||||
@Get('rooms/typing')
|
||||
listTyping(
|
||||
@CurrentUser() user: { userId: string; organizationId: string; role: string },
|
||||
@Query('externalRoomId') externalRoomId: string,
|
||||
) {
|
||||
return this.conversations.listTyping(
|
||||
user.userId,
|
||||
user.organizationId,
|
||||
user.role,
|
||||
externalRoomId || '',
|
||||
);
|
||||
}
|
||||
|
||||
@Post('rooms/dm')
|
||||
createDm(
|
||||
@CurrentUser() user: { userId: string; organizationId: string; role: string },
|
||||
|
||||
@@ -26,12 +26,78 @@ import {
|
||||
|
||||
@Injectable()
|
||||
export class ConversationsService {
|
||||
/** presence: org|room|userId → epoch ms (بدون DB؛ کافی برای typing کوتاهعمر) */
|
||||
private readonly typingPresence = new Map<string, number>();
|
||||
private typingSweepAt = 0;
|
||||
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly crypto: TahesabCryptoService,
|
||||
private readonly chat: ChatExternalClient,
|
||||
) {}
|
||||
|
||||
private typingKey(organizationId: string, roomId: string, userId: string) {
|
||||
return `${organizationId}|${roomId}|${userId}`;
|
||||
}
|
||||
|
||||
private sweepTypingPresence(now = Date.now()) {
|
||||
if (now - this.typingSweepAt < 10_000) return;
|
||||
this.typingSweepAt = now;
|
||||
for (const [k, at] of this.typingPresence) {
|
||||
if (now - at > 8000) this.typingPresence.delete(k);
|
||||
}
|
||||
}
|
||||
|
||||
async signalTyping(
|
||||
userId: string,
|
||||
organizationId: string,
|
||||
role: string,
|
||||
externalRoomId: string,
|
||||
): Promise<{ ok: true }> {
|
||||
await this.loadAccess(userId, organizationId, role);
|
||||
const roomId = externalRoomId.trim();
|
||||
if (!roomId) throw new BadRequestException('شناسه اتاق خالی است');
|
||||
const now = Date.now();
|
||||
this.typingPresence.set(this.typingKey(organizationId, roomId, userId), now);
|
||||
this.sweepTypingPresence(now);
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
async listTyping(
|
||||
userId: string,
|
||||
organizationId: string,
|
||||
role: string,
|
||||
externalRoomId: string,
|
||||
): Promise<Array<{ userId: string; fullName: string; chatUserId: string }>> {
|
||||
await this.loadAccess(userId, organizationId, role);
|
||||
const roomId = externalRoomId.trim();
|
||||
if (!roomId) return [];
|
||||
const now = Date.now();
|
||||
this.sweepTypingPresence(now);
|
||||
const prefix = `${organizationId}|${roomId}|`;
|
||||
const ids: string[] = [];
|
||||
for (const [k, at] of this.typingPresence) {
|
||||
if (!k.startsWith(prefix)) continue;
|
||||
if (now - at > 4000) {
|
||||
this.typingPresence.delete(k);
|
||||
continue;
|
||||
}
|
||||
const uid = k.slice(prefix.length);
|
||||
if (!uid || uid === userId) continue;
|
||||
ids.push(uid);
|
||||
}
|
||||
if (!ids.length) return [];
|
||||
const users = await this.prisma.user.findMany({
|
||||
where: { id: { in: ids } },
|
||||
select: { id: true, fullName: true },
|
||||
});
|
||||
return users.map((u) => ({
|
||||
userId: u.id,
|
||||
fullName: u.fullName || 'کاربر',
|
||||
chatUserId: buildChatUserId(organizationId, u.id),
|
||||
}));
|
||||
}
|
||||
|
||||
async getAccess(userId: string, organizationId: string, role: string): Promise<ConversationAccessDto> {
|
||||
const access = await this.loadAccess(userId, organizationId, role);
|
||||
return access;
|
||||
|
||||
@@ -162,3 +162,10 @@ export class UpdateConversationRoomMemberRoleDto {
|
||||
@IsString()
|
||||
role!: 'admin' | 'member';
|
||||
}
|
||||
|
||||
export class ConversationTypingDto {
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(200)
|
||||
externalRoomId!: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user