Skip to content

Commit d681c9a

Browse files
committed
publish
0 parents  commit d681c9a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+9693
-0
lines changed

.eslintrc.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": [
3+
"next/core-web-vitals",
4+
"standard",
5+
"plugin:tailwindcss/recommended",
6+
"prettier"
7+
]
8+
}

.gitignore

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.js
7+
.yarn/install-state.gz
8+
9+
# testing
10+
/coverage
11+
12+
# next.js
13+
/.next/
14+
/out/
15+
16+
# production
17+
/build
18+
19+
# misc
20+
.DS_Store
21+
*.pem
22+
23+
# debug
24+
npm-debug.log*
25+
yarn-debug.log*
26+
yarn-error.log*
27+
28+
# local env files
29+
.env*.local
30+
31+
# vercel
32+
.vercel
33+
34+
# typescript
35+
*.tsbuildinfo
36+
next-env.d.ts
37+
38+
.vscode
39+
40+
.env

.prettierrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"singleQuote": true
3+
}

README.md

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<a name="readme-top"></a>
2+
3+
# Room - A Video & Audio Meeting Platform
4+
5+
![image](public/Preview.png)
6+
7+
## 📋 <a name="table">Table of Contents</a>
8+
9+
1. [Introduction](#introduction)
10+
2. [Tech Stack](#tech-stack)
11+
3. [Features](#features)
12+
4. [Getting Started](#getting-started)
13+
5. [Assets & Code](#snippets)
14+
6. [More](#more)
15+
16+
## <a name="introduction">Introduction</a>
17+
18+
Built with the latest Next.js and TypeScript, this project replicates Zoom, a widely used video conferencing tool. It enables users to securely log in, create meetings and access various meeting functionalities such as recording, screen sharing, and managing participants.
19+
20+
## <a name="tech-stack">Tech Stack</a>
21+
22+
- Next.js
23+
- TypeScript
24+
- Clerk
25+
- getstream
26+
- shadcn
27+
- Tailwind CSS
28+
29+
## <a name="features">Features</a>
30+
31+
**Authentication**: Implements authentication and authorization features using Clerk, allowing users to securely log in via social sign-on or traditional email and password methods, while ensuring appropriate access levels and permissions within the platform.
32+
33+
**New Meeting**: Quickly start a new meeting, configuring camera and microphone settings before joining.
34+
35+
**Meeting Controls**: Participants have full control over meeting aspects, including recording, emoji reactions, screen sharing, muting/unmuting, sound adjustments, grid layout, participant list view, and individual participant management (pinning, muting, unmuting, blocking, allowing video share).
36+
37+
**Exit Meeting**: Participants can leave a meeting, or creators can end it for all attendees.
38+
39+
**Schedule Future Meetings**: Input meeting details (date, time) to schedule future meetings, accessible on the 'Upcoming Meetings' page for sharing the link or immediate start.
40+
41+
**Past Meetings List**: Access a list of previously held meetings, including details and metadata.
42+
43+
**View Recorded Meetings**: Access recordings of past meetings for review or reference.
44+
45+
**Personal Room**: Users have a personal room with a unique meeting link for instant meetings, shareable with others.
46+
47+
**Join Meetings via Link**: Easily join meetings created by others by providing a link.
48+
49+
**Secure Real-time Functionality**: All interactions within the platform are secure and occur in real-time, maintaining user privacy and data integrity.
50+
51+
**Responsive Design**: Follows responsive design principles to ensure optimal user experience across devices, adapting seamlessly to different screen sizes and resolutions.
52+
53+
and many more, including code architecture and reusability.
54+
55+
## <a name="getting-started">Getting Started</a>
56+
57+
**Make sure you have the following installed on your machine**
58+
59+
- [Git](https://git-scm.com/)
60+
- [Node.js](https://nodejs.org/en)
61+
- [npm](https://www.npmjs.com/) (Node Package Manager)
62+
63+
**Cloning the Repository**
64+
65+
```bash
66+
git clone https://github.com/theankushshah/room.git
67+
cd zoom-clone
68+
```
69+
70+
**Installation**
71+
72+
Install the project dependencies using npm:
73+
74+
```bash
75+
npm install
76+
```
77+
78+
**Set Up Environment Variables**
79+
80+
Create a new file named `.env` in the root of your project and add the following content:
81+
82+
```env
83+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
84+
CLERK_SECRET_KEY=
85+
86+
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
87+
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
88+
89+
NEXT_PUBLIC_STREAM_API_KEY=
90+
STREAM_SECRET_KEY=
91+
```
92+
93+
Replace the placeholder values with your actual Clerk & getstream credentials. You can obtain these credentials by signing up on the [Clerk website](https://clerk.com/) and [getstream website](https://getstream.io/)
94+
95+
**Running the Project**
96+
97+
```bash
98+
npm run dev
99+
```
100+
101+
Open [http://localhost:3000](http://localhost:3000) in your browser to view the project.
102+
103+
## More Screenshots
104+
105+
![image](public/up.png)
106+
107+
![image](public/past.png)
108+
109+
![image](public/record.png)
110+
111+
![image](public/link.png)
112+
113+
## Feedback
114+
115+
You might encounter some bugs while using this app. You are more than welcome to contribute. Just submit changes via pull request and I will review them before merging. Make sure you follow community guidelines.
116+
117+
## Developer & Main
118+
119+
- Ankush Shah ([email protected]) (Main)
120+
121+
Happy coding! 🚀

actions/stream.actions.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
'use server';
2+
3+
import { currentUser } from '@clerk/nextjs/server';
4+
import { StreamClient } from '@stream-io/node-sdk';
5+
6+
const STREAM_API_KEY = process.env.NEXT_PUBLIC_STREAM_API_KEY;
7+
const STREAM_API_SECRET = process.env.STREAM_SECRET_KEY;
8+
9+
export const tokenProvider = async () => {
10+
const user = await currentUser();
11+
12+
if (!user) throw new Error('User is not authenticated');
13+
if (!STREAM_API_KEY) throw new Error('Stream API key secret is missing');
14+
if (!STREAM_API_SECRET) throw new Error('Stream API secret is missing');
15+
16+
const streamClient = new StreamClient(STREAM_API_KEY, STREAM_API_SECRET);
17+
18+
const expirationTime = Math.floor(Date.now() / 1000) + 3600;
19+
const issuedAt = Math.floor(Date.now() / 1000) - 60;
20+
21+
const token = streamClient.createToken(user.id, expirationTime, issuedAt);
22+
23+
return token;
24+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { SignIn } from '@clerk/nextjs';
2+
3+
export default function SiginInPage() {
4+
return (
5+
<main className="flex h-screen w-full items-center justify-center">
6+
<SignIn />
7+
</main>
8+
);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { SignUp } from '@clerk/nextjs';
2+
3+
export default function SignUpPage() {
4+
return (
5+
<main className="flex h-screen w-full items-center justify-center">
6+
<SignUp />
7+
</main>
8+
);
9+
}

app/(root)/(home)/layout.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Metadata } from 'next';
2+
import { ReactNode } from 'react';
3+
4+
import Navbar from '@/components/Navbar';
5+
import Sidebar from '@/components/Sidebar';
6+
7+
export const metadata: Metadata = {
8+
title: 'Room',
9+
description: 'A workspace for your team, powered by Stream Chat and Clerk.',
10+
};
11+
12+
const RootLayout = ({ children }: Readonly<{children: ReactNode}>) => {
13+
return (
14+
<main className="relative">
15+
<Navbar />
16+
17+
<div className="flex">
18+
<Sidebar />
19+
20+
<section className="flex min-h-screen flex-1 flex-col px-6 pb-6 pt-28 max-md:pb-14 sm:px-14">
21+
<div className="w-full">{children}</div>
22+
</section>
23+
</div>
24+
</main>
25+
);
26+
};
27+
28+
export default RootLayout;

app/(root)/(home)/page.tsx

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import MeetingTypeList from '@/components/MeetingTypeList';
2+
3+
const Home = () => {
4+
const now = new Date();
5+
6+
const time = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
7+
const date = (new Intl.DateTimeFormat('en-US', { dateStyle: 'full' })).format(now);
8+
9+
return (
10+
<section className="flex size-full flex-col gap-5 text-white">
11+
<div className="h-[303px] w-full rounded-[20px] bg-hero bg-cover">
12+
<div className="flex h-full flex-col justify-between max-md:px-5 max-md:py-8 lg:p-11">
13+
<h2 className="glassmorphism max-w-[273px] rounded py-2 text-center text-base font-normal">
14+
Built with ❤️ by Ankush shah
15+
</h2>
16+
<div className="flex flex-col gap-2">
17+
<h1 className="text-4xl font-extrabold lg:text-7xl">{time}</h1>
18+
<p className="text-lg font-medium text-sky-1 lg:text-2xl">{date}</p>
19+
</div>
20+
</div>
21+
</div>
22+
23+
<MeetingTypeList />
24+
</section>
25+
);
26+
};
27+
28+
export default Home;
+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"use client";
2+
3+
import { useUser } from "@clerk/nextjs";
4+
import { useStreamVideoClient } from "@stream-io/video-react-sdk";
5+
import { useRouter } from "next/navigation";
6+
7+
import { useGetCallById } from "@/hooks/useGetCallById";
8+
import { Button } from "@/components/ui/button";
9+
import { useToast } from "@/components/ui/use-toast";
10+
11+
const Table = ({
12+
title,
13+
description,
14+
}: {
15+
title: string;
16+
description: string;
17+
}) => {
18+
return (
19+
<div className="flex flex-col items-start gap-2 xl:flex-row">
20+
<h1 className="text-base font-medium text-sky-1 lg:text-xl xl:min-w-32">
21+
{title}:
22+
</h1>
23+
<h1 className="truncate text-sm font-bold max-sm:max-w-[320px] lg:text-xl">
24+
{description}
25+
</h1>
26+
</div>
27+
);
28+
};
29+
30+
const PersonalRoom = () => {
31+
const router = useRouter();
32+
const { user } = useUser();
33+
const client = useStreamVideoClient();
34+
const { toast } = useToast();
35+
36+
const meetingId = user?.id;
37+
38+
const { call } = useGetCallById(meetingId!);
39+
40+
const startRoom = async () => {
41+
if (!client || !user) return;
42+
43+
const newCall = client.call("default", meetingId!);
44+
45+
if (!call) {
46+
await newCall.getOrCreate({
47+
data: {
48+
starts_at: new Date().toISOString(),
49+
},
50+
});
51+
}
52+
53+
router.push(`/meeting/${meetingId}?personal=true`);
54+
};
55+
56+
const meetingLink = `${process.env.NEXT_PUBLIC_BASE_URL}/meeting/${meetingId}?personal=true`;
57+
58+
return (
59+
<section className="flex size-full flex-col gap-10 text-white">
60+
<h1 className="text-xl font-bold lg:text-3xl">Personal Meeting Room</h1>
61+
<div className="flex w-full flex-col gap-8 xl:max-w-[900px]">
62+
<Table title="Topic" description={`${user?.username}'s Meeting Room`} />
63+
<Table title="Meeting ID" description={meetingId!} />
64+
<Table title="Invite Link" description={meetingLink} />
65+
</div>
66+
<div className="flex gap-5">
67+
<Button className="bg-blue-1" onClick={startRoom}>
68+
Start Meeting
69+
</Button>
70+
<Button
71+
className="bg-dark-3"
72+
onClick={() => {
73+
navigator.clipboard.writeText(meetingLink);
74+
toast({
75+
title: "Link Copied",
76+
});
77+
}}
78+
>
79+
Copy Invitation
80+
</Button>
81+
</div>
82+
</section>
83+
);
84+
};
85+
86+
export default PersonalRoom;

app/(root)/(home)/previous/page.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import CallList from "@/components/CallList";
2+
3+
const PreviousPage = () => {
4+
return (
5+
<section className="flex size-full flex-col gap-10 text-white">
6+
<h1 className="text-3xl font-bold">Previous Calls</h1>
7+
8+
<CallList type="ended" />
9+
</section>
10+
);
11+
};
12+
13+
export default PreviousPage;

app/(root)/(home)/recordings/page.tsx

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import CallList from '@/components/CallList';
2+
3+
const PreviousPage = () => {
4+
return (
5+
<section className="flex size-full flex-col gap-10 text-white">
6+
<h1 className="text-3xl font-bold">Recordings</h1>
7+
8+
<CallList type="recordings" />
9+
</section>
10+
);
11+
};
12+
13+
export default PreviousPage;

0 commit comments

Comments
 (0)