import { Inject, Injectable, Logger } from '@nestjs/common' import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken } from '@smatch-corp/nestjs-pothos' import { Builder } from '../Graphql/graphql.builder' import { PrismaService } from '../Prisma/prisma.service' // import { LiveKitRoomService } from 'src/LiveKit/livekit.room.service' import { v4 as uuidv4 } from 'uuid' import { CollaborationSession, Role, ScheduleDateStatus } from '@prisma/client' import { DateTimeUtils } from 'src/common/utils/datetime.utils' @Injectable() export class CollaborationSessionSchema extends PothosSchema { constructor( @Inject(SchemaBuilderToken) private readonly builder: Builder, private readonly prisma: PrismaService, // private readonly liveKitRoomService: LiveKitRoomService, ) { super() } @PothosRef() collaborationSession() { return this.builder.prismaObject('CollaborationSession', { description: 'A collaboration session in the system.', fields: (t) => ({ id: t.exposeID('id', { description: 'The ID of the collaboration session.', }), chatRoomId: t.exposeString('chatRoomId', { description: 'The ID of the chat room.', }), chatRoom: t.relation('chatRoom', { description: 'The chat room.', }), collaboratorsIds: t.exposeStringList('collaboratorsIds', { description: 'The IDs of the collaborators.', }), meetingRoom: t.relation('meetingRoom', { description: 'The meeting room.', }), scheduleDate: t.relation('scheduleDate', { description: 'The schedule date.', }), activeDocumentId: t.exposeString('activeDocumentId', { description: 'The ID of the active document.', }), activeDocument: t.relation('activeDocument', { description: 'The active document.', }), createdAt: t.expose('createdAt', { type: 'DateTime', description: 'The creation date of the collaboration session.', }), updatedAt: t.expose('updatedAt', { type: 'DateTime', description: 'The update date of the collaboration session.', }), }), }) } @Pothos() init(): void { this.builder.queryFields((t) => ({ // get collaboration session by schedule date id, if not exist, create new one collaborationSession: t.prismaField({ type: this.collaborationSession(), args: { scheduleDateId: t.arg.string({ description: 'The ID of the schedule date.', required: true, }), }, description: 'Retrieve a single collaboration session by its unique identifier.', resolve: async (_query, _root, args, ctx, _info) => { if (ctx.isSubscription) throw new Error('Not allowed') if (!ctx.http.me) throw new Error('Cannot get your info') const scheduleDate = await this.prisma.scheduleDate.findUnique({ where: { id: args.scheduleDateId, }, }) if (!scheduleDate) throw new Error('Schedule date not found') let collaborationSession: CollaborationSession | null = null collaborationSession = await this.prisma.collaborationSession.findUnique({ where: { scheduleDateId: scheduleDate.id, }, }) /* ---------- use case 1 : customer get collaboration session by id --------- */ if (ctx.http.me?.role === Role.CUSTOMER && collaborationSession) { // if collaboratorsIds not include current user id, add it // if (!collaborationSession.collaboratorsIds.includes(ctx.http.me?.id)) { // collaborationSession.collaboratorsIds.push(ctx.http.me?.id) // await this.prisma.collaborationSession.update({ // where: { // id: collaborationSession.id, // }, // data: { // collaboratorsIds: collaborationSession.collaboratorsIds, // }, // }) // } // check if user is participant if ( !collaborationSession.collaboratorsIds.includes(ctx.http.me.id) || ctx.http.me.id === 'user_2nkDilSYEiljIraFGF9PENjILPr' ) throw new Error('User not allowed') return collaborationSession } /* ---------- use case 2 : center mentor get collaboration session by schedule date id --------- */ if (ctx.http.me.role !== Role.CENTER_MENTOR && ctx.http.me.role !== Role.CENTER_OWNER) { if (!collaborationSession) throw new Error('Mentor does not created collaboration session yet') throw new Error('User not allowed') } // check if user is participant if (!scheduleDate.participantIds.includes(ctx.http.me.id)) throw new Error('User not allowed') // check if order is exist in schedule date if (!scheduleDate.orderId) throw new Error('Order not found') const order = await this.prisma.order.findUnique({ where: { id: scheduleDate.orderId, }, }) if (!order) throw new Error('Order not found') if (!order.chatRoomId) throw new Error('Order chat room not found') // only in time before 10 minutes from start time or less and not after end time can create new collaboration session const now = DateTimeUtils.now() const startTime = DateTimeUtils.fromDate(scheduleDate.start) const endTime = DateTimeUtils.fromDate(scheduleDate.end) /* ----------------------- disabled in development mode ---------------------- */ // if ( // now.diff(startTime, 'minutes').minutes > 10 || // before start time 10 minutes // now > endTime // after end time // ) { // throw new Error('Collaboration session not allowed in this time') // } if (!collaborationSession) { const chatRoom = await this.prisma.chatRoom.findUnique({ where: { id: order.chatRoomId, }, }) if (!chatRoom) throw new Error('Chat room not found') // create new one const newCollaborationSession = await this.prisma.collaborationSession.create({ data: { scheduleDateId: scheduleDate.id, // assign chat room chatRoomId: chatRoom.id, collaboratorsIds: [ctx.http.me.id], }, }) // case after start time and before end time, mark as late if (now > startTime && now < endTime) { // mark as late await this.prisma.scheduleDate.update({ where: { id: scheduleDate.id, }, data: { lateStart: DateTimeUtils.now().toJSDate(), status: ScheduleDateStatus.IN_PROGRESS, }, }) } return newCollaborationSession // if not exist use case } return collaborationSession // if exist use case }, }), collaborationSessions: t.prismaField({ type: [this.collaborationSession()], args: this.builder.generator.findManyArgs('CollaborationSession'), description: 'Retrieve a list of collaboration sessions with optional filtering, ordering, and pagination.', resolve: async (query, _root, args, _ctx, _info) => { return await this.prisma.collaborationSession.findMany({ ...query, skip: args.skip ?? undefined, take: args.take ?? undefined, orderBy: args.orderBy ?? undefined, where: args.filter ?? undefined, }) }, }), })) this.builder.mutationFields((t) => ({ // update active document id updateActiveDocumentId: t.prismaField({ type: this.collaborationSession(), args: { activeDocumentId: t.arg.string({ description: 'The ID of the active document.', required: true, }), collaborationSessionId: t.arg.string({ description: 'The ID of the collaboration session.', required: true, }), }, description: 'Update the active document ID for a collaboration session.', resolve: async (_query, _root, args, ctx, _info) => { if (ctx.isSubscription) throw new Error('Not allowed') if (!ctx.http.me) throw new Error('Cannot get your info') // check permission const collaborationSession = await this.prisma.collaborationSession.findUnique({ where: { id: args.collaborationSessionId, }, include: { scheduleDate: true, }, }) if (!collaborationSession) throw new Error('Collaboration session not found') if (!collaborationSession.scheduleDate.participantIds.includes(ctx.http.me.id)) throw new Error('User not allowed') const updatedCollaborationSession = await this.prisma.collaborationSession.update({ where: { id: args.collaborationSessionId, }, data: { activeDocumentId: args.activeDocumentId, }, }) ctx.http.pubSub.publish(`collaborationSessionUpdated:${collaborationSession.id}`, updatedCollaborationSession) Logger.log(`Collaboration session updated: ${updatedCollaborationSession.id}`, 'updateActiveDocumentId') return updatedCollaborationSession }, }), })) this.builder.subscriptionFields((t) => ({ collaborationSessionUpdated: t.field({ type: this.collaborationSession(), description: 'Subscribe to collaboration session updates.', args: { collaborationSessionId: t.arg.string({ description: 'The ID of the collaboration session.', required: true, }), }, subscribe: async (_parent, args, ctx) => { if (!ctx.isSubscription) throw new Error('Not allowed') if (!ctx.websocket.me) throw new Error('Cannot get your info') const collaborationSession = await this.prisma.collaborationSession.findUnique({ where: { id: args.collaborationSessionId, }, }) if (!collaborationSession) throw new Error('Collaboration session not found') return ctx.websocket.pubSub.asyncIterator( `collaborationSessionUpdated:${collaborationSession.id}`, ) as unknown as AsyncIterable }, resolve: async (payload: CollaborationSession) => payload, }), })) } }