adding route for the getting of conversation details
This commit is contained in:
@@ -3,6 +3,7 @@ import express, { Application, Request, Response } from "express";
|
||||
import { NextFunction } from "express";
|
||||
import { connectToDatabase } from "./services/database.service";
|
||||
import cors from "cors";
|
||||
import getConversationRoutes from "./routes/conversation";
|
||||
import getSearchRoutes from "./routes/search";
|
||||
import getUserRoutes from "./routes/user";
|
||||
|
||||
@@ -21,6 +22,7 @@ app.get("/", (req: Request, res: Response) => {
|
||||
|
||||
app.use("/api/users", getUserRoutes());
|
||||
app.use("/api/search", getSearchRoutes());
|
||||
app.use("/api/conversations", getConversationRoutes());
|
||||
|
||||
app.listen(5000, () => console.log("Example app is listening on port 5000."));
|
||||
|
||||
|
||||
@@ -23,8 +23,9 @@ export const authCheck = async (
|
||||
const userId = ticket.getPayload()?.sub;
|
||||
const email = ticket.getPayload()?.email;
|
||||
|
||||
// used in subsequent handlers
|
||||
if (!userId) throw new Error("No google user Id found");
|
||||
|
||||
// used in subsequent handlers
|
||||
// todo: have to get our database id for the user instead of google's id
|
||||
res.locals.userId = userId;
|
||||
res.locals.email = email;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import express, { Application, Request, Response } from "express";
|
||||
|
||||
import { ContactService } from "../services/contact.service";
|
||||
import Content from "@nirvana/core/models/content.model";
|
||||
import GetConversationDetailsResponse from "@nirvana/core/responses/getConversationDetails.response";
|
||||
import { ObjectId } from "mongodb";
|
||||
import Relationship from "@nirvana/core/models/relationship.model";
|
||||
import { UserService } from "../services/user.service";
|
||||
import { authCheck } from "../middleware/auth";
|
||||
import { collections } from "../services/database.service";
|
||||
|
||||
export default function getConversationRoutes() {
|
||||
const router = express.Router();
|
||||
|
||||
router.use(express.json());
|
||||
|
||||
// get data for a one on one conversation
|
||||
router.get("/:otherUserGoogleUserId", authCheck, getConversationDetails);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
async function getConversationDetails(req: Request, res: Response) {
|
||||
try {
|
||||
// google user Id of current user
|
||||
const { userId } = res.locals;
|
||||
const { otherUserGoogleUserId } = req.params;
|
||||
|
||||
// get the "other" user's details
|
||||
|
||||
const otherUser = await UserService.getUserByGoogleId(
|
||||
otherUserGoogleUserId
|
||||
);
|
||||
|
||||
if (!otherUser) {
|
||||
res.status(404).send("No such user found");
|
||||
return;
|
||||
}
|
||||
|
||||
// find the relationship between us...if not there just null
|
||||
const ourRelationship = await ContactService.getRelationship(
|
||||
userId,
|
||||
otherUserGoogleUserId
|
||||
);
|
||||
|
||||
// todo: get latest content based on limit count
|
||||
const latestContent: Content[] = [];
|
||||
|
||||
const resObj = new GetConversationDetailsResponse(
|
||||
otherUser,
|
||||
false,
|
||||
ourRelationship ?? undefined,
|
||||
latestContent
|
||||
);
|
||||
|
||||
res.status(200).send(resObj);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
res.status(500).send(`something went wrong`);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ async function getUserDetails(req: Request, res: Response) {
|
||||
|
||||
try {
|
||||
// return user details if it passed auth middleware
|
||||
const user = await UserService.getUserById(userId);
|
||||
const user = await UserService.getUserByGoogleId(userId);
|
||||
|
||||
// if no user found, then go ahead and create user
|
||||
if (!user) {
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import Relationship from "@nirvana/core/models/relationship.model";
|
||||
import { collections } from "./database.service";
|
||||
|
||||
export class ContactService {
|
||||
static async getRelationship(
|
||||
myGoogleUserId: string,
|
||||
otherGoogleUserId: string
|
||||
) {
|
||||
// i am the sender and they are the receiver
|
||||
const clauseOne = {
|
||||
$and: [
|
||||
{ senderUserId: myGoogleUserId },
|
||||
{ receiverUserId: otherGoogleUserId },
|
||||
],
|
||||
};
|
||||
// i could also be the receiver and them the sender
|
||||
const clauseTwo = {
|
||||
$and: [
|
||||
{ senderUserId: myGoogleUserId },
|
||||
{ receiverUserId: otherGoogleUserId },
|
||||
],
|
||||
};
|
||||
|
||||
const query = { $or: [clauseOne, clauseTwo] };
|
||||
const res = await collections.users?.findOne(query);
|
||||
|
||||
// exists
|
||||
if (res?._id) {
|
||||
return res as Relationship;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export class ConversationService {
|
||||
// static async getUserById(userId: string) {
|
||||
// const query = { googleId: userId };
|
||||
// const res = await collections.users?.findOne(query);
|
||||
// // exists
|
||||
// if (res?._id) {
|
||||
// return res as User;
|
||||
// }
|
||||
// return null;
|
||||
// }
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import axios from "axios";
|
||||
import { collections } from "./database.service";
|
||||
|
||||
export class UserService {
|
||||
static async getUserById(userId: string) {
|
||||
static async getUserByGoogleId(userId: string) {
|
||||
const query = { googleId: userId };
|
||||
|
||||
const res = await collections.users?.findOne(query);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
export default class Content {
|
||||
constructor(
|
||||
public relationshipId: string, // if it's a one on one, this will be the relationship Id
|
||||
public sentDate: Date,
|
||||
public contentUrl: string,
|
||||
public contentData: string,
|
||||
public contentType: ContentType,
|
||||
public _id?: ObjectId,
|
||||
public listenedDate?: Date
|
||||
) {}
|
||||
}
|
||||
|
||||
export enum ContentType {
|
||||
link = "LINK",
|
||||
audioClip = "AUDIO_CLIP",
|
||||
text = "TEXT",
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import { ObjectId } from "mongodb";
|
||||
|
||||
export default class Relationship {
|
||||
constructor(
|
||||
public senderUserId: string,
|
||||
public senderUserId: string, // sender as the one to initiate the relationship
|
||||
public receiverUserId: string,
|
||||
public state: RelationshipState,
|
||||
public createdDate: Date = new Date(),
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import Content from "../models/content.model";
|
||||
import Relationship from "../models/relationship.model";
|
||||
import { User } from "@nirvana/core/models";
|
||||
|
||||
export default class GetConversationDetailsResponse {
|
||||
// get the messages for this conversation for like last 7 days or just a count of them until we paginate?
|
||||
|
||||
constructor(
|
||||
public contactUser: User,
|
||||
isLiveOrPinned: boolean,
|
||||
public ourRelationship?: Relationship,
|
||||
public latestContent?: Content[]
|
||||
) {}
|
||||
}
|
||||
@@ -45,6 +45,7 @@ export function useGetUserDetails() {
|
||||
() => getUserDetails(authTokens?.accessToken, authTokens?.idToken),
|
||||
{
|
||||
retry: false,
|
||||
refetchOnWindowFocus: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,3 +18,9 @@ export const $authFailureCount = atom<number>({
|
||||
key: "AUTH_FAILURE_COUNT",
|
||||
default: 0,
|
||||
});
|
||||
|
||||
// google Id of the selected person/conversation
|
||||
export const $selectedConversation = atom<string>({
|
||||
key: "SELECTED_CONVERSATION",
|
||||
default: null,
|
||||
});
|
||||
|
||||
@@ -7,6 +7,15 @@ import {
|
||||
} from "@mui/icons-material";
|
||||
|
||||
export default function Conversations() {
|
||||
/** Data we need: have this huge data store client side to be able to access
|
||||
* for now, need a simple list of contacts
|
||||
* - first list is the live/pinned
|
||||
* - second list is the pending invites, out going or incoming? or this on a separate page?
|
||||
* - third list is just the rest of my contacts
|
||||
*
|
||||
* web sockets for all of my active contacts
|
||||
* - actually play messages if pinned contact for me
|
||||
*/
|
||||
return (
|
||||
<>
|
||||
{/* actions navbar */}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { $searchQuery } from "../../../controller/recoil";
|
||||
import { FaPaperPlane } from "react-icons/fa";
|
||||
import {
|
||||
$searchQuery,
|
||||
$selectedConversation,
|
||||
} from "../../../controller/recoil";
|
||||
|
||||
import { FaAngleRight } from "react-icons/fa";
|
||||
import { Tooltip } from "@mui/material";
|
||||
import { User } from "@nirvana/core/models";
|
||||
import { useEffect } from "react";
|
||||
@@ -14,6 +18,10 @@ import { useSearch } from "../../../controller";
|
||||
export default function Search() {
|
||||
const [searchQuery, setSearchQuery] = useRecoilState($searchQuery);
|
||||
|
||||
const [selectedConvo, setSelectedConvo] = useRecoilState(
|
||||
$selectedConversation
|
||||
);
|
||||
|
||||
const { data, isLoading, isError, refetch } = useSearch();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -33,11 +41,22 @@ export default function Search() {
|
||||
setSearchQuery("");
|
||||
};
|
||||
|
||||
const selectContact = async (googleUserId: string) => {
|
||||
// if this person is already selected, unselect
|
||||
if (selectedConvo === googleUserId) {
|
||||
setSelectedConvo(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedConvo(googleUserId);
|
||||
};
|
||||
|
||||
const renderUserRow = (user: User) => {
|
||||
return (
|
||||
<span
|
||||
className="border-t border-t-slate-400 py-5 flex flex-row justify-start
|
||||
items-center w-full hover:bg-slate-600 cursor-pointer"
|
||||
onClick={() => selectContact(user.googleId)}
|
||||
className="border-t border-t-slate-500 py-5 flex flex-row justify-start
|
||||
items-center w-full hover:bg-slate-600 cursor-pointer group"
|
||||
>
|
||||
<span className="relative mx-5">
|
||||
<img
|
||||
@@ -59,7 +78,7 @@ export default function Search() {
|
||||
{/* actions */}
|
||||
<Tooltip title="Request connect">
|
||||
<button className="hover:bg-slate-300 p-1 flex flex-row items-center justify-center ml-auto">
|
||||
<FaPaperPlane className="text-emerald-500 text-lg" />
|
||||
<FaAngleRight className="group-hover:text-slate-300 text-lg" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { $selectedConversation } from "../../../controller/recoil";
|
||||
import { useRecoilState } from "recoil";
|
||||
|
||||
export default function SelectedConversation() {
|
||||
const [selectedConvo, setSelectedConvo] = useRecoilState(
|
||||
$selectedConversation
|
||||
);
|
||||
|
||||
// want to grab the right data based on the selected contact
|
||||
|
||||
/** Data we need:
|
||||
* user details of the selected person...should be in the search results client side, but just do another fetch?
|
||||
* websocket for the status only if there exists a relationship?
|
||||
* ability to see my relationship with this user...whether null, pending, active, etc.
|
||||
* all messages between me and them
|
||||
*/
|
||||
|
||||
return <span>this is the selected conversation</span>;
|
||||
}
|
||||
Reference in New Issue
Block a user