✨(frontend) introduce a side panel for effects
It simply renders the video track if enabled. It's a basis for building the 'blur your screen' feature. More in the upcoming commits.
This commit is contained in:
committed by
aleb_the_flash
parent
00fa7beebd
commit
756be17cc7
@@ -0,0 +1,68 @@
|
|||||||
|
import { useEffect, useRef } from 'react'
|
||||||
|
import { useLocalParticipant } from '@livekit/components-react'
|
||||||
|
import { LocalVideoTrack } from 'livekit-client'
|
||||||
|
import { Div, P } from '@/primitives'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export const Effects = () => {
|
||||||
|
const { t } = useTranslation('rooms', { keyPrefix: 'effects' })
|
||||||
|
const { isCameraEnabled, cameraTrack } = useLocalParticipant()
|
||||||
|
const videoRef = useRef<HTMLVideoElement>(null)
|
||||||
|
|
||||||
|
const localCameraTrack = cameraTrack?.track as LocalVideoTrack
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const videoElement = videoRef.current
|
||||||
|
|
||||||
|
const attachVideoTrack = async () => {
|
||||||
|
if (!videoElement) return
|
||||||
|
localCameraTrack?.attach(videoElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
attachVideoTrack()
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (!videoElement) return
|
||||||
|
localCameraTrack.detach(videoElement)
|
||||||
|
}
|
||||||
|
}, [localCameraTrack, isCameraEnabled])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Div padding="0 1.5rem">
|
||||||
|
{localCameraTrack && isCameraEnabled ? (
|
||||||
|
<video
|
||||||
|
ref={videoRef}
|
||||||
|
width="100%"
|
||||||
|
muted
|
||||||
|
style={{
|
||||||
|
transform: 'rotateY(180deg)',
|
||||||
|
minHeight: '173px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: '174px',
|
||||||
|
display: 'flex',
|
||||||
|
backgroundColor: 'black',
|
||||||
|
justifyContent: 'center',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<P
|
||||||
|
style={{
|
||||||
|
color: 'white',
|
||||||
|
textAlign: 'center',
|
||||||
|
textWrap: 'balance',
|
||||||
|
marginBottom: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t('activateCamera')}
|
||||||
|
</P>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { ParticipantsList } from './controls/Participants/ParticipantsList'
|
import { ParticipantsList } from './controls/Participants/ParticipantsList'
|
||||||
import { useWidgetInteraction } from '../hooks/useWidgetInteraction'
|
import { useWidgetInteraction } from '../hooks/useWidgetInteraction'
|
||||||
import { ReactNode } from 'react'
|
import { ReactNode } from 'react'
|
||||||
|
import { Effects } from './Effects'
|
||||||
|
|
||||||
type StyledSidePanelProps = {
|
type StyledSidePanelProps = {
|
||||||
title: string
|
title: string
|
||||||
@@ -65,7 +66,7 @@ export const SidePanel = () => {
|
|||||||
const layoutSnap = useSnapshot(layoutStore)
|
const layoutSnap = useSnapshot(layoutStore)
|
||||||
const sidePanel = layoutSnap.sidePanel
|
const sidePanel = layoutSnap.sidePanel
|
||||||
|
|
||||||
const { isParticipantsOpen } = useWidgetInteraction()
|
const { isParticipantsOpen, isEffectsOpen } = useWidgetInteraction()
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
|
const { t } = useTranslation('rooms', { keyPrefix: 'sidePanel' })
|
||||||
|
|
||||||
if (!sidePanel) {
|
if (!sidePanel) {
|
||||||
@@ -81,6 +82,7 @@ export const SidePanel = () => {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{isParticipantsOpen && <ParticipantsList />}
|
{isParticipantsOpen && <ParticipantsList />}
|
||||||
|
{isEffectsOpen && <Effects />}
|
||||||
</StyledSidePanel>
|
</StyledSidePanel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { Dispatch, SetStateAction } from 'react'
|
import { Dispatch, SetStateAction } from 'react'
|
||||||
import { DialogState } from './OptionsButton'
|
import { DialogState } from './OptionsButton'
|
||||||
import { Separator } from '@/primitives/Separator'
|
import { Separator } from '@/primitives/Separator'
|
||||||
|
import { useWidgetInteraction } from '../../../hooks/useWidgetInteraction'
|
||||||
|
|
||||||
// @todo try refactoring it to use MenuList component
|
// @todo try refactoring it to use MenuList component
|
||||||
export const OptionsMenuItems = ({
|
export const OptionsMenuItems = ({
|
||||||
@@ -18,7 +19,7 @@ export const OptionsMenuItems = ({
|
|||||||
onOpenDialog: Dispatch<SetStateAction<DialogState>>
|
onOpenDialog: Dispatch<SetStateAction<DialogState>>
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
|
const { t } = useTranslation('rooms', { keyPrefix: 'options.items' })
|
||||||
|
const { toggleEffects } = useWidgetInteraction()
|
||||||
return (
|
return (
|
||||||
<RACMenu
|
<RACMenu
|
||||||
style={{
|
style={{
|
||||||
@@ -28,7 +29,7 @@ export const OptionsMenuItems = ({
|
|||||||
>
|
>
|
||||||
<Section>
|
<Section>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onAction={() => console.log('open dialog')}
|
onAction={() => toggleEffects()}
|
||||||
className={menuItemRecipe({ icon: true })}
|
className={menuItemRecipe({ icon: true })}
|
||||||
>
|
>
|
||||||
<RiAccountBoxLine size={20} />
|
<RiAccountBoxLine size={20} />
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const useWidgetInteraction = () => {
|
|||||||
const sidePanel = layoutSnap.sidePanel
|
const sidePanel = layoutSnap.sidePanel
|
||||||
|
|
||||||
const isParticipantsOpen = sidePanel == 'participants'
|
const isParticipantsOpen = sidePanel == 'participants'
|
||||||
|
const isEffectsOpen = sidePanel == 'effects'
|
||||||
|
|
||||||
const toggleParticipants = () => {
|
const toggleParticipants = () => {
|
||||||
if (dispatch && state?.showChat) {
|
if (dispatch && state?.showChat) {
|
||||||
@@ -26,11 +27,20 @@ export const useWidgetInteraction = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toggleEffects = () => {
|
||||||
|
if (dispatch && state?.showChat) {
|
||||||
|
dispatch({ msg: 'toggle_chat' })
|
||||||
|
}
|
||||||
|
layoutStore.sidePanel = isEffectsOpen ? null : 'effects'
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
toggleParticipants,
|
toggleParticipants,
|
||||||
toggleChat,
|
toggleChat,
|
||||||
|
toggleEffects,
|
||||||
isChatOpen: state?.showChat,
|
isChatOpen: state?.showChat,
|
||||||
unreadMessages: state?.unreadMessages,
|
unreadMessages: state?.unreadMessages,
|
||||||
isParticipantsOpen,
|
isParticipantsOpen,
|
||||||
|
isEffectsOpen,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,12 +73,17 @@
|
|||||||
"effects": ""
|
"effects": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"effects": {
|
||||||
|
"activateCamera": ""
|
||||||
|
},
|
||||||
"sidePanel": {
|
"sidePanel": {
|
||||||
"heading": {
|
"heading": {
|
||||||
"participants": ""
|
"participants": "",
|
||||||
|
"effects": ""
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"participants": ""
|
"participants": "",
|
||||||
|
"effects": ""
|
||||||
},
|
},
|
||||||
"closeButton": ""
|
"closeButton": ""
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -71,12 +71,17 @@
|
|||||||
"effects": "Apply effects"
|
"effects": "Apply effects"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"effects": {
|
||||||
|
"activateCamera": "Camera is disabled"
|
||||||
|
},
|
||||||
"sidePanel": {
|
"sidePanel": {
|
||||||
"heading": {
|
"heading": {
|
||||||
"participants": "Participants"
|
"participants": "Participants",
|
||||||
|
"effects": "Effects"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"participants": "participants"
|
"participants": "participants",
|
||||||
|
"effects": "effects"
|
||||||
},
|
},
|
||||||
"closeButton": "Hide {{content}}"
|
"closeButton": "Hide {{content}}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -71,12 +71,17 @@
|
|||||||
"effects": "Appliquer des effets"
|
"effects": "Appliquer des effets"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"effects": {
|
||||||
|
"activateCamera": "Votre camera est désactivée"
|
||||||
|
},
|
||||||
"sidePanel": {
|
"sidePanel": {
|
||||||
"heading": {
|
"heading": {
|
||||||
"participants": "Participants"
|
"participants": "Participants",
|
||||||
|
"effects": "Effets"
|
||||||
},
|
},
|
||||||
"content": {
|
"content": {
|
||||||
"participants": "les participants"
|
"participants": "les participants",
|
||||||
|
"effects": "les effets"
|
||||||
},
|
},
|
||||||
"closeButton": "Masquer {{content}}"
|
"closeButton": "Masquer {{content}}"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { proxy } from 'valtio'
|
|||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
showHeader: boolean
|
showHeader: boolean
|
||||||
sidePanel: 'participants' | null
|
sidePanel: 'participants' | 'effects' | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const layoutStore = proxy<State>({
|
export const layoutStore = proxy<State>({
|
||||||
|
|||||||
Reference in New Issue
Block a user