Skip to content

Commit

Permalink
Merge pull request #16 from adityathakurxd/multiple-rooms
Browse files Browse the repository at this point in the history
Creates new room code before join for participants
  • Loading branch information
adityathakurxd authored Mar 4, 2024
2 parents b46b6ca + d603882 commit bf0ac51
Show file tree
Hide file tree
Showing 7 changed files with 218 additions and 90 deletions.
52 changes: 52 additions & 0 deletions app/api/create/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { randomUUID } from 'crypto'
import { NextRequest, NextResponse } from 'next/server'

export async function GET() {
try {
const response = await fetch('https://api.100ms.live/v2/rooms', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.MANAGEMENT_TOKEN}`,
},
body: JSON.stringify({
name: `polls-ai-${randomUUID()}`,
description: 'This is a sample description for the room',
template_id: process.env.TEMPLATE_ID,
}),
})

if (response.ok) {
const body = await response.json()
return NextResponse.json({ body }, { status: 200 })
} else {
return NextResponse.json({ message: 'Failed to create room' }, { status: 401 })
}
} catch (error) {
console.error('Error creating room:', error)
return NextResponse.json({ message: 'Internal Server error' }, { status: 500 })
}
}

export async function POST(req: NextRequest) {
try {
const { roomId } = await req.json()
const response = await fetch('https://api.100ms.live/v2/room-codes/room/' + roomId, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.MANAGEMENT_TOKEN}`,
},
})

if (response.ok) {
const body = await response.json()
return NextResponse.json({ body }, { status: 200 })
} else {
return NextResponse.json({ message: 'Failed to create room codes' }, { status: 401 })
}
} catch (error) {
console.error('Error creating room codes:', error)
return NextResponse.json({ message: 'Internal Server error' }, { status: 500 })
}
}
8 changes: 7 additions & 1 deletion app/components/Conference.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import { useState } from 'react'
import { Pagination } from './Pagination'
import { LiveResults } from './LiveResults'
import Footer from './Footer'
import CopyButton from './CopyButton'

const HOST_URL = 'wss://demo-yjs-server-production.up.railway.app'

function Conference() {
const peers = useHMSStore(selectPeers)
const roomCode = localStorage.getItem('roomCode')

const store = useYjsStore({
roomId: 'example17',
roomId: roomCode,
hostUrl: HOST_URL,
})

Expand All @@ -42,6 +44,10 @@ function Conference() {
) : null}
</div>
<LiveResults />
<div className="invite-banner">
<div className="invite-text">COPY INVITE LINK</div>
<CopyButton value={`make-real-polls.vercel.app/?room=${roomCode}`} />
</div>
<Footer />
</div>
<div className="editor">
Expand Down
24 changes: 24 additions & 0 deletions app/components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CheckIcon, CopyIcon } from '@100mslive/react-icons'
import { useState } from 'react'

const CopyButton = ({ value }: { value: string }) => {
const [copied, setCopied] = useState(false)

const copyText = () => {
navigator.clipboard.writeText(value)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}

return (
<button
title="Copy"
style={{ padding: '4px', display: 'flex' }}
disabled={copied}
onClick={copyText}
>
{copied ? <CheckIcon height={16} width={16} /> : <CopyIcon height={16} width={16} />}
</button>
)
}
export default CopyButton
186 changes: 101 additions & 85 deletions app/components/JoinForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,87 @@
import { ArrowRightIcon, Svg100MsLogoIcon } from '@100mslive/react-icons'
import { useHMSActions } from '@100mslive/react-sdk'
import Image from 'next/image'
import { useState, ChangeEvent, FormEvent, useEffect } from 'react'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

// Define a type for your state
type InputValues = {
name: string
roomCode: string // Assuming roomCode is optional
}
import { useState, FormEvent, useRef } from 'react'
import { useSearchParams } from 'next/navigation'
import { ROLES } from './constants'
import { useToasts } from '@tldraw/tldraw'

const JoinForm = () => {
const [activeTabRole, setActiveTabRole] = useState('teacher')
const [activeTabRole, setActiveTabRole] = useState(ROLES.TEACHER)
const hmsActions = useHMSActions()

const handleTabClick = (tab) => {
setActiveTabRole(tab)
}

const [inputValues, setInputValues] = useState<InputValues>({
name: '',
roomCode: '',
})

// Type the event parameter
const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setInputValues((prevValues) => ({
...prevValues,
[e.target.name]: e.target.value,
}))
}
const searchParams = useSearchParams()
const roomCodeParam = searchParams.get('room') || ''
const inputRef = useRef<HTMLInputElement>()
const { addToast } = useToasts()

// Type the event parameter
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()

const { name: userName = '', roomCode = '' } = inputValues

try {
var authToken = ''
if (activeTabRole === 'teacher') {
authToken = await hmsActions.getAuthTokenByRoomCode({ roomCode: 'afu-oanp-nlh' })
} else {
authToken = await hmsActions.getAuthTokenByRoomCode({ roomCode: 'dsv-tdwb-mqk' })
if (roomCodeParam) {
try {
localStorage.setItem('roomCode', roomCodeParam)
const authToken = await hmsActions.getAuthTokenByRoomCode({ roomCode: roomCodeParam })
await hmsActions.join({ userName: inputRef.current.value, authToken })
} catch (e) {
console.error(e)
addToast({
icon: 'cross-2',
title: 'Room Code might be invalid or expired. Please try again.',
})
localStorage.setItem('roomCode', undefined)
}
} else {
try {
const response = await fetch('/api/create', {
method: 'GET',
})

const hmsRoomsAPIResponse = await response.json()

if (hmsRoomsAPIResponse) {
const roomId = hmsRoomsAPIResponse.body.id

const res = await fetch('/api/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ roomId }),
})

const hmsRoomCodesAPIResponse = await res.json()
const data = hmsRoomCodesAPIResponse.body.data

if (data.length >= 2) {
const roomCodeForStudent = data[0].code
const roomCodeForTeacher = data[1].code
localStorage.setItem('roomCode', roomCodeForTeacher)
const authToken = await hmsActions.getAuthTokenByRoomCode({
roomCode: activeTabRole === ROLES.TEACHER ? roomCodeForTeacher : roomCodeForStudent,
})
try {
await hmsActions.join({ userName: inputRef.current.value, authToken })
} catch (e) {
addToast({
icon: 'cross-2',
title: 'Failed to join the room',
})
setTimeout(() => window.location.reload(), 2000)
}
}
} else {
addToast({
icon: 'cross-2',
title: 'Failed to create a new room',
})
}
} catch (e) {
addToast({
icon: 'cross-2',
title: 'Failed to join room',
})
}

await hmsActions.join({ userName, authToken })
} catch (e) {
console.error(e)
}
}

Expand All @@ -69,66 +101,50 @@ const JoinForm = () => {
click on create poll. That’s it.
</p>
</div>
{roomCodeParam ? (
<div className="bottom-banner">
<div className="bottom-notif body-regular-text">{"You've been invited!"}</div>
</div>
) : null}
<form onSubmit={handleSubmit}>
<div className="input-container">
<div className="input-label">Your Name</div>
<input
required
id="name"
type="text"
name="name"
value={inputValues.name}
onChange={handleInputChange}
placeholder="Name"
/>
<input required id="name" type="text" name="name" ref={inputRef} placeholder="Name" />
</div>

<div className="input-container">
<div className="input-label">Join as</div>

<div className="tabs-container">
<div className="tab-item">
<button
type="button"
onClick={() => handleTabClick('teacher')}
className={activeTabRole === 'teacher' ? 'active-tab' : 'disabled-tab'}
>
Teacher
</button>
</div>
<div className="tab-item">
<button
type="button"
onClick={() => handleTabClick('student')}
className={activeTabRole === 'student' ? 'active-tab' : 'disabled-tab'}
>
Student
</button>
{roomCodeParam ? null : (
<div className="input-container">
<div className="input-label">Join as</div>

<div className="tabs-container">
{Object.values(ROLES).map((role) => (
<div className="tab-item" key={role}>
<button
type="button"
onClick={() => setActiveTabRole(role)}
style={{ textTransform: 'capitalize' }}
className={activeTabRole === role ? 'active-tab' : 'disabled-tab'}
>
{role}
</button>
</div>
))}
</div>
</div>
</div>
)}

<button type="submit" className="btn-primary primary">
Join Room
<ArrowRightIcon style={{ height: '15px', width: '15px', paddingLeft: '5px' }} />
{roomCodeParam ? 'Accept Invite' : 'Join Room'}
<ArrowRightIcon height={20} width={20} style={{ marginLeft: '4px' }} />
</button>
</form>
<div className="bottom-banner">
<div className="bottom-room-info">PUBLIC ROOM</div>
<div className="bottom-notif body-regular-text ">There might be others in the room</div>
</div>

<div className="responsive-banner">
<div className="responsive-banner-info">USE DESKTOP</div>
<div className="responsive-banner-notif body-regular-text ">
This website is best expeienced on a Desktop or Laptop.
<br />
We are working to improve for mobile devices.
</div>
<div className="responsive-banner-info">PLEASE VIEW IN A DESKTOP</div>
</div>
</div>

<div className="input-graphic">
<img alt="" src="/images/pollsAI.png" width={600} />
<img alt="Generate Polls with AI" src="/images/pollsAI.png" width={600} />
</div>
</>
)
Expand Down
2 changes: 2 additions & 0 deletions app/components/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export enum HMSPollQuestionType {
SHORT_ANSWER = 'short-answer',
LONG_ANSWER = 'long-answer',
}

export const ROLES = { TEACHER: 'teacher', STUDENT: 'student' }
Loading

0 comments on commit bf0ac51

Please sign in to comment.