diff --git a/services/api/src/application/controllers/conversations/conversations.ts b/services/api/src/application/controllers/conversations/conversations.ts index 3bd2b9ec..2c15f9a9 100644 --- a/services/api/src/application/controllers/conversations/conversations.ts +++ b/services/api/src/application/controllers/conversations/conversations.ts @@ -19,16 +19,27 @@ export class ConversationController { } static async create (req: Request) { - const { body } = validate({ + const { body, tutorId } = validate({ body: Schema.string().min(1), + tutorId: Schema.string().min(1).nullable(), }, req.body) const authUserId = req.authUser!.id const user = await UsersUseCases.find(authUserId) if (!user || user.isDeleted()) throw new BadRequestError('user not found') - const title = await AI.summarizeForTitle(body) - const conversation = await ConversationsUseCases.add({ title, user: user.getEmbedded() }) + const tutor = tutorId ? await UsersUseCases.find(tutorId) : null + if (tutorId) { + if (!tutor || tutor.isDeleted()) throw new BadRequestError('tutor not found') + if (!tutor.canJoinConversations()) throw new BadRequestError('tutor can\'t join conversations right now') + } + + const title = tutorId ? body : await AI.summarizeForTitle(body) + const conversation = await ConversationsUseCases.add({ + title, user: user.getEmbedded(), + pending: !!tutorId, accepted: !tutorId ? { is: true, at: Date.now() } : null, + tutor: tutor?.getEmbedded() ?? null + }) await MessagesUseCases.add({ body, media: null, starred: false, conversationId: conversation.id, @@ -59,18 +70,22 @@ export class ConversationController { throw new NotAuthorizedError() } - static async removeTutor (req: Request) { + static async accept (req: Request) { + const { accept } = validate({ accept: Schema.boolean() }, req.body) + const isUpdated = await ConversationsUseCases.accept({ id: req.params.id, accept, tutorId: req.authUser!.id }) + if (isUpdated) return isUpdated + throw new NotAuthorizedError() + } + + static async end (req: Request) { const { rating, message } = validate({ rating: Schema.number().round(0).gte(0).lte(5), message: Schema.string() }, req.body) - const user = await UsersUseCases.find(req.authUser!.id) - if (!user || user.isDeleted()) throw new BadRequestError('profile not found') - - const updatedConversation = await ConversationsUseCases.removeTutor({ + const updatedConversation = await ConversationsUseCases.end({ rating, message, conversationId: req.params.id, - user: user.getEmbedded() + userId: req.authUser!.id }) if (updatedConversation) return updatedConversation diff --git a/services/api/src/application/controllers/conversations/tutorRequests.ts b/services/api/src/application/controllers/conversations/tutorRequests.ts deleted file mode 100644 index 99871ef6..00000000 --- a/services/api/src/application/controllers/conversations/tutorRequests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { TutorRequestsUseCases } from '@modules/conversations' -import { WalletsUseCases } from '@modules/payment' -import { UsersUseCases } from '@modules/users' -import { BadRequestError, NotAuthorizedError, QueryKeys, QueryParams, Request, Schema, validate } from 'equipped' - -export class TutorRequestController { - static async find (req: Request) { - const tutorRequest = await TutorRequestsUseCases.find(req.params.id) - if (!tutorRequest) return null - if (![tutorRequest.userId, tutorRequest.tutor.id].includes(req.authUser!.id)) return null - return tutorRequest - } - - static async get (req: Request) { - const query = req.query as QueryParams - query.authType = QueryKeys.or - query.auth = [{ field: 'userId', value: req.authUser!.id }, { field: 'tutor.id', value: req.authUser!.id }] - return await TutorRequestsUseCases.get(query) - } - - static async create (req: Request) { - const { conversationId, message, tutorId } = validate({ - conversationId: Schema.string().min(1), - message: Schema.string().min(1), - tutorId: Schema.string().min(1), - }, req.body) - - const tutor = await UsersUseCases.find(tutorId) - if (!tutor || tutor.isDeleted()) throw new BadRequestError('tutor not found') - if (!tutor.canJoinConversations()) throw new BadRequestError('tutor can\'t join conversations right now') - - const wallet = await WalletsUseCases.get(req.authUser!.id) - if (!wallet.canAddTutorToConversation()) throw new BadRequestError('you can\'t add a tutor to your conversations') - - return await TutorRequestsUseCases.create({ userId: req.authUser!.id, conversationId, message, tutor: tutor.getEmbedded() }) - } - - static async delete (req: Request) { - const isDeleted = await TutorRequestsUseCases.delete({ id: req.params.id, userId: req.authUser!.id }) - if (isDeleted) return isDeleted - throw new NotAuthorizedError() - } - - static async accept (req: Request) { - const { accept } = validate({ - accept: Schema.boolean() - }, req.body) - const isUpdated = await TutorRequestsUseCases.accept({ id: req.params.id, accept, tutorId: req.authUser!.id }) - if (isUpdated) return isUpdated - throw new NotAuthorizedError() - } -} \ No newline at end of file diff --git a/services/api/src/application/routes/conversations/conversations.ts b/services/api/src/application/routes/conversations/conversations.ts index d600a070..63c0ae70 100644 --- a/services/api/src/application/routes/conversations/conversations.ts +++ b/services/api/src/application/routes/conversations/conversations.ts @@ -64,14 +64,26 @@ export const conversationsRoutes = groupRoutes('/conversations', [ }) ] }, { - path: '/:id/tutor', - method: 'delete', + path: '/:id/accept', + method: 'post', + controllers: [ + isAuthenticated, + makeController(async (req) => { + return { + status: StatusCodes.Ok, + result: await ConversationController.accept(req) + } + }) + ] + }, { + path: '/:id/end', + method: 'post', controllers: [ isAuthenticated, makeController(async (req) => { return { status: StatusCodes.Ok, - result: await ConversationController.removeTutor(req) + result: await ConversationController.end(req) } }) ] diff --git a/services/api/src/application/routes/conversations/index.ts b/services/api/src/application/routes/conversations/index.ts index 661b6c2d..94a25aa8 100644 --- a/services/api/src/application/routes/conversations/index.ts +++ b/services/api/src/application/routes/conversations/index.ts @@ -1,10 +1,8 @@ import { groupRoutes } from 'equipped' import { conversationsRoutes } from './conversations' import { messagesRoutes } from './messages' -import { tutorRequestRoutes } from './tutorRequests' export const conversationRoutes = groupRoutes('/conversations', [ ...conversationsRoutes, ...messagesRoutes, - ...tutorRequestRoutes ]) \ No newline at end of file diff --git a/services/api/src/application/routes/conversations/tutorRequests.ts b/services/api/src/application/routes/conversations/tutorRequests.ts deleted file mode 100644 index 6c0d5d15..00000000 --- a/services/api/src/application/routes/conversations/tutorRequests.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { TutorRequestController } from '@application/controllers/conversations/tutorRequests' -import { isAuthenticated } from '@application/middlewares' -import { groupRoutes, makeController, StatusCodes } from 'equipped' - -export const tutorRequestRoutes = groupRoutes('/tutorRequests', [ - { - path: '/', - method: 'get', - controllers: [ - isAuthenticated, - makeController(async (req) => { - return { - status: StatusCodes.Ok, - result: await TutorRequestController.get(req) - } - }) - ] - }, { - path: '/:id', - method: 'get', - controllers: [ - isAuthenticated, - makeController(async (req) => { - return { - status: StatusCodes.Ok, - result: await TutorRequestController.find(req) - } - }) - ] - }, { - path: '/', - method: 'post', - controllers: [ - isAuthenticated, - makeController(async (req) => { - return { - status: StatusCodes.Ok, - result: await TutorRequestController.create(req) - } - }) - ] - }, { - path: '/:id/accept', - method: 'put', - controllers: [ - isAuthenticated, - makeController(async (req) => { - return { - status: StatusCodes.Ok, - result: await TutorRequestController.accept(req) - } - }) - ] - }, { - path: '/:id', - method: 'delete', - controllers: [ - isAuthenticated, - makeController(async (req) => { - return { - status: StatusCodes.Ok, - result: await TutorRequestController.delete(req) - } - }) - ] - } -]) \ No newline at end of file diff --git a/services/api/src/modules/conversations/data/mappers/conversations.ts b/services/api/src/modules/conversations/data/mappers/conversations.ts index 854e64d2..254863be 100644 --- a/services/api/src/modules/conversations/data/mappers/conversations.ts +++ b/services/api/src/modules/conversations/data/mappers/conversations.ts @@ -6,18 +6,21 @@ import { MessageMapper } from './messages' export class ConversationMapper extends BaseMapper { mapFrom (model: ConversationFromModel | null) { if (!model) return null - const { _id, title, user, tutor, createdAt, updatedAt, last, readAt } = model + const { _id, title, user, tutor, pending, accepted, ended, createdAt, updatedAt, last, readAt } = model const lastData = new MessageMapper().mapFrom(last) return new ConversationEntity({ id: _id.toString(), title, user, tutor, createdAt, updatedAt, - last: lastData, readAt + pending, accepted, ended, last: lastData, readAt }) } mapTo (entity: ConversationEntity) { return { title: entity.title, - user: entity.user + user: entity.user, + tutor: entity.tutor, + pending: entity.pending, + accepted: entity.accepted } } } \ No newline at end of file diff --git a/services/api/src/modules/conversations/data/mappers/tutorRequests.ts b/services/api/src/modules/conversations/data/mappers/tutorRequests.ts deleted file mode 100644 index b2c5c1a2..00000000 --- a/services/api/src/modules/conversations/data/mappers/tutorRequests.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { BaseMapper } from 'equipped' -import { TutorRequestEntity } from '../../domain/entities/tutorRequests' -import { TutorRequestFromModel, TutorRequestToModel } from '../models/tutorRequests' - -export class TutorRequestMapper extends BaseMapper { - mapFrom (param: TutorRequestFromModel | null) { - return !param ? null : new TutorRequestEntity({ - id: param._id.toString(), - tutor: param.tutor, - userId: param.userId, - conversationId: param.conversationId, - message: param.message, - pending: param.pending, - accepted: param.accepted, - createdAt: param.createdAt, - updatedAt: param.updatedAt - }) - } - - mapTo (param: TutorRequestEntity) { - return { - tutor: param.tutor, - userId: param.userId, - conversationId: param.conversationId, - message: param.message, - } - } -} \ No newline at end of file diff --git a/services/api/src/modules/conversations/data/models/conversations.ts b/services/api/src/modules/conversations/data/models/conversations.ts index a2c3761b..b066f9bc 100644 --- a/services/api/src/modules/conversations/data/models/conversations.ts +++ b/services/api/src/modules/conversations/data/models/conversations.ts @@ -3,7 +3,7 @@ import { MessageFromModel } from './messages' export interface ConversationFromModel extends ConversationToModel { _id: string - tutor: EmbeddedUser | null + ended: { rating: number, message: string, at: number } | null createdAt: number updatedAt: number readAt: Record @@ -13,4 +13,7 @@ export interface ConversationFromModel extends ConversationToModel { export interface ConversationToModel { title: string user: EmbeddedUser + tutor: EmbeddedUser | null + pending: boolean + accepted: { is: boolean, at: number } | null } \ No newline at end of file diff --git a/services/api/src/modules/conversations/data/models/tutorRequests.ts b/services/api/src/modules/conversations/data/models/tutorRequests.ts deleted file mode 100644 index 71ae1845..00000000 --- a/services/api/src/modules/conversations/data/models/tutorRequests.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { EmbeddedUser } from '../../domain/types' - -export interface TutorRequestFromModel extends TutorRequestToModel { - _id: string - pending: boolean - accepted: boolean - createdAt: number - updatedAt: number -} - -export interface TutorRequestToModel { - tutor: EmbeddedUser - conversationId: string - message: string - userId: string -} \ No newline at end of file diff --git a/services/api/src/modules/conversations/data/mongooseModels/conversations.ts b/services/api/src/modules/conversations/data/mongooseModels/conversations.ts index 1555986f..255cf474 100644 --- a/services/api/src/modules/conversations/data/mongooseModels/conversations.ts +++ b/services/api/src/modules/conversations/data/mongooseModels/conversations.ts @@ -22,6 +22,21 @@ const Schema = new appInstance.dbs.mongo.Schema({ required: false, default: null }, + pending: { + type: Boolean, + required: false, + default: true + }, + accepted: { + type: appInstance.dbs.mongo.Schema.Types.Mixed, + required: false, + default: null + }, + ended: { + type: appInstance.dbs.mongo.Schema.Types.Mixed, + required: false, + default: null + }, createdAt: { type: Number, required: false, diff --git a/services/api/src/modules/conversations/data/mongooseModels/tutorRequests.ts b/services/api/src/modules/conversations/data/mongooseModels/tutorRequests.ts deleted file mode 100644 index 3d642f61..00000000 --- a/services/api/src/modules/conversations/data/mongooseModels/tutorRequests.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { appInstance } from '@utils/types' -import { TutorRequestDbChangeCallbacks } from '../../utils/changes/tutorRequests' -import { TutorRequestMapper } from '../mappers/tutorRequests' -import { TutorRequestFromModel } from '../models/tutorRequests' - -const TutorRequestSchema = new appInstance.dbs.mongo.Schema({ - _id: { - type: String, - default: () => appInstance.dbs.mongo.Id.toString() - }, - tutor: { - type: appInstance.dbs.mongo.Schema.Types.Mixed, - required: true - }, - userId: { - type: String, - required: true - }, - conversationId: { - type: String, - required: true - }, - message: { - type: String, - required: false, - default: '' - }, - pending: { - type: Boolean, - required: false, - default: true - }, - accepted: { - type: Boolean, - required: false, - default: false - }, - createdAt: { - type: Number, - required: false, - default: Date.now - }, - updatedAt: { - type: Number, - required: false, - default: Date.now - } -}, { timestamps: { currentTime: Date.now }, minimize: false }) - -export const TutorRequest = appInstance.dbs.mongo.use('conversations').model('TutorRequest', TutorRequestSchema) - -export const TutorRequestChange = appInstance.dbs.mongo.change(TutorRequest, TutorRequestDbChangeCallbacks, new TutorRequestMapper().mapFrom) \ No newline at end of file diff --git a/services/api/src/modules/conversations/data/repositories/conversations.ts b/services/api/src/modules/conversations/data/repositories/conversations.ts index 5a5080cb..c6090eac 100644 --- a/services/api/src/modules/conversations/data/repositories/conversations.ts +++ b/services/api/src/modules/conversations/data/repositories/conversations.ts @@ -3,22 +3,17 @@ import { BadRequestError, QueryParams } from 'equipped' import { IConversationRepository } from '../../domain/irepositories/conversations' import { EmbeddedUser } from '../../domain/types' import { ConversationMapper } from '../mappers/conversations' -import { ConversationFromModel, ConversationToModel } from '../models/conversations' +import { ConversationToModel } from '../models/conversations' import { MessageFromModel } from '../models/messages' import { Conversation } from '../mongooseModels/conversations' -import { TutorRequest } from '../mongooseModels/tutorRequests' import { Message } from '../mongooseModels/messages' -import { TutorRequestFromModel } from '../models/tutorRequests' -import { TutorRequestMapper } from '../mappers/tutorRequests' export class ConversationRepository implements IConversationRepository { private static instance: ConversationRepository private mapper: ConversationMapper - private tutorRequestMapper: TutorRequestMapper private constructor () { this.mapper = new ConversationMapper() - this.tutorRequestMapper = new TutorRequestMapper() } static getInstance () { @@ -63,7 +58,6 @@ export class ConversationRepository implements IConversationRepository { await Conversation.collection.conn.transaction(async (session) => { const conversation = await Conversation.findOneAndDelete({ _id: id, 'user.id': userId }, { session }) if (!conversation) throw new BadRequestError('conversation not found') - await TutorRequest.deleteMany({ conversationId: conversation.id }, { session }) await Message.deleteMany({ conversationId: conversation.id }, { session }) res = !!conversation return res @@ -72,28 +66,20 @@ export class ConversationRepository implements IConversationRepository { return res } - async removeTutor (data: { conversationId: string, userId: string }) { - const res = { - conversation: null as ConversationFromModel | null, - tutorRequest: null as TutorRequestFromModel | null - } - await Conversation.collection.conn.transaction(async (session) => { - const conversation = await Conversation.findById(data.conversationId, {}, { session }) - if (!conversation) return - if (conversation.user.id !== data.userId) return - if (!conversation.tutor) throw new BadRequestError('conversation has no tutor') - res.conversation = await Conversation.findByIdAndUpdate(data.conversationId, { - $set: { tutor: null } - }, { new: true, session }) - res.tutorRequest = await TutorRequest.findOne({ - conversationId: conversation.id, 'tutor.id': conversation.tutor.id, accepted: true, - }, {}, { session }).sort({ createdAt: -1 }) - return res - }) - return { - conversation: this.mapper.mapFrom(res.conversation), - tutorRequest: this.tutorRequestMapper.mapFrom(res.tutorRequest) - } + async accept ({ id, tutorId, accept }: { id: string, tutorId, accept: boolean }) { + const conversation = await Conversation.findOneAndUpdate( + { _id: id, 'tutor.id': tutorId, pending: true, accepted: null }, + { $set: { pending: false, accepted: { is: accept, at: Date.now() } } }, + { new: true }) + return this.mapper.mapFrom(conversation) + } + + async end (data: { conversationId: string, userId: string, rating: number, message: string }) { + const conversation = await Conversation.findOneAndUpdate( + { _id: data.conversationId, 'user.id': data.userId, ended: null }, + { $set: { ended: { at: Date.now(), rating: data.rating, message: data.message } } }, + { new: true }) + return this.mapper.mapFrom(conversation) } async updateLastMessage (message: MessageFromModel) { diff --git a/services/api/src/modules/conversations/data/repositories/tutorRequests.ts b/services/api/src/modules/conversations/data/repositories/tutorRequests.ts deleted file mode 100644 index 7a09a4ee..00000000 --- a/services/api/src/modules/conversations/data/repositories/tutorRequests.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { appInstance } from '@utils/types' -import { ITutorRequestRepository } from '../../domain/irepositories/tutorRequests' -import { TutorRequestMapper } from '../mappers/tutorRequests' -import { TutorRequestFromModel, TutorRequestToModel } from '../models/tutorRequests' -import { TutorRequest } from '../mongooseModels/tutorRequests' -import { Conversation } from '../mongooseModels/conversations' -import { ConversationMapper } from '../mappers/conversations' -import { EmbeddedUser } from '../../domain/types' -import { BadRequestError } from 'equipped' - -export class TutorRequestRepository implements ITutorRequestRepository { - private static instance: TutorRequestRepository - private mapper = new TutorRequestMapper() - private conversationMapper = new ConversationMapper() - - static getInstance (): TutorRequestRepository { - if (!TutorRequestRepository.instance) TutorRequestRepository.instance = new TutorRequestRepository() - return TutorRequestRepository.instance - } - - async find (id: string) { - const tutorRequest = await TutorRequest.findById(id) - return this.mapper.mapFrom(tutorRequest) - } - - async get (query) { - const data = await appInstance.dbs.mongo.query(TutorRequest, query) - return { - ...data, - results: data.results.map((u) => this.mapper.mapFrom(u)!) - } - } - - async create (data: TutorRequestToModel) { - let res = null as TutorRequestFromModel | null - await TutorRequest.collection.conn.transaction(async (session) => { - const conversation = this.conversationMapper.mapFrom(await Conversation.findById(data.conversationId, {}, { session })) - if (!conversation || conversation.user.id !== data.userId) throw new BadRequestError('Conversation not found') - if (conversation.tutor) throw new BadRequestError('Conversation already has a tutor') - const tutorRequest = await TutorRequest.findOneAndUpdate( - { conversationId: data.conversationId, pending: true }, - { $setOnInsert: { ...data, pending: true, accepted: false } }, - { upsert: true, new: true, session }) - res = tutorRequest - return res - }) - return this.mapper.mapFrom(res)! - } - - async updateUserBio (user: EmbeddedUser) { - const res = await TutorRequest.updateMany({ 'tutor.id': user.id }, { $set: { tutor: user } }) - return res.acknowledged - } - - async accept ({ id, tutorId, accept }: { id: string, tutorId, accept: boolean }) { - let res = false - await TutorRequest.collection.conn.transaction(async (session) => { - const tutorRequest = await TutorRequest.findOneAndUpdate({ _id: id, 'tutor.id': tutorId, pending: true }, { $set: { accepted: accept, pending: false } }, { session }) - if (!tutorRequest) throw new BadRequestError('Request not found') - const conversation = this.conversationMapper.mapFrom(await Conversation.findById(tutorRequest.conversationId, {}, { session })) - if (!conversation) throw new BadRequestError('Conversation not found') - if (accept) { - if (conversation.tutor && conversation.tutor.id !== tutorId) throw new BadRequestError('Conversation already has a tutor') - await Conversation.findByIdAndUpdate(tutorRequest.conversationId, { $set: { tutor: tutorRequest.tutor } }, { new: true, session }) - } - res = !!tutorRequest - return res - }) - return res - } - - async delete (data: { id: string, userId: string }) { - const tutorRequest = await TutorRequest.findOneAndDelete({ _id: data.id, userId: data.userId, pending: true }) - return !!tutorRequest - } -} \ No newline at end of file diff --git a/services/api/src/modules/conversations/domain/entities/conversations.ts b/services/api/src/modules/conversations/domain/entities/conversations.ts index 2914ec79..6c0a1eb4 100644 --- a/services/api/src/modules/conversations/domain/entities/conversations.ts +++ b/services/api/src/modules/conversations/domain/entities/conversations.ts @@ -9,17 +9,23 @@ export class ConversationEntity extends BaseEntity { public readonly title: string public readonly user: EmbeddedUser public readonly tutor: EmbeddedUser | null + public readonly pending: boolean + public readonly accepted: { is: boolean, at: number } | null + public readonly ended: { rating: number, message: string, at: number } | null public readonly createdAt: number public readonly updatedAt: number public readonly last: MessageEntity | null public readonly readAt: Record - constructor ({ id, title, user, tutor, createdAt, updatedAt, last, readAt }: ConversationConstructorArgs) { + constructor ({ id, title, user, tutor, pending, accepted, ended, createdAt, updatedAt, last, readAt }: ConversationConstructorArgs) { super() this.id = id this.title = title this.user = generateDefaultUser(user) this.tutor = tutor ? generateDefaultUser(tutor) : null + this.pending = pending + this.accepted = accepted + this.ended = ended this.createdAt = createdAt this.updatedAt = updatedAt this.last = last @@ -36,6 +42,9 @@ type ConversationConstructorArgs = { title: string user: EmbeddedUser tutor: EmbeddedUser | null + pending: boolean + accepted: { is: boolean, at: number } | null + ended: { rating: number, message: string, at: number } | null createdAt: number updatedAt: number last: MessageEntity | null diff --git a/services/api/src/modules/conversations/domain/entities/tutorRequests.ts b/services/api/src/modules/conversations/domain/entities/tutorRequests.ts deleted file mode 100644 index aefd0681..00000000 --- a/services/api/src/modules/conversations/domain/entities/tutorRequests.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { BaseEntity } from 'equipped' -import { generateDefaultUser } from '@modules/users' -import { EmbeddedUser } from '../types' - -export class TutorRequestEntity extends BaseEntity { - public readonly id: string - public readonly tutor: EmbeddedUser - public readonly userId: string - public readonly conversationId: string - public readonly message: string - public readonly pending: boolean - public readonly accepted: boolean - public readonly createdAt: number - public readonly updatedAt: number - - constructor ({ id, tutor, userId, conversationId, message, pending, accepted, createdAt, updatedAt }: TutorRequestConstructorArgs) { - super() - this.id = id - this.tutor = generateDefaultUser(tutor) - this.userId = userId - this.conversationId = conversationId - this.message = message - this.pending = pending - this.accepted = accepted - this.createdAt = createdAt - this.updatedAt = updatedAt - } -} - -type TutorRequestConstructorArgs = { - id: string - tutor: EmbeddedUser - userId: string - conversationId: string - message: string - pending: boolean - accepted: boolean - createdAt: number - updatedAt: number -} \ No newline at end of file diff --git a/services/api/src/modules/conversations/domain/irepositories/conversations.ts b/services/api/src/modules/conversations/domain/irepositories/conversations.ts index eb7c88b0..a2fbfa43 100644 --- a/services/api/src/modules/conversations/domain/irepositories/conversations.ts +++ b/services/api/src/modules/conversations/domain/irepositories/conversations.ts @@ -3,7 +3,6 @@ import { ConversationToModel } from '../../data/models/conversations' import { MessageFromModel } from '../../data/models/messages' import { ConversationEntity } from '../entities/conversations' import { EmbeddedUser } from '../types' -import { TutorRequestEntity } from '../entities/tutorRequests' export interface IConversationRepository { add: (data: ConversationToModel) => Promise @@ -11,7 +10,8 @@ export interface IConversationRepository { find: (id: string) => Promise delete: (id: string, userId: string) => Promise update: (id: string, userId: string, data: Partial) => Promise + accept: (data: { id: string, tutorId: string, accept: boolean }) => Promise updateUserBio: (user: EmbeddedUser) => Promise - removeTutor: (data: { conversationId: string, userId: string }) => Promise<{ conversation: ConversationEntity | null, tutorRequest: TutorRequestEntity | null }> + end: (data: { conversationId: string, userId: string, rating: number, message: string }) => Promise updateLastMessage: (message: MessageFromModel) => Promise } \ No newline at end of file diff --git a/services/api/src/modules/conversations/domain/irepositories/tutorRequests.ts b/services/api/src/modules/conversations/domain/irepositories/tutorRequests.ts deleted file mode 100644 index 4911198e..00000000 --- a/services/api/src/modules/conversations/domain/irepositories/tutorRequests.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { QueryParams, QueryResults } from 'equipped' -import { TutorRequestToModel } from '../../data/models/tutorRequests' -import { TutorRequestEntity } from '../entities/tutorRequests' -import { EmbeddedUser } from '../types' - -export interface ITutorRequestRepository { - find: (id: string) => Promise - get: (query: QueryParams) => Promise> - create: (data: TutorRequestToModel) => Promise - accept: (data: { id: string, tutorId: string, accept: boolean }) => Promise - updateUserBio: (user: EmbeddedUser) => Promise - delete: (data: { id: string, userId: string }) => Promise -} \ No newline at end of file diff --git a/services/api/src/modules/conversations/domain/useCases/conversations.ts b/services/api/src/modules/conversations/domain/useCases/conversations.ts index 7690e65d..8a6329d7 100644 --- a/services/api/src/modules/conversations/domain/useCases/conversations.ts +++ b/services/api/src/modules/conversations/domain/useCases/conversations.ts @@ -1,5 +1,4 @@ import { QueryParams } from 'equipped' -import { InteractionEntities, ReviewsUseCases } from '@modules/interactions' import { ConversationToModel } from '../../data/models/conversations' import { MessageFromModel } from '../../data/models/messages' import { IConversationRepository } from '../irepositories/conversations' @@ -36,13 +35,12 @@ export class ConversationsUseCase { return await this.repository.updateUserBio(user) } - async removeTutor (data: { conversationId: string, user: EmbeddedUser, rating: number, message: string }) { - const { conversation, tutorRequest } = await this.repository.removeTutor({ conversationId: data.conversationId, userId: data.user.id }) - if (conversation && tutorRequest) await ReviewsUseCases.add({ - user: data.user, rating: data.rating, message: data.message, - entity: { type: InteractionEntities.tutorConversations, id: tutorRequest.id, userId: tutorRequest.tutor.id } - }) - return conversation + async accept (data: { id: string, tutorId: string, accept: boolean }) { + return await this.repository.accept(data) + } + + async end (data: { conversationId: string, userId: string, rating: number, message: string }) { + return await this.repository.end(data) } async updateLastMessage (message: MessageFromModel) { diff --git a/services/api/src/modules/conversations/domain/useCases/tutorRequests.ts b/services/api/src/modules/conversations/domain/useCases/tutorRequests.ts deleted file mode 100644 index fa3cc6c0..00000000 --- a/services/api/src/modules/conversations/domain/useCases/tutorRequests.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { QueryParams } from 'equipped' -import { TutorRequestToModel } from '../../data/models/tutorRequests' -import { ITutorRequestRepository } from '../irepositories/tutorRequests' -import { EmbeddedUser } from '../types' - -export class TutorRequestsUseCase { - repository: ITutorRequestRepository - - constructor (repo: ITutorRequestRepository) { - this.repository = repo - } - - async find (id: string) { - return await this.repository.find(id) - } - - async get (input: QueryParams) { - return await this.repository.get(input) - } - - async create (input: TutorRequestToModel) { - return await this.repository.create(input) - } - - async accept (data: { id: string, tutorId: string, accept: boolean }) { - return await this.repository.accept(data) - } - - async delete (data: { id: string, userId: string }) { - return await this.repository.delete(data) - } - - async updateUserBio (user: EmbeddedUser) { - return await this.repository.updateUserBio(user) - } -} \ No newline at end of file diff --git a/services/api/src/modules/conversations/index.ts b/services/api/src/modules/conversations/index.ts index 844492a3..562a7f1c 100644 --- a/services/api/src/modules/conversations/index.ts +++ b/services/api/src/modules/conversations/index.ts @@ -1,16 +1,12 @@ import { ConversationRepository } from './data/repositories/conversations' import { MessageRepository } from './data/repositories/messages' -import { TutorRequestRepository } from './data/repositories/tutorRequests' import { ConversationsUseCase } from './domain/useCases/conversations' import { MessagesUseCase } from './domain/useCases/messages' -import { TutorRequestsUseCase } from './domain/useCases/tutorRequests' const conversationRepository = ConversationRepository.getInstance() const messageRepository = MessageRepository.getInstance() -const tutorRequestRepository = TutorRequestRepository.getInstance() export const ConversationsUseCases = new ConversationsUseCase(conversationRepository) export const MessagesUseCases = new MessagesUseCase(messageRepository) -export const TutorRequestsUseCases = new TutorRequestsUseCase(tutorRequestRepository) export { canAccessConversation } from './utils' diff --git a/services/api/src/modules/conversations/utils/changes/conversations.ts b/services/api/src/modules/conversations/utils/changes/conversations.ts index 3620d9d6..d757be63 100644 --- a/services/api/src/modules/conversations/utils/changes/conversations.ts +++ b/services/api/src/modules/conversations/utils/changes/conversations.ts @@ -1,3 +1,6 @@ +import { InteractionEntities, ReviewsUseCases } from '@modules/interactions' +import { NotificationType, sendNotification } from '@modules/notifications' +import { PlanDataType, WalletsUseCases } from '@modules/payment' import { UsersUseCases } from '@modules/users' import { appInstance } from '@utils/types' import { DbChangeCallbacks } from 'equipped' @@ -20,25 +23,46 @@ export const ConversationDbChangeCallbacks: DbChangeCallbacks { @@ -48,10 +72,8 @@ export const ConversationDbChangeCallbacks: DbChangeCallbacks = { - created: async ({ after }) => { - await appInstance.listener.created( - [after.tutor.id, after.userId].map((uid) => [`conversations/tutorRequests/${uid}`, `conversations/tutorRequests/${after.id}/${uid}`]).flat(), - after) - - await WalletsUseCases.updateSubscriptionData({ userId: after.userId, key: PlanDataType.tutorAidedConversations, value: -1 }) - }, - updated: async ({ after, before, changes }) => { - await appInstance.listener.created( - [after.tutor.id, after.userId].map((uid) => [`conversations/tutorRequests/${uid}`, `conversations/tutorRequests/${after.id}/${uid}`]).flat(), - after) - - if (changes.pending && before.pending && !after.pending) { - if (!after.accepted) await WalletsUseCases.updateSubscriptionData({ userId: after.userId, key: PlanDataType.tutorAidedConversations, value: 1 }) - await sendNotification([after.userId], { - title: `Tutor request ${after.accepted ? 'accepted' : 'declined'}`, - body: after.accepted ? `Your tutor request has been accepted by ${after.tutor.bio.name}` : `Your tutor request has been declined by ${after.tutor.bio.name}. You can send a new request to a different tutor`, - sendEmail: true, - data: { - type: NotificationType.TutorAddedToConversation, - accepted: after.accepted, - tutorId: after.tutor.id, - conversationId: after.conversationId - } - }) - } - }, - deleted: async ({ before }) => { - await appInstance.listener.created( - [before.tutor.id, before.userId].map((uid) => [`conversations/tutorRequests/${uid}`, `conversations/tutorRequests/${before.id}/${uid}`]).flat(), - before) - - if (before.pending) await WalletsUseCases.updateSubscriptionData({ userId: before.userId, key: PlanDataType.tutorAidedConversations, value: 1 }) - } -} diff --git a/services/api/src/modules/interactions/domain/types/index.ts b/services/api/src/modules/interactions/domain/types/index.ts index d257e0f3..23f3684a 100644 --- a/services/api/src/modules/interactions/domain/types/index.ts +++ b/services/api/src/modules/interactions/domain/types/index.ts @@ -6,7 +6,7 @@ export enum InteractionEntities { quizzes = 'quizzes', users = 'users', quizQuestions = 'quizQuestions', - tutorConversations = 'tutorConversations', + conversations = 'conversations', } export type Interaction = { diff --git a/services/api/src/modules/interactions/utils/changes/reviews.ts b/services/api/src/modules/interactions/utils/changes/reviews.ts index c721086c..8f77d7b2 100644 --- a/services/api/src/modules/interactions/utils/changes/reviews.ts +++ b/services/api/src/modules/interactions/utils/changes/reviews.ts @@ -12,7 +12,7 @@ export const ReviewDbChangeCallbacks: DbChangeCallbacks { await appInstance.listener.updated(['interactions/reviews', `interactions/reviews/${after.id}`], after) @@ -22,7 +22,7 @@ export const ReviewDbChangeCallbacks: DbChangeCallbacks CoursesUseCases.updateRatings({ id: after.entity.id, ratings: after.rating, add: true })) if (after.entity.type === InteractionEntities.quizzes) await QuizzesUseCases.updateRatings({ id: after.entity.id, ratings: before.rating, add: false }) .then(() => QuizzesUseCases.updateRatings({ id: after.entity.id, ratings: after.rating, add: true })) - if (after.entity.type === InteractionEntities.tutorConversations) await UsersUseCases.updateRatings({ id: after.entity.userId, ratings: before.rating, add: false }) + if (after.entity.type === InteractionEntities.conversations) await UsersUseCases.updateRatings({ id: after.entity.userId, ratings: before.rating, add: false }) .then(() => UsersUseCases.updateRatings({ id: after.entity.userId, ratings: after.rating, add: true })) } }, @@ -31,7 +31,7 @@ export const ReviewDbChangeCallbacks: DbChangeCallbacks { - const request = await TutorRequestsUseCases.find(id) - return request?.tutor.id + [InteractionEntities.conversations]: async (id: string) => { + const conversation = await ConversationsUseCases.find(id) + return conversation?.id }, [InteractionEntities.users]: async (id: string) => { const user = await UsersUseCases.find(id) @@ -40,7 +40,7 @@ export const verifyInteractionAndGetUserId = async (type: InteractionEntities, i if (interaction === 'comments') return [InteractionEntities.comments] if (interaction === 'views') return [InteractionEntities.courses, InteractionEntities.quizzes] if (interaction === 'reports') return [InteractionEntities.courses, InteractionEntities.quizzes, InteractionEntities.users, InteractionEntities.quizQuestions] - if (interaction === 'reviews') return [InteractionEntities.courses, InteractionEntities.quizzes, /* InteractionEntities.tutorConversations // hidden so users cannot create this externally */] + if (interaction === 'reviews') return [InteractionEntities.courses, InteractionEntities.quizzes, /* InteractionEntities.conversations // hidden so users cannot create this externally */] return [] })().reduce((acc, cur) => { acc[cur] = finders[cur] diff --git a/services/api/src/modules/users/utils/changes/users.ts b/services/api/src/modules/users/utils/changes/users.ts index b2cc973f..74863898 100644 --- a/services/api/src/modules/users/utils/changes/users.ts +++ b/services/api/src/modules/users/utils/changes/users.ts @@ -1,4 +1,4 @@ -import { ConversationsUseCases, TutorRequestsUseCases } from '@modules/conversations' +import { ConversationsUseCases } from '@modules/conversations' import { CommentsUseCases, LikesUseCases, ReportsUseCases, ReviewsUseCases, ViewsUseCases } from '@modules/interactions' import { GamesUseCases } from '@modules/plays' import { CoursesUseCases, FilesUseCases, FoldersUseCases, QuizzesUseCases } from '@modules/study' @@ -26,7 +26,7 @@ export const UserDbChangeCallbacks: DbChangeCallbacks const updatedBioOrRoles = !!changes.bio || !!changes.roles if (updatedBioOrRoles) await Promise.all([ - ConversationsUseCases, TutorRequestsUseCases, + ConversationsUseCases, CommentsUseCases, LikesUseCases, ReportsUseCases, ReviewsUseCases, ViewsUseCases, GamesUseCases, CoursesUseCases, FoldersUseCases, QuizzesUseCases, FilesUseCases, diff --git a/services/api/src/utils/sockets.ts b/services/api/src/utils/sockets.ts index 7d60bb91..a1777d89 100644 --- a/services/api/src/utils/sockets.ts +++ b/services/api/src/utils/sockets.ts @@ -28,7 +28,6 @@ export const registerSockets = () => { appInstance.listener .register('conversations/conversations', isMine) .register('conversations/conversations/:conversationId/messages', conversationsCb) - .register('conversations/tutorRequests', isMine) .register('interactions/comments', isOpen) .register('interactions/likes', isOpen)