diff --git a/client/src/FetchActions.ts b/client/src/FetchActions.ts index 31a03123..d6b118a9 100644 --- a/client/src/FetchActions.ts +++ b/client/src/FetchActions.ts @@ -1,4 +1,5 @@ import { APP_API_BASE_URL } from './configs'; +import { Message } from './types'; export const getRequest = ( url: string, @@ -26,6 +27,42 @@ export const getRequest = ( } }); }; + +export const patchRequest = ( + url: string, + body: any, + onSuccess: Function, + abortController?: AbortController, + onError = (statusCode?: Number, statusText?: string) => {}, +) => { + let options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + signal: abortController?.signal, + body: JSON.stringify(body), + }; + fetch(url, options) + .then((resp) => { + if (!resp.ok) { + onError(resp.status, resp.statusText); + } else { + return resp.json(); + } + }) + .then((data) => onSuccess(data)) + .catch((e) => { + if (e.name === 'AbortError') { + // usually, this abort is intentional due to a change in props + console.log('fetch aborted'); + } else { + onError(); + } + }); +}; +// best practice for fetching is to allow fetches to be aborted, in case props change while a fetch is in progress + export const fetchNeeds = ( limit: string, offset: string, @@ -63,3 +100,12 @@ export const fetchInbox = ( const assetsApiRequest = new URL(`${APP_API_BASE_URL}/transactions/inbox`); getRequest(assetsApiRequest.href + assetsApiRequest.hash, onSuccess, abortController, onError); }; + +export const updateMessage = ( + messageBody: Message, + onSuccess: Function, + onError = (statusCode?: Number, statusText?: string) => {}, +) => { + const messageApiRequest = new URL(`${APP_API_BASE_URL}/messages/${messageBody.id}`); + patchRequest(messageApiRequest.href, messageBody, onSuccess, new AbortController(), () => {}); +}; diff --git a/client/src/components/Users/Inbox/MessageCard.tsx b/client/src/components/Users/Inbox/MessageCard.tsx index db5e33ef..0afa4122 100644 --- a/client/src/components/Users/Inbox/MessageCard.tsx +++ b/client/src/components/Users/Inbox/MessageCard.tsx @@ -4,6 +4,7 @@ import { makeStyles } from 'tss-react/mui'; import type { Theme } from '@mui/material/styles'; import { Typography } from '@mui/material'; +import { Button } from '@mui/base'; const useStyles = makeStyles()((theme: Theme) => ({ currentUserMessage: { @@ -30,14 +31,15 @@ function MessageCard({ senderName, isCurrentUser, dateString, + messageReadCallback, }: { senderName: string; text: string; isCurrentUser: boolean; dateString: string; + messageReadCallback: React.MouseEventHandler; }): JSX.Element { const { classes } = useStyles(); - return (
@@ -46,6 +48,7 @@ function MessageCard({ {dateString} +
); } diff --git a/client/src/types/index.ts b/client/src/types/index.ts index 79ca6960..08a6a0df 100644 --- a/client/src/types/index.ts +++ b/client/src/types/index.ts @@ -78,6 +78,7 @@ export type Message = { sendingUserId: number | null; sendingOrgId: number | null; created_date: string; + read: boolean; }; export type Option = { diff --git a/client/src/views/Messages.tsx b/client/src/views/Messages.tsx index 280dbed3..0e8a6bb4 100644 --- a/client/src/views/Messages.tsx +++ b/client/src/views/Messages.tsx @@ -1,38 +1,47 @@ import React from 'react'; -import { Message } from '../types'; +import { Message, Transaction } from '../types'; import MessageCard from '../components/Users/Inbox/MessageCard'; +import { patchRequest, updateMessage } from '../FetchActions'; function Messages({ messages, transaction, user }: any) { const formatDate = (date: string): string => { return new Date(date).toLocaleString('en-US'); }; + const getSenderName = (transaction: Transaction, message: Message) => { + if (message.sendingOrgId) { + if (message.sendingOrgId === transaction.claimer?.id) { + return transaction.claimer.name; + } else if (message.sendingOrgId === transaction.donater_organization?.id) { + return transaction.donater_organization.name; + } else { + return ''; + } + } else if (message.sendingUserId === (user && user.id)) { + return user.firstName; + } else { + return transaction.donater_user?.firstName; + } + }; + return (
{messages.map((message: Message) => { - const sendingOrg = [transaction.donater_organization, transaction.claimer].find( - (org) => org && org.id === message.sendingOrgId, - ); - const userOrg = - user && - user.organizations[0] && - user.organizations[0].organization && - user.organizations[0].organization.id; - - let sendingUser = null; - if (transaction.donater_user && transaction.donater_user.id === message.sendingUserId) { - sendingUser = transaction.donater_user; - } - const senderName = sendingOrg ? sendingOrg.name : sendingUser && sendingUser.firstName; - const isCurrentUser = - message.sendingUserId === user.id || (userOrg && userOrg === message.sendingOrgId); - + const markMessageRead: React.MouseEventHandler = (event) => + updateMessage( + message, + () => {}, + () => { + console.log('error'); + }, + ); return ( ); })} diff --git a/server/src/messages/dto/create-message.dto.ts b/server/src/messages/dto/create-message.dto.ts index 463f2f6a..6918d3ad 100644 --- a/server/src/messages/dto/create-message.dto.ts +++ b/server/src/messages/dto/create-message.dto.ts @@ -8,6 +8,9 @@ export class CreateMessageDto { @IsNotEmpty() text: string; + @IsOptional() + read: boolean + @IsNotEmpty() transaction: Transaction; @@ -15,5 +18,5 @@ export class CreateMessageDto { sending_user: User; @IsOptional() - sending_org: Organization; + sending_org?: Organization; } diff --git a/server/src/messages/dto/return-message.dto.ts b/server/src/messages/dto/return-message.dto.ts index f9ad59cb..4a9490b8 100644 --- a/server/src/messages/dto/return-message.dto.ts +++ b/server/src/messages/dto/return-message.dto.ts @@ -1,8 +1,24 @@ -import { IsNotEmpty } from 'class-validator'; +import { IsNotEmpty, IsOptional } from 'class-validator'; import { CreateMessageDto } from './create-message.dto'; +import { Transaction } from 'src/transactions/entities/transaction.entity'; +import { User } from 'src/acccount-manager/entities/user.entity'; +import { Organization } from 'src/organizations/entities/organization.entity'; export class ReturnMessageDto extends CreateMessageDto { + id: number + + text: string + + read: boolean + @IsNotEmpty() - id: number; + transaction: Transaction; + + @IsOptional() + sending_user: User; + + @IsOptional() + sending_org?: Organization; + } diff --git a/server/src/messages/dto/update-message.dto.ts b/server/src/messages/dto/update-message.dto.ts index 2e88d19c..2972523e 100644 --- a/server/src/messages/dto/update-message.dto.ts +++ b/server/src/messages/dto/update-message.dto.ts @@ -2,4 +2,4 @@ import { PickType } from '@nestjs/swagger'; import { CreateMessageDto } from './create-message.dto'; -export class UpdateMessageDto extends PickType(CreateMessageDto, ['text'] as const) {} +export class UpdateMessageDto extends PickType(CreateMessageDto, ['text', 'read' ] as const) {} diff --git a/server/src/messages/entities/message.entity.ts b/server/src/messages/entities/message.entity.ts index 0d480619..4c08b135 100644 --- a/server/src/messages/entities/message.entity.ts +++ b/server/src/messages/entities/message.entity.ts @@ -43,10 +43,11 @@ export class Message { @Column({ nullable: true }) sendingOrgId: number; + @Column() + read: boolean + @ManyToOne(() => Transaction, (transaction) => transaction.messages) @JoinColumn() transaction: Transaction; - @OneToMany(() => Receivedmessage, (userMessage) => userMessage.message) - readReceipts?: Receivedmessage[]; } diff --git a/server/src/messages/messages.controller.ts b/server/src/messages/messages.controller.ts index 35dc09ce..3345c752 100644 --- a/server/src/messages/messages.controller.ts +++ b/server/src/messages/messages.controller.ts @@ -30,7 +30,7 @@ export class MessagesController { async create( @Request() request: ExpressRequest, @Body() createMessageDto: CreateMessageDto, - ): Promise { + ): Promise { const newMessage = await this.messagesService.create(createMessageDto); return newMessage; } @@ -52,10 +52,10 @@ export class MessagesController { @Patch(':id') @ApiOperation({ summary: 'Update a message.' }) async update( - @Param('id') id: string, + @Param('id') id: number, @Body() updateMessageDto: UpdateMessageDto, ): Promise { - return this.messagesService.update(+id, updateMessageDto); + return this.messagesService.update(id, updateMessageDto); } @Delete(':id') diff --git a/server/src/transactions/transactions.controller.ts b/server/src/transactions/transactions.controller.ts index d9ab88c3..5c630654 100644 --- a/server/src/transactions/transactions.controller.ts +++ b/server/src/transactions/transactions.controller.ts @@ -1,4 +1,4 @@ -import { Get, Post, Body, Query, Param, Patch, Delete, Controller } from '@nestjs/common'; +import { Get, Post, Body, Query, Param, Patch, Delete, Controller, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { TransactionsService } from './transactions.service';