diff --git a/packages/api/routes/conversations.ts b/packages/api/routes/conversations.ts index 4cdede5..0d0e588 100644 --- a/packages/api/routes/conversations.ts +++ b/packages/api/routes/conversations.ts @@ -9,9 +9,10 @@ import express, { NextFunction, Request, Response, response } from 'express'; import ConversationService from '../services/conversation.service'; import CreateConversationRequest from '@nirvana/core/requests/CreateConversationRequest.request'; import CreateConversationResponse from '@nirvana/core/responses/CreateConversationResponse.response'; -import { MemberState } from '../../core/models/conversation.model'; +import { MemberState } from '@nirvana/core/models/conversation.model'; import NirvanaResponse from '@nirvana/core/responses/nirvanaResponse'; import { ObjectId } from 'mongodb'; +import UpdateConversationPriorityRequest from '@nirvana/core/requests/UpdateConversationPriority.request'; import { UserService } from '../services/user.service'; export default function getConversationRoutes() { @@ -35,6 +36,9 @@ export default function getConversationRoutes() { // create a conversation router.post('/', authCheck, createConversation); + // change user's categorization of conversation + router.post('/:conversationId/priority', authCheck, updatePriorityOfConversation); + return router; } @@ -130,7 +134,7 @@ const createConversation = async (req: Request, res: Response, next: NextFunctio const conversationUserMembers: ConversationUserMember[] = []; createRequest.otherUsers.forEach((userObject) => { - const newConversationMember = new ConversationMember(MemberRole.regular, MemberState.inbox); + const newConversationMember = new ConversationMember(MemberRole.regular, 'inbox'); // ! hack as json over the wire converts to string for some reason userObject._id = new ObjectId(userObject._id); @@ -142,7 +146,7 @@ const createConversation = async (req: Request, res: Response, next: NextFunctio }); // adding in the admin user which is the user who started the conversation - const adminMember = new ConversationMember(MemberRole.admin, MemberState.inbox); + const adminMember = new ConversationMember(MemberRole.admin, 'inbox'); const currentUser = await UserService.getUserById(userInfo.userId); if (!currentUser) { return next(new Error('unable to find your user object for caching')); @@ -172,3 +176,27 @@ const createConversation = async (req: Request, res: Response, next: NextFunctio ); return res.json(responseObj); }; + +const updatePriorityOfConversation = async (req: Request, res: Response, next: NextFunction) => { + const { conversationId } = req.params; + const userInfo = res.locals.userInfo as JwtClaims; + const reqObj = req.body as UpdateConversationPriorityRequest; + + try { + const updateRes = await ConversationService.updateUserConversationPriority( + new ObjectId(conversationId), + new ObjectId(userInfo.userId), + reqObj.newState, + ); + + return res.json( + new NirvanaResponse( + undefined, + undefined, + updateRes?.ok ? 'updated' : 'failed to update conversation priority', + ), + ); + } catch (error) { + next(Error('unable to set conversation as priority')); + } +}; diff --git a/packages/api/services/conversation.service.ts b/packages/api/services/conversation.service.ts index 302feb3..07da3f7 100644 --- a/packages/api/services/conversation.service.ts +++ b/packages/api/services/conversation.service.ts @@ -1,4 +1,5 @@ -import Conversation from '@nirvana/core/models/conversation.model'; +import Conversation, { MemberState } from '@nirvana/core/models/conversation.model'; + import { ObjectId } from 'mongodb'; import { collections } from './database.service'; @@ -65,4 +66,16 @@ export default class ConversationService { return null; } + + static async updateUserConversationPriority( + conversationId: ObjectId, + memberId: ObjectId, + newState: MemberState, + ) { + const query = { _id: conversationId, 'members._id': memberId }; + + return await collections.conversations?.findOneAndUpdate(query, { + $set: { 'members.$.memberState': newState }, + }); + } } diff --git a/packages/core/models/conversation.model.ts b/packages/core/models/conversation.model.ts index 3eb5538..e8cddf7 100644 --- a/packages/core/models/conversation.model.ts +++ b/packages/core/models/conversation.model.ts @@ -39,16 +39,18 @@ export enum MemberRole { regular = 'regular', } -export enum MemberState { - priority = 'priority', - inbox = 'inbox', - // deleted = "deleted" -} +//! for some reason using this breaks frontend to need node modules +// export enum MemberState { +// priority = 'priority', +// inbox = 'inbox', +// // deleted = "deleted" +// } +export type MemberState = 'priority' | 'inbox'; export class ConversationMember { constructor( public role: MemberRole, - public memberState: 'priority' | 'inbox', + public memberState: MemberState, public joinedDate = new Date(), diff --git a/packages/core/requests/UpdateConversationPriority.request.ts b/packages/core/requests/UpdateConversationPriority.request.ts new file mode 100644 index 0000000..51f0f31 --- /dev/null +++ b/packages/core/requests/UpdateConversationPriority.request.ts @@ -0,0 +1,5 @@ +import { MemberState } from '../models/conversation.model'; + +export default class UpdateConversationPriorityRequest { + constructor(public newState: MemberState) {} +} diff --git a/packages/desktop/src/api/NirvanaApi.tsx b/packages/desktop/src/api/NirvanaApi.tsx index 230da3f..8cefee4 100644 --- a/packages/desktop/src/api/NirvanaApi.tsx +++ b/packages/desktop/src/api/NirvanaApi.tsx @@ -1,10 +1,11 @@ +import Conversation, { MemberState } from '@nirvana/core/models/conversation.model'; import NirvanaResponse, { INirvanaResponse } from '@nirvana/core/responses/nirvanaResponse'; import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios'; -import Conversation from '@nirvana/core/models/conversation.model'; import CreateConversationRequest from '@nirvana/core/requests/CreateConversationRequest.request'; import CreateConversationResponse from '@nirvana/core/responses/CreateConversationResponse.response'; import LoginResponse from '@nirvana/core/responses/login.response'; +import UpdateConversationPriorityRequest from '@nirvana/core/requests/UpdateConversationPriority.request'; import UserDetailsResponse from '@nirvana/core/responses/userDetails.response'; import UserSearchResponse from '@nirvana/core/responses/userSearch.response'; @@ -95,3 +96,15 @@ export async function getConversationById( ): Promise> { return await NirvanaApi.fetch(`/conversations/${conversationId}`, 'GET', true); } + +export async function updateConversationPriority( + conversationId: string, + newState: MemberState, +): Promise> { + return await NirvanaApi.fetch( + `/conversations/${conversationId}/priority`, + 'POST', + true, + new UpdateConversationPriorityRequest(newState), + ); +} diff --git a/packages/desktop/src/providers/ConversationProvider.tsx b/packages/desktop/src/providers/ConversationProvider.tsx index 5b65d7a..ead1dc2 100644 --- a/packages/desktop/src/providers/ConversationProvider.tsx +++ b/packages/desktop/src/providers/ConversationProvider.tsx @@ -23,6 +23,7 @@ import { createConversation, getConversationById, getConversations, + updateConversationPriority, } from '../api/NirvanaApi'; import { useAsyncFn, useEffectOnce, useKeyPressEvent, useUnmount } from 'react-use'; @@ -75,6 +76,8 @@ interface IConversationContext { handleStartConversation?: (otherUsers: User[], conversationName?: string) => Promise; handleEscape?: () => void; + + updateConversationPriority?: (conversationId: string, newState: MemberState) => Promise; } const ConversationContext = React.createContext({ @@ -397,6 +400,29 @@ export function ConversationProvider({ children }: { children: React.ReactNode } return [priorityConversations, inboxConversations]; }, [conversationMap, user]); + const updateConversationPriority = useCallback( + async (conversationId: string, newState: MemberState) => { + try { + await updateConversationPriority(conversationId, newState); + + setConversationMap((draft) => { + if (draft[conversationId]) { + const currUserMember = draft[conversationId].members.find( + (mem) => mem._id.toString() === user._id.toString(), + ); + + if (currUserMember) { + currUserMember.memberState = newState; + } + } + }); + } catch (error) { + toast.error('problem in pinning conversation'); + } + }, + [setConversationMap, user], + ); + const handleEscape = useCallback(() => { selectConversation(undefined); }, [selectConversation]); @@ -434,6 +460,7 @@ export function ConversationProvider({ children }: { children: React.ReactNode } priorityConversations: masterConversations[0], inboxConversations: masterConversations[1], handleEscape, + updateConversationPriority, }} > {children} diff --git a/packages/desktop/src/tree/FooterControls.tsx b/packages/desktop/src/tree/FooterControls.tsx index 29ba2a7..e782f1b 100644 --- a/packages/desktop/src/tree/FooterControls.tsx +++ b/packages/desktop/src/tree/FooterControls.tsx @@ -186,7 +186,7 @@ function OverlayConversation({ }) { const { user } = useAuth(); - const { selectConversation, handleEscape } = useConversations(); + const { selectConversation, handleEscape, updateConversationPriority } = useConversations(); const handleSelectConversation = useCallback(() => { if (!isSelected) { @@ -205,6 +205,14 @@ function OverlayConversation({ return false; }, [masterConversation, user]); + const handleToggleConversationZap = useCallback(() => { + if (isPriority) { + updateConversationPriority(masterConversation._id.toString(), 'inbox'); + return; + } + updateConversationPriority(masterConversation._id.toString(), 'priority'); + }, [updateConversationPriority, isPriority, masterConversation._id]); + return ( {isPriority ? : }