-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CIT-28] Allow users to message one another (#51)
* [CIT-28] Make messaging API related changes * [CIT-28] Get Ably demo working in prod * [CIT-28] Begin adding code to persist messages * [CIT-28] Group creation, message persistence * [CIT-28] API rewrite into library functions * [CIT-28] Intermediate commit * [CIT-28] Cleanup, make groups secure * [CIT-28] Cleanup * [CIT-28] Cleanup, let users follow each other * CIT-28: add link for chats --------- Co-authored-by: Daniel <[email protected]>
- Loading branch information
1 parent
c5dd676
commit cca3075
Showing
25 changed files
with
1,855 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,4 +38,7 @@ citrus/next-env.d.ts | |
|
||
#data | ||
cockroach-data/ | ||
node_modules/ | ||
node_modules/ | ||
|
||
#VS Code files | ||
*.code-workspace |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"use client" | ||
import AblyChatComponent from "@/components/AblyChatComponent" | ||
|
||
export default function ChatContainer(params) { | ||
return ( | ||
<div className="container"> | ||
|
||
<main> | ||
<h1 className="title">{params.groupName}</h1> | ||
<AblyChatComponent channelID={params.groupID} /> | ||
</main> | ||
|
||
<style jsx>{` | ||
.container { | ||
display: grid; | ||
grid-template-rows: 1fr 100px; | ||
min-height: 100vh; | ||
background-color: #eee; | ||
max-width: 100vw; | ||
} | ||
main { | ||
display: grid; | ||
grid-template-rows: auto 1fr; | ||
width: calc(100% - 40px); | ||
max-width: 900px; | ||
margin: 20px auto; | ||
border-radius: 10px; | ||
overflow: hidden; | ||
box-shadow: 0px 3px 10px 1px rgba(0,0,0,0.2); | ||
background-color: white; | ||
} | ||
.title { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 100px; | ||
margin: 0; | ||
color: white; | ||
background: #005C97; | ||
background: -webkit-linear-gradient(to right, #363795, #005C97); | ||
background: linear-gradient(to right, #363795, #005C97); | ||
} | ||
.logo { | ||
display: block; | ||
height: 20px; | ||
margin: 0.5em; | ||
} | ||
.svg { | ||
fill:#005C97; | ||
color:#fff; | ||
position: absolute; | ||
top: 0; | ||
border: 0; | ||
right: 0; | ||
} | ||
@media (min-width: 600px) { | ||
.logo { | ||
height: 40px; | ||
margin: 1em; | ||
} | ||
.ably { | ||
height: 60px; | ||
} | ||
} | ||
`}</style> | ||
|
||
<style jsx global>{` | ||
html, | ||
body { | ||
padding: 0; | ||
margin: 0; | ||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, | ||
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, | ||
sans-serif; | ||
} | ||
* { | ||
box-sizing: border-box; | ||
} | ||
[data-author="me"] { | ||
background: linear-gradient(to right, #363795, #005C97); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ | ||
color: white; | ||
align-self: flex-end; | ||
border-bottom-right-radius: 0!important; | ||
border-bottom-left-radius: 10px!important; | ||
} | ||
`}</style> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function Page() { | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import ChatContainer from './JSXStyleComponent'; | ||
import { getServerSession } from 'next-auth'; | ||
import { authOptions } from '@/app/api/auth/[...nextauth]/route'; | ||
import { isUserInGroup, getGroup } from '@/lib/messages'; | ||
export const dynamic = "force-dynamic" | ||
|
||
export default async function Home({ params }: { params: { group: string } }) { | ||
const groupID = params.group; | ||
const session = await getServerSession(authOptions); | ||
|
||
if (!session || !session.user) { | ||
return <div>loading...</div> | ||
} | ||
|
||
const inGroup = await isUserInGroup(session.user.name as string, groupID) | ||
if (!inGroup) { | ||
return <div>loading...</div> | ||
} | ||
|
||
const group = await getGroup(groupID); | ||
|
||
return ( | ||
<ChatContainer groupID={groupID} groupName={group.name}/> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import GroupChatHolder from "@/components/GroupChatHolder" | ||
import { use } from "react"; | ||
import { getGroupsForUser } from "@/lib/messages" | ||
export const dynamic = "force-dynamic" | ||
|
||
function GroupCard({ group }: { group: any }) { | ||
return ( | ||
<li className="bg-blue-600 p-2 rounded-lg my-2 border-white border-2"> | ||
<a href={`/groups/${group.id}`}>{group.name}</a> | ||
</li> | ||
) | ||
} | ||
|
||
export default function Page() { | ||
const groups = use(getGroupsForUser()) | ||
if (groups === "Unauthorized") { | ||
return <div>loading...</div> | ||
} | ||
return ( | ||
<div className="text-center"> | ||
<h1 className="text-3xl mx-auto">Groups</h1> | ||
<div className='flex flex-row flex-wrap'> | ||
<ul className="mx-auto"> | ||
{groups.map((group: any) => <GroupCard group={group} key={group.id}/>)} | ||
</ul> | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import Ably from "ably/promises"; | ||
import { NextResponse } from "next/server"; | ||
import * as db from "@/lib/db" | ||
import "@/lib/patch" | ||
import { getServerSession } from "next-auth"; | ||
import { authOptions } from "@/app/api/auth/[...nextauth]/route"; | ||
|
||
const prisma = db.getClient(); | ||
|
||
export async function POST(request: Request) { | ||
const session = await getServerSession(authOptions); | ||
const body = await request.json(); | ||
|
||
if (!session || !session.user) { | ||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); | ||
} | ||
|
||
try { | ||
const follows = await prisma.Follows.create({ | ||
data: { | ||
follower_username: session.user.name, | ||
following_username: body.username | ||
} | ||
}) | ||
|
||
const group = await prisma.Group.create({ | ||
data: { | ||
name: `${session.user.name} & ${body.username}`, | ||
users: { | ||
connect: [ | ||
{ username: session.user.name }, | ||
{ username: body.username } | ||
] | ||
} | ||
} | ||
}) | ||
return NextResponse.json( | ||
{ follows, group, success: true }, | ||
{ status: 200 } | ||
) | ||
} catch (e) { | ||
return db.handleError(e); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import Ably from "ably/promises"; | ||
import { NextResponse } from "next/server"; | ||
|
||
export async function GET(request: Request) { | ||
const { searchParams } = new URL(request.url); | ||
const clientId = searchParams.get("clientId"); | ||
if (!clientId) return NextResponse.json({ error: "clientId is required" }, { status: 400 }) | ||
|
||
const client = new Ably.Realtime(process.env.ABLY_API_KEY as string); | ||
const tokenRequestData = await client.auth.createTokenRequest({ clientId: clientId}); | ||
return NextResponse.json(tokenRequestData); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import * as db from "@/lib/db" | ||
import { getServerSession } from "next-auth"; | ||
import { NextResponse } from "next/server"; | ||
import { authOptions } from "@/app/api/auth/[...nextauth]/route"; | ||
|
||
const prisma = db.getClient(); | ||
|
||
// Return messages in a group 200 at a time, starting from the most recent | ||
export async function GET(request:Request, { params }: { params: { id: string } }) { | ||
const session = await getServerSession(authOptions); | ||
const { searchParams } = new URL(request.url) | ||
const prevCursor = searchParams.get("cursor") | ||
const nextCursor = searchParams.get("cursor") | ||
var take = 200 | ||
var cursor = undefined | ||
var skip = 1 | ||
|
||
if (prevCursor) { | ||
cursor = { | ||
id: prevCursor | ||
} | ||
take = -200 | ||
} else if (nextCursor) { | ||
cursor = { | ||
id: nextCursor | ||
} | ||
} else { | ||
skip = 0 | ||
} | ||
|
||
if (!session || !session.user) { | ||
return NextResponse.json("Unauthorized", { status: 401 }) | ||
} | ||
|
||
try { | ||
const messages = await prisma.Message.findMany({ | ||
where: { | ||
group_id: params.id | ||
}, | ||
orderBy: { | ||
created_at: "desc" | ||
}, | ||
take: take, | ||
skip: skip, | ||
cursor: cursor | ||
}) | ||
|
||
const nextCursor = messages[messages.length - 1]?.id | ||
const prevCursor = messages[0]?.id | ||
|
||
return NextResponse.json( | ||
{ messages: messages, | ||
nextCursor: nextCursor, | ||
prevCursor: prevCursor }, | ||
{ status: 200 } | ||
) | ||
} catch(e) { | ||
return db.handleError(e) | ||
} | ||
} | ||
|
||
export async function POST(request: Request, | ||
{ params }: { params: { id: string } }) { | ||
const body = await request.json(); | ||
const { text } = body; | ||
const session = await getServerSession(authOptions); | ||
|
||
if (!session || !session.user) { | ||
return NextResponse.json("Unauthorized", { status: 401 }) | ||
} | ||
|
||
try { | ||
await prisma.Message.create({ | ||
data: { | ||
text: text, | ||
user_id: session.user?.name, | ||
group_id: params.id | ||
} | ||
}) | ||
} catch (e) { | ||
return db.handleError(e) | ||
} | ||
|
||
return NextResponse.json( | ||
{ message: "Message sent" }, | ||
{ status: 200 } | ||
) | ||
} |
Oops, something went wrong.
cca3075
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
final-project-s23-citrus – ./
final-project-s23-citrus.vercel.app
final-project-s23-citrus-git-main-ishaan-upadhyay.vercel.app
final-project-s23-citrus-ishaan-upadhyay.vercel.app