Skip to content

Commit

Permalink
chore: initial video stream setup
Browse files Browse the repository at this point in the history
  • Loading branch information
aseerkt committed Aug 16, 2024
1 parent befe3e5 commit 06b2077
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 14 deletions.
22 changes: 19 additions & 3 deletions web/src/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
import { stringToColor } from '@/utils/style'
import { cva, VariantProps } from 'cva'
import { memo } from 'react'

const getInitials = (name: string) => {
const nameParts = name.split(' ')
const initials = nameParts.map(part => part.charAt(0).toUpperCase()).join('')
return initials.slice(0, 2) // Get the first two initials
}
const avatarVariants = cva(
'flex shrink-0 items-center justify-center rounded-full border font-bold',
{
variants: {
size: {
sm: 'h-10 w-10 text-base',
lg: 'h-16 w-16 text-lg',
xl: 'h-28 w-28 text-3xl',
},
},
defaultVariants: {
size: 'sm',
},
},
)

interface AvatarProps {
type AvatarProps = VariantProps<typeof avatarVariants> & {
id: number
name: string
}

export const Avatar = memo(({ name, id }: AvatarProps) => {
export const Avatar = memo(({ name, id, size }: AvatarProps) => {
if (!name) return null

const initials = getInitials(name)
Expand All @@ -26,7 +42,7 @@ export const Avatar = memo(({ name, id }: AvatarProps) => {
return (
<div
aria-label={`Avatar of ${name}`}
className='flex h-10 w-10 shrink-0 items-center justify-center rounded-full border text-base font-bold'
className={avatarVariants({ size })}
style={avatarStyle}
>
{initials}
Expand Down
40 changes: 29 additions & 11 deletions web/src/features/chat/components/ChatHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,55 @@
import backArrow from '@/assets/back-svgrepo-com.svg'
import usersSvg from '@/assets/users-svgrepo-com.svg'
import { Alert } from '@/components/Alert'
import { Dialog } from '@/components/Dialog'
import { MediaCaller } from '@/features/webrtc/components'
import { useDisclosure } from '@/hooks/useDisclosure'
import { Phone, Users } from 'lucide-react'
import { NavLink } from 'react-router-dom'

interface ChatHeaderProps {
error: Error | null
chatId?: number
chatName?: string
toggleGroupInfo?: () => void
chatType: 'group' | 'dm'
}

export const ChatHeader = ({
error,
chatId,
chatName,
chatType,
toggleGroupInfo,
}: ChatHeaderProps) => {
const { isOpen: isMediaCallerOpen, toggle: toggleMediaCaller } =
useDisclosure()

let content

if (chatId) {
content = (
<>
<h3 className='text-lg font-bold'>{chatName}</h3>
{toggleGroupInfo && (
<button onClick={toggleGroupInfo} className='ml-auto'>
<img
src={usersSvg}
alt='open member drawer'
height={24}
width={24}
/>
</button>
)}
<div className='ml-auto flex items-center gap-4'>
{chatType === 'group' && (
<>
<button
onClick={toggleMediaCaller}
aria-label='open media caller'
>
<Phone />
</button>
<Dialog isOpen={isMediaCallerOpen} onClose={toggleMediaCaller}>
<MediaCaller onCancel={toggleMediaCaller} />
</Dialog>
</>
)}
{toggleGroupInfo && (
<button onClick={toggleGroupInfo}>
<Users />
</button>
)}
</div>
</>
)
} else if (error) {
Expand Down
64 changes: 64 additions & 0 deletions web/src/features/webrtc/components/MediaCaller.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Phone } from 'lucide-react'
import { useEffect, useRef } from 'react'

export const MediaCaller = ({ onCancel }: { onCancel: () => void }) => {
const localVideoRef = useRef<HTMLVideoElement>(null)

useEffect(() => {
startStream()

return () => {
stopStream()
}
}, [])

const startStream = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true,
})
if (localVideoRef.current) {
localVideoRef.current.srcObject = stream
}
} catch (error) {
// TODO: handle stream error
}
}

const handleCancelStream = () => {
stopStream()
onCancel()
}

const stopStream = () => {
if (localVideoRef.current?.srcObject) {
const stream = localVideoRef.current.srcObject as MediaStream
stream.getTracks().forEach(track => track.stop())
localVideoRef.current.srcObject = null
}
}

return (
<div>
<div className='mb-5 flex justify-center'>
<video ref={localVideoRef} autoPlay playsInline controls={false} />
</div>
<div className='mt-3 inline-flex w-full items-center justify-around'>
{/* <button type='button' aria-label='toggle video visibility'>
{isVideoShared ? <Video /> : <VideoOff />}
</button>
<button type='button' aria-label='toggle audio visibility'>
{isMuted ? <Mic /> : <MicOff />}
</button> */}
<button
type='button'
aria-label='cancel call'
onClick={handleCancelStream}
>
<Phone />
</button>
</div>
</div>
)
}
1 change: 1 addition & 0 deletions web/src/features/webrtc/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { MediaCaller } from './MediaCaller'
2 changes: 2 additions & 0 deletions web/src/features/webrtc/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type DeviceArrayMap = Record<MediaDeviceKind, MediaDeviceInfo[]>
export type DeviceMap = Record<MediaDeviceKind, string>
1 change: 1 addition & 0 deletions web/src/pages/ChatDM.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const Component = () => {
<ChatHeader
chatId={receiver?.id}
chatName={receiver?.username}
chatType='dm'
error={error}
/>
<MessageContainer partnerId={partnerId} />
Expand Down
1 change: 1 addition & 0 deletions web/src/pages/ChatGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const Component = () => {
<ChatHeader
chatId={group?.id}
chatName={group?.name}
chatType='group'
error={error}
toggleGroupInfo={toggle}
/>
Expand Down

0 comments on commit 06b2077

Please sign in to comment.