import { Resolver, Query, FieldResolver, Root, Arg, Mutation, Ctx, Args, Authorized } from 'type-graphql'; import { addOneGeneric } from '@seed/graphql/BaseService'; import RoomModel, { RoomArgs, RemovePeopleFromRoomInput, AddPeopleToRoomInput, NewRoomInput, } from '@services/api-messaging/functions/rooms/room.model'; import { ApolloContext } from '@seed/interfaces/context'; import { ApolloError } from 'apollo-server-lambda'; import AccountModel from '@src/accounts/account.model'; import { onCreate } from '@services/api-messaging/functions/rooms/room.model'; import _ from 'lodash'; import MessageModel, { MessageArgs } from '../messages/message.model'; import { oneToManyComplexity } from '@seed/graphql/Middleware'; import { UnreadCount } from '@services/api-messaging/components'; @Resolver(RoomModel) export default class RoomResolver { /* ███████╗██╗███████╗██╗ ██████╗ ███████╗ ██╔════╝██║██╔════╝██║ ██╔══██╗██╔════╝ █████╗ ██║█████╗ ██║ ██║ ██║███████╗ ██╔══╝ ██║██╔══╝ ██║ ██║ ██║╚════██║ ██║ ██║███████╗███████╗██████╔╝███████║ ╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ ╚══════╝ */ @FieldResolver() async getAccounts(@Root() room: RoomModel, @Ctx() ctx: ApolloContext): Promise<(AccountModel | Error)[]> { return await ctx.ctx.loaders.accountLoader.loadMany(room.r); } @FieldResolver(() => [MessageModel], { complexity: oneToManyComplexity }) async getMessages(@Args() args: MessageArgs, @Root() room: RoomModel, @Ctx() ctx: ApolloContext): Promise { try { if (!args.pagination) args.pagination = { limit: 10, skip: 0, sort: 'createdAt desc' }; else if (args.pagination && !args.pagination.sort) args.pagination = { ...args.pagination, sort: 'createdAt desc' }; return new MessageModel().getMany({ roomId: room._id }, args.pagination, ctx); } catch (error) { throw error; } } /* ██████╗ ██╗ ██╗███████╗██████╗ ██╗ ██╗ ██╔═══██╗██║ ██║██╔════╝██╔══██╗╚██╗ ██╔╝ ██║ ██║██║ ██║█████╗ ██████╔╝ ╚████╔╝ ██║▄▄ ██║██║ ██║██╔══╝ ██╔══██╗ ╚██╔╝ ╚██████╔╝╚██████╔╝███████╗██║ ██║ ██║ ╚══▀▀═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ */ @Query(() => RoomModel) @Authorized() async roomsGetOne(@Arg('id') id: string, @Ctx() ctx: ApolloContext): Promise { const model = new RoomModel(); const roomData = await model.getOne({ _id: id }, ctx); if (!roomData.r.includes(ctx.ctx.user._id)) throw new ApolloError('permissions.notAllowed', '400', { you: ctx.ctx.user._id, allowed: roomData.r }); return roomData; } @Query(() => [RoomModel]) @Authorized() async roomsGetMany(@Args() args: RoomArgs, @Ctx() ctx: ApolloContext): Promise { const model = new RoomModel(); const result = await model.getMany({ accountIds: ctx.ctx.user._id }, args.pagination, ctx); return result; } @Query(() => [MessageModel]) @Authorized() async roomsGetMessages(@Arg('id') id: string, @Args() args: RoomArgs, @Ctx() ctx: ApolloContext): Promise { const model = new RoomModel(); const roomData = await model.getOne({ _id: id }, ctx); if (!roomData.r.includes(ctx.ctx.user._id)) throw new ApolloError('permissions.notAllowed', '400', { you: ctx.ctx.user._id, allowed: roomData.r }); if (!args.pagination) args.pagination = { limit: 10, skip: 0, sort: 'createdAt desc' }; else if (args.pagination && !args.pagination.sort) args.pagination = { ...args.pagination, sort: 'createdAt desc' }; return new MessageModel().getMany({ roomId: id }, args.pagination, ctx); } /* ███╗ ███╗██╗ ██╗████████╗ █████╗ ████████╗ ██████╗ ██████╗ ███████╗ ████╗ ████║██║ ██║╚══██╔══╝██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗██╔════╝ ██╔████╔██║██║ ██║ ██║ ███████║ ██║ ██║ ██║██████╔╝███████╗ ██║╚██╔╝██║██║ ██║ ██║ ██╔══██║ ██║ ██║ ██║██╔══██╗╚════██║ ██║ ╚═╝ ██║╚██████╔╝ ██║ ██║ ██║ ██║ ╚██████╔╝██║ ██║███████║ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ */ @Mutation(() => RoomModel) @Authorized() async roomsCreateOne(@Arg('input') input: NewRoomInput, @Ctx() ctx: ApolloContext): Promise { const model = new RoomModel(); try { await onCreate(input, ctx); } catch (error) { throw error; } input.accountIds.push(ctx.ctx.user._id); input.accountIds = _.uniq(input.accountIds); // Check if a room with the same user doesn't exists already try { return await model.getOne( { r: { $all: input.accountIds }, }, ctx, ); } catch (error) { const unreadCounts: UnreadCount[] = []; for (let index = 0; index < input.accountIds.length; index++) { unreadCounts.push({ accountId: input.accountIds[index], count: 0, }); } const dataToSave: Partial = { ...input, r: input.accountIds, w: input.accountIds, stats: { unreadCounts: unreadCounts, }, }; return addOneGeneric(model, dataToSave, ctx); } } @Mutation(() => RoomModel) @Authorized() async roomsAddPeople(@Arg('input') input: AddPeopleToRoomInput, @Ctx() ctx: ApolloContext): Promise { const model = new RoomModel(); // Get the room first to see if allowed const roomData = await model.getOne({ _id: input.roomId }, ctx); if (!roomData.r.includes(ctx.ctx.user._id)) throw new ApolloError('permissions.notAllowed', '400', { you: ctx.ctx.user._id, allowed: roomData.r }); const newAccountIds = _.uniq(roomData.r.concat(input.accountIds)); try { await onCreate(input, ctx); } catch (error) { throw error; } // TODO : Remove from stats return roomData.updateOne({ _id: input.roomId }, { r: newAccountIds, w: newAccountIds }, ctx); } @Mutation(() => RoomModel) @Authorized() async roomsRemovePeople(@Arg('input') input: RemovePeopleFromRoomInput, @Ctx() ctx: ApolloContext): Promise { const model = new RoomModel(); // Get the room first to see if allowed const roomData = await model.getOne({ _id: input.roomId }, ctx); if (!roomData.r.includes(ctx.ctx.user._id)) throw new ApolloError('permissions.notAllowed', '400', { you: ctx.ctx.user._id, allowed: roomData.r }); const newAccountIds = _.uniq( roomData.r.filter(function(element) { return roomData.r.indexOf(element) === -1; }), ); // TODO : Remove from stats return roomData.updateOne({ _id: input.roomId }, { r: newAccountIds, w: newAccountIds }, ctx); } @Mutation(() => RoomModel) @Authorized() async roomsMarkAllMessageAsRead(@Arg('roomId') roomId: string, @Ctx() ctx: ApolloContext): Promise { const roomModel = new RoomModel(); await roomModel.getOne({ _id: roomId }, ctx); // The hypothesis here is that the message are accessible only by concerned people. const model = new MessageModel(); await (await model.db()).updateMany( { roomId: roomId, 'readBy.accountId': { $ne: ctx.ctx.user._id } }, { $push: { readBy: { accountId: ctx.ctx.user._id, readAt: new Date() } }, }, ); // Update stats & notify for (let index = 0; index < roomModel.stats.unreadCounts.length; index++) { // Add a count to all the other one if (roomModel.stats.unreadCounts[index].accountId == ctx.ctx.user._id) roomModel.stats.unreadCounts[index].count = 0; } await (await roomModel.db()).findOneAndUpdate( { _id: roomModel._id }, { $set: { stats: roomModel.stats } }, { upsert: false, returnOriginal: false }, ); return roomModel; } }