import moment from 'moment' import { schema } from 'nexus' import { sendPost } from '../../queues' import * as EmailUtils from '../../utils/email' import GeneralUtils from '../../utils/general' import * as PostController from '../../controllers/post' import * as Auth from '../../utils/auth' import { PostWhereInput, PostWhereUniqueInput, PostUpdateInput, PostOrderByInput, Post as PostPrisma, } from 'nexus-plugin-prisma/client' export const AttachmentInputType = schema.inputObjectType({ name: 'AttachmentInput', definition(t) { t.string('url') t.string('name') t.string('type') }, }) export const Post = schema.objectType({ name: 'Post', definition(t) { t.model.id() t.model.createdAt() t.model.updatedAt() t.model.title() t.model.description() t.model.sendAsSMS() t.model.sendAsEmailToNonAppUser() t.model.type() t.model.sendPostAt() t.model.postSentAt() t.model.assemblyDate() t.model.assemblyLocation() t.model.assemblyTime() t.model.isDraft() t.model.sendAsAPP() t.model.sendAsEmail() t.model.coverImage() t.model.poll() t.model.project() t.model.notifications() t.model.audience() t.model.attachments() t.model.seenBy() t.model.signedProxies() t.model.segments() }, }) export const PostQueries = schema.extendType({ type: 'Query', definition(t) { t.crud.post() t.crud.posts({ filtering: true, ordering: true, pagination: true }) // TMP because we don't want resident to read post that wasn't for them t.field('posts', { type: 'Post', list: true, args: { where: schema.arg({ type: 'PostWhereInput' }), orderBy: schema.arg({ type: 'PostOrderByInput', list: true }), first: schema.arg({ type: 'Int' }), skip: schema.arg({ type: 'Int' }), }, resolve: async ( parent, { where, orderBy, first, skip, }: { where?: PostWhereInput; orderBy?: PostOrderByInput[]; first?: number; skip?: number }, ctx: NexusContext ) => { const userId = Auth.getUserId(ctx) const user = await ctx.prisma.user.findOne({ where: { id: userId }, select: { id: true, role: true }, }) // Make sure to not return post for people that are not in segments if (user.role === 'RESIDENT') { const posts = await ctx.prisma.post.findMany({ where: where, ...(orderBy?.length > 0 && { orderBy: orderBy[0] }), take: first, skip: skip, }) const validPosts: PostPrisma[] = [] await Promise.all( posts.map(async (post) => { const postIsValid = await PostController.postIsValidForResident({ postId: post.id, userId, }) if (postIsValid) { validPosts.push(post) } }) ) return validPosts } return ctx.prisma.post.findMany({ where: where, ...(orderBy?.length > 0 && { orderBy: orderBy[0] }), take: first, skip: skip, }) }, }) }, }) export const PostMutation = schema.extendType({ type: 'Mutation', definition: (t) => { t.crud.deleteOnePost({ alias: 'deletePost' }) t.crud.updateOnePost({ alias: 'updatePost' }) t.crud.createOnePost({ alias: 'createPost' }) t.field('sendPostPreview', { type: 'Boolean', args: { email: schema.stringArg(), title: schema.stringArg(), description: schema.stringArg(), projectId: schema.stringArg(), attachments: schema.arg({ type: 'AttachmentInput', list: true }), coverImageUrl: schema.stringArg(), }, resolve: async ( parent, { email, title, description, projectId, attachments, coverImageUrl }, ctx ) => { const project = await ctx.prisma.project.findOne({ where: { id: projectId }, select: { id: true, name: true, colorHex: true, logo: { select: { url: true }, }, managingCompany: { select: { logo: { select: { url: true, }, }, }, }, }, }) const descriptionFormatted = GeneralUtils.removeRichTextareaLineBreak(description) await EmailUtils.send({ to: [{ email }], template: 'RESIDENT_NEW_POST', attachments: attachments ? attachments.map(({ name, url }) => ({ url, filename: name })) : null, subject: `${project.name} - ${title}`, customVariables: { title, coverImageUrl, body: GeneralUtils.splitTextWithLineBreak(descriptionFormatted), bodySplitted: GeneralUtils.splitTextWithLineBreak(descriptionFormatted), managingCompanyLogoUrl: project.managingCompany.logo?.url, projectLogoUrl: project.logo?.url, projectColor: project.colorHex, projectName: project.name, buildingName: project.name, hasInstalledApp: true, }, }) return true }, }) t.field('createPost', { type: 'Post', args: { data: schema.arg({ type: 'PostCreateInput' }), }, resolve: async (parent, { data }, ctx: NexusContext) => { if (!data.project) { throw new Error("Can't create a post without having a project linked to it") } if (data.type === 'PROXY' && !data.assemblyDate) { throw new Error("Can't create proxy post without assembly date") } if (data.type === 'PROXY') { data.sendAsSMS = true } const createdPost = await ctx.prisma.post.create({ data }) if (!createdPost.isDraft) { if (createdPost.sendPostAt) { const delayMS = moment(createdPost.sendPostAt).diff(moment()) await sendPost(createdPost.id, delayMS) } else { await sendPost(createdPost.id) } } return ctx.prisma.post.findOne({ where: { id: createdPost.id } }) }, }) t.field('updatePost', { type: 'Post', args: { data: schema.arg({ type: 'PostUpdateInput' }), where: schema.arg({ type: 'PostWhereUniqueInput' }), }, resolve: async ( parent, { where, data }: { where?: PostWhereUniqueInput; data?: PostUpdateInput }, ctx: NexusContext ) => { const updatedPost = await ctx.prisma.post.update({ where, data }) if (data.isDraft) { if (updatedPost.sendPostAt) { const delayMS = moment(updatedPost.sendPostAt).diff(moment()) await sendPost(updatedPost.id, delayMS) } else { await sendPost(updatedPost.id) } } return updatedPost }, }) }, })