diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..991141d --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,18 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g' | { grep -E '(js|ts|tsx|json|yml|md|html|css)$' || true; }) + +if [ -z "$FILES" ]; then + exit 0 +fi + +echo "Running prettier" +npm run pre-commit:fix:prettier -- $FILES + +TS_FILES=$(git diff --cached --name-only --diff-filter=ACMR | sed 's| |\\ |g' | { grep -E '(js|ts|tsx)$' || true; }) + +if [[ ! -z "$TS_FILES" ]];then + echo "Running lints" + npm run pre-commit:fix:code -- $TS_FILES +fi diff --git a/package.json b/package.json index 9012fb5..985bcbb 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "check:code": "eslint -c .eslintrc.js src", "fix:code": "npm run check:code -- --fix", "pre-commit:fix:code": "eslint -c .eslintrc.js --fix", + "pre-commit:fix:prettier": "prettier --config .prettierrc.json --write", "prepare": "husky install", "test": "jest", "test:coverage": "npm run test -- --coverage" @@ -88,4 +89,4 @@ "url": "https://github.com/decentraland/meet.git" }, "homepage": "" -} \ No newline at end of file +} diff --git a/src/assets/icons/Close.svg b/src/assets/icons/Close.svg new file mode 100644 index 0000000..5f82355 --- /dev/null +++ b/src/assets/icons/Close.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/People.svg b/src/assets/icons/People.svg index 1612c99..a0d4e8a 100644 --- a/src/assets/icons/People.svg +++ b/src/assets/icons/People.svg @@ -1,13 +1,3 @@ - - - - - - - - - - - - - + + + \ No newline at end of file diff --git a/src/assets/icons/PeopleIcon.tsx b/src/assets/icons/PeopleIcon.tsx new file mode 100644 index 0000000..7740a55 --- /dev/null +++ b/src/assets/icons/PeopleIcon.tsx @@ -0,0 +1,15 @@ +import * as React from 'react' +import type { SVGProps } from 'react' + +const PeopleIcon = (props: SVGProps) => ( + + + +) + +export default PeopleIcon diff --git a/src/components/VideoConference/ControlBar/ControlBar.module.css b/src/components/VideoConference/ControlBar/ControlBar.module.css new file mode 100644 index 0000000..c942846 --- /dev/null +++ b/src/components/VideoConference/ControlBar/ControlBar.module.css @@ -0,0 +1,12 @@ +.ControlBarContainer { + position: relative; +} + +.ControlBarRightButtonGroup { + position: absolute; + right: 0; + display: flex; + gap: 0.5rem; + align-items: center; + justify-content: center; +} diff --git a/src/components/VideoConference/ControlBar/ControlBar.tsx b/src/components/VideoConference/ControlBar/ControlBar.tsx index 3dd2631..1f73e2f 100644 --- a/src/components/VideoConference/ControlBar/ControlBar.tsx +++ b/src/components/VideoConference/ControlBar/ControlBar.tsx @@ -1,5 +1,4 @@ -import * as React from 'react' -import { useEffect, useMemo, useRef, useState } from 'react' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { supportsScreenSharing } from '@livekit/components-core' import { ChatToggle, @@ -8,16 +7,18 @@ import { StartAudio, TrackToggle, useLocalParticipantPermissions, - useMaybeLayoutContext, useRoomContext } from '@livekit/components-react' import { LocalAudioTrack, LocalVideoTrack, Track } from 'livekit-client' import ChatIcon from '../../../assets/icons/ChatIcon' import LeaveIcon from '../../../assets/icons/LeaveIcon' +import { useLayoutContext } from '../../../hooks/useLayoutContext' import { useMediaQuery } from '../../../hooks/useMediaQuery' import { usePreviewTracks } from '../../../hooks/usePreviewTracks' import { mergeProps } from '../../../utils/mergeProps' +import PeoplePanelToggleButton from './PeoplePanelToggleButton' import { ControlBarProps, DEFAULT_USER_CHOICES } from './ControlBar.types' +import styles from './ControlBar.module.css' /** * The ControlBar prefab component gives the user the basic user interface @@ -37,7 +38,7 @@ import { ControlBarProps, DEFAULT_USER_CHOICES } from './ControlBar.types' */ export function ControlBar({ variation, controls, ...props }: ControlBarProps) { const [isChatOpen, setIsChatOpen] = useState(false) - const layoutContext = useMaybeLayoutContext() + const layoutContext = useLayoutContext() const { options: { videoCaptureDefaults, audioCaptureDefaults } } = useRoomContext() @@ -73,7 +74,7 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) { setIsScreenShareEnabled(enabled) } - const htmlProps = mergeProps({ className: 'lk-control-bar' }, props) + const htmlProps = mergeProps({ className: `lk-control-bar ${styles.ControlBarContainer}` }, props) const [videoEnabled, setVideoEnabled] = useState(visibleControls.camera ?? DEFAULT_USER_CHOICES.videoEnabled) const initialVideoDeviceId = (videoCaptureDefaults?.deviceId as string) ?? DEFAULT_USER_CHOICES.videoDeviceId @@ -166,12 +167,6 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) { {showText && (isScreenShareEnabled ? 'Stop screen share' : 'Share screen')} )} - {visibleControls.chat && ( - - {showIcon && } - {showText && 'Chat'} - - )} {visibleControls.leave && ( {showIcon && } @@ -179,6 +174,15 @@ export function ControlBar({ variation, controls, ...props }: ControlBarProps) { )} +
+ {visibleControls.chat && ( + + {showIcon && } + {showText && 'Chat'} + + )} + {visibleControls.peoplePanel && } +
) } diff --git a/src/components/VideoConference/ControlBar/ControlBar.types.ts b/src/components/VideoConference/ControlBar/ControlBar.types.ts index 5e30c99..98ff8c1 100644 --- a/src/components/VideoConference/ControlBar/ControlBar.types.ts +++ b/src/components/VideoConference/ControlBar/ControlBar.types.ts @@ -8,7 +8,9 @@ export const DEFAULT_USER_CHOICES = { } /** @public */ -export type ControlBarControls = BaseControlBarControls +export type ControlBarControls = BaseControlBarControls & { + peoplePanel?: boolean +} /** @public */ export interface ControlBarProps extends BaseControlBarProps { diff --git a/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.container.ts b/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.container.ts new file mode 100644 index 0000000..4a3e255 --- /dev/null +++ b/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.container.ts @@ -0,0 +1,13 @@ +import { connect } from 'react-redux' +import { getData as getProfiles } from 'decentraland-dapps/dist/modules/profile/selectors' +import { RootState } from '../../../../modules/reducer' +import { PeoplePanelToggleButton } from './PeoplePanelToggleButton' +import type { MapStateProps } from './PeoplePanelToggleButton.types' + +const mapStateToProps = (state: RootState): MapStateProps => { + return { + peopleCount: Object.keys(getProfiles(state)).filter(address => !!address).length + } +} + +export default connect(mapStateToProps)(PeoplePanelToggleButton) diff --git a/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.module.css b/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.module.css new file mode 100644 index 0000000..6997db6 --- /dev/null +++ b/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.module.css @@ -0,0 +1,20 @@ +.button { + background-color: transparent; + padding: 0.625rem 1rem; +} + +.buttonActive { + background-color: var(--lk-control-active-bg); +} + +.peopleIcon { + height: 18px; + width: 18px; +} + +.label { + position: relative; + top: -16px; + left: -14px; + min-width: 40px; +} diff --git a/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.tsx b/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.tsx new file mode 100644 index 0000000..9a2fce4 --- /dev/null +++ b/src/components/VideoConference/ControlBar/PeoplePanelToggleButton/PeoplePanelToggleButton.tsx @@ -0,0 +1,36 @@ +import React, { useCallback, useMemo } from 'react' +import classNames from 'classnames' +import { Label } from 'decentraland-ui' +import PeopleIcon from '../../../../assets/icons/PeopleIcon' +import { WidgetState, useLayoutContext } from '../../../../hooks/useLayoutContext' +import type { Props } from './PeoplePanelToggleButton.types' +import styles from './PeoplePanelToggleButton.module.css' + +export const PeoplePanelToggleButton: React.FC = ({ peopleCount = 0, onClick }) => { + const layoutContext = useLayoutContext() + + const isPeoplePanelActive = useMemo(() => { + return (layoutContext.widget.state as WidgetState).showPeoplePanel + }, [layoutContext.widget.state]) + + const handleTogglePeoplePanel = useCallback(() => { + const { dispatch } = layoutContext.widget + if (dispatch) { + dispatch({ msg: 'toggle_people_panel' }) + } + }, [layoutContext]) + + return ( + <> + +