From aa47648ef599e38cf2eb68ce1e9def7dd244f52c Mon Sep 17 00:00:00 2001 From: Rafael Galdino da Silva Date: Mon, 30 Oct 2023 21:06:12 -0300 Subject: [PATCH 1/7] refactor: refactoring all non-null assertions --- .gitignore | 3 ++ package-lock.json | 4 +-- .../ChatWindow/MessageList/MessageList.tsx | 30 ++++++++-------- .../MessageListScrollContainer.tsx | 26 ++++++++------ src/components/GalleryView/GalleryView.tsx | 27 +++++++------- .../usePagination/usePagination.ts | 2 +- .../IntroContainer/UserMenu/UserMenu.tsx | 14 +++++--- .../MainParticipant/MainParticipant.tsx | 9 +++-- .../MainParticipantInfo.tsx | 12 +++---- src/components/MenuBar/Menu/Menu.tsx | 36 ++++++++++--------- src/components/MenuBar/MenuBar.tsx | 15 ++++---- .../MobileTopMenuBar/MobileTopMenuBar.tsx | 5 +-- .../ParticipantList/ParticipantList.tsx | 13 ++++--- src/components/Snackbar/Snackbar.tsx | 10 +++--- src/components/VideoProvider/index.tsx | 4 +-- 15 files changed, 117 insertions(+), 93 deletions(-) diff --git a/.gitignore b/.gitignore index 85162c36d..090139f34 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ serviceAccountKey.json public/noisecancellation/ public/virtualbackground/ +files_smells.csv +components_smells.csv +*.components_smells.csv diff --git a/package-lock.json b/package-lock.json index b932e37f8..8fbc07b20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "twilio-video-app-react", - "version": "0.10.1", + "version": "0.10.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "twilio-video-app-react", - "version": "0.10.1", + "version": "0.10.2", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/src/components/ChatWindow/MessageList/MessageList.tsx b/src/components/ChatWindow/MessageList/MessageList.tsx index e4866f0cf..845dda424 100644 --- a/src/components/ChatWindow/MessageList/MessageList.tsx +++ b/src/components/ChatWindow/MessageList/MessageList.tsx @@ -1,10 +1,10 @@ -import React from 'react'; import { Message } from '@twilio/conversations'; +import React from 'react'; +import useVideoContext from '../../../hooks/useVideoContext/useVideoContext'; +import MediaMessage from './MediaMessage/MediaMessage'; import MessageInfo from './MessageInfo/MessageInfo'; import MessageListScrollContainer from './MessageListScrollContainer/MessageListScrollContainer'; import TextMessage from './TextMessage/TextMessage'; -import useVideoContext from '../../../hooks/useVideoContext/useVideoContext'; -import MediaMessage from './MediaMessage/MediaMessage'; interface MessageListProps { messages: Message[]; @@ -15,7 +15,7 @@ const getFormattedTime = (message?: Message) => export default function MessageList({ messages }: MessageListProps) { const { room } = useVideoContext(); - const localParticipant = room!.localParticipant; + const localParticipant = room && room.localParticipant; return ( @@ -26,17 +26,19 @@ export default function MessageList({ messages }: MessageListProps) { // Display the MessageInfo component when the author or formatted timestamp differs from the previous message const shouldDisplayMessageInfo = time !== previousTime || message.author !== messages[idx - 1]?.author; - const isLocalParticipant = localParticipant.identity === message.author; + const isLocalParticipant = localParticipant && localParticipant.identity === message.author; - return ( - - {shouldDisplayMessageInfo && ( - - )} - {message.type === 'text' && } - {message.type === 'media' && } - - ); + if (message.author && message.body && message.attachedMedia && isLocalParticipant) { + return ( + + {shouldDisplayMessageInfo && ( + + )} + {message.type === 'text' && } + {message.type === 'media' && } + + ); + } })} ); diff --git a/src/components/ChatWindow/MessageList/MessageListScrollContainer/MessageListScrollContainer.tsx b/src/components/ChatWindow/MessageList/MessageListScrollContainer/MessageListScrollContainer.tsx index 0aad04dd4..c1b9b3be2 100644 --- a/src/components/ChatWindow/MessageList/MessageListScrollContainer/MessageListScrollContainer.tsx +++ b/src/components/ChatWindow/MessageList/MessageListScrollContainer/MessageListScrollContainer.tsx @@ -1,11 +1,11 @@ /* istanbul ignore file */ -import React from 'react'; -import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; import Button from '@material-ui/core/Button'; -import clsx from 'clsx'; +import { WithStyles, createStyles, withStyles } from '@material-ui/core/styles'; +import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; import { Message } from '@twilio/conversations'; +import clsx from 'clsx'; import throttle from 'lodash.throttle'; -import { withStyles, WithStyles, createStyles } from '@material-ui/core/styles'; +import React from 'react'; const styles = createStyles({ outerContainer: { @@ -70,13 +70,14 @@ export class MessageListScrollContainer extends React.Component< state = { isScrolledToBottom: true, showButton: false, messageNotificationCount: 0 }; scrollToBottom() { - const innerScrollContainerEl = this.chatThreadRef.current!; - innerScrollContainerEl.scrollTop = innerScrollContainerEl!.scrollHeight; + const innerScrollContainerEl = this.chatThreadRef.current; + if (!innerScrollContainerEl) return; + innerScrollContainerEl.scrollTop = innerScrollContainerEl.scrollHeight; } componentDidMount() { this.scrollToBottom(); - this.chatThreadRef.current!.addEventListener('scroll', this.handleScroll); + this.chatThreadRef.current && this.chatThreadRef.current.addEventListener('scroll', this.handleScroll); } // This component updates as users send new messages: @@ -102,7 +103,8 @@ export class MessageListScrollContainer extends React.Component< } handleScroll = throttle(() => { - const innerScrollContainerEl = this.chatThreadRef.current!; + const innerScrollContainerEl = this.chatThreadRef.current; + // Because this.handleScroll() is a throttled method, // it's possible that it can be called after this component unmounts, and this element will be null. // Therefore, if it doesn't exist, don't do anything: @@ -112,7 +114,7 @@ export class MessageListScrollContainer extends React.Component< // "isScrolledToBottom" calculation. const isScrolledToBottom = Math.abs( - innerScrollContainerEl.clientHeight + innerScrollContainerEl.scrollTop - innerScrollContainerEl!.scrollHeight + innerScrollContainerEl.clientHeight + innerScrollContainerEl.scrollTop - innerScrollContainerEl.scrollHeight ) < 1; this.setState(prevState => ({ @@ -122,7 +124,8 @@ export class MessageListScrollContainer extends React.Component< }, 300); handleClick = () => { - const innerScrollContainerEl = this.chatThreadRef.current!; + const innerScrollContainerEl = this.chatThreadRef.current; + if (!innerScrollContainerEl) return; innerScrollContainerEl.scrollTo({ top: innerScrollContainerEl.scrollHeight, behavior: 'smooth' }); @@ -130,7 +133,8 @@ export class MessageListScrollContainer extends React.Component< }; componentWillUnmount() { - const innerScrollContainerEl = this.chatThreadRef.current!; + const innerScrollContainerEl = this.chatThreadRef.current; + if (!innerScrollContainerEl) return; innerScrollContainerEl.removeEventListener('scroll', this.handleScroll); } diff --git a/src/components/GalleryView/GalleryView.tsx b/src/components/GalleryView/GalleryView.tsx index 6f7f4f78a..194bec110 100644 --- a/src/components/GalleryView/GalleryView.tsx +++ b/src/components/GalleryView/GalleryView.tsx @@ -1,17 +1,16 @@ -import React from 'react'; +import { IconButton, Theme, createStyles, makeStyles } from '@material-ui/core'; import ArrowBack from '@material-ui/icons/ArrowBack'; import ArrowForward from '@material-ui/icons/ArrowForward'; +import { Pagination } from '@material-ui/lab'; import clsx from 'clsx'; import { GALLERY_VIEW_ASPECT_RATIO, GALLERY_VIEW_MARGIN } from '../../constants'; -import { IconButton, makeStyles, createStyles, Theme } from '@material-ui/core'; -import { Pagination } from '@material-ui/lab'; -import Participant from '../Participant/Participant'; +import useDominantSpeaker from '../../hooks/useDominantSpeaker/useDominantSpeaker'; import useGalleryViewLayout from '../../hooks/useGalleryViewLayout/useGalleryViewLayout'; -import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; import useParticipantsContext from '../../hooks/useParticipantsContext/useParticipantsContext'; -import { usePagination } from './usePagination/usePagination'; -import useDominantSpeaker from '../../hooks/useDominantSpeaker/useDominantSpeaker'; +import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; import { useAppState } from '../../state'; +import Participant from '../Participant/Participant'; +import { usePagination } from './usePagination/usePagination'; const CONTAINER_GUTTER = '50px'; @@ -85,10 +84,10 @@ export function GalleryView() { const { galleryViewParticipants } = useParticipantsContext(); const dominantSpeaker = useDominantSpeaker(true); - const { paginatedParticipants, setCurrentPage, currentPage, totalPages } = usePagination([ - room!.localParticipant, - ...galleryViewParticipants, - ]); + const participants = + room && room.localParticipant ? [room.localParticipant, ...galleryViewParticipants] : [...galleryViewParticipants]; + + const { paginatedParticipants, setCurrentPage, currentPage, totalPages } = usePagination(participants); const galleryViewLayoutParticipantCount = currentPage === 1 ? paginatedParticipants.length : maxGalleryViewParticipants; @@ -97,6 +96,10 @@ export function GalleryView() { const participantWidth = `${participantVideoWidth}px`; const participantHeight = `${Math.floor(participantVideoWidth * GALLERY_VIEW_ASPECT_RATIO)}px`; + if (!room) { + return <>; + } + return (
@@ -136,7 +139,7 @@ export function GalleryView() { >
diff --git a/src/components/GalleryView/usePagination/usePagination.ts b/src/components/GalleryView/usePagination/usePagination.ts index 70d476848..f4164ba79 100644 --- a/src/components/GalleryView/usePagination/usePagination.ts +++ b/src/components/GalleryView/usePagination/usePagination.ts @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { Participant } from 'twilio-video'; import { useAppState } from '../../../state'; diff --git a/src/components/IntroContainer/UserMenu/UserMenu.tsx b/src/components/IntroContainer/UserMenu/UserMenu.tsx index 60596f8f7..64f17e872 100644 --- a/src/components/IntroContainer/UserMenu/UserMenu.tsx +++ b/src/components/IntroContainer/UserMenu/UserMenu.tsx @@ -1,10 +1,10 @@ -import React, { useState, useRef, useCallback } from 'react'; -import { makeStyles, Typography, Button, MenuItem, Link } from '@material-ui/core'; +import { Button, Link, MenuItem, Typography, makeStyles } from '@material-ui/core'; +import Menu from '@material-ui/core/Menu'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import React, { useCallback, useRef, useState } from 'react'; +import useVideoContext from '../../../hooks/useVideoContext/useVideoContext'; import { useAppState } from '../../../state'; import UserAvatar from './UserAvatar/UserAvatar'; -import Menu from '@material-ui/core/Menu'; -import useVideoContext from '../../../hooks/useVideoContext/useVideoContext'; const useStyles = makeStyles({ userContainer: { @@ -38,6 +38,10 @@ const UserMenu: React.FC = () => { signOut?.(); }, [localTracks, signOut]); + if (!user) { + return <>; + } + if (process.env.REACT_APP_SET_AUTH === 'passcode') { return (
@@ -53,7 +57,7 @@ const UserMenu: React.FC = () => {
{ setMenuOpen(false); - if (isRecording) { - updateRecordingRules(room!.sid, [{ type: 'exclude', all: true }]); - } else { - updateRecordingRules(room!.sid, [{ type: 'include', all: true }]); + if (room) { + if (isRecording) { + updateRecordingRules(room.sid, [{ type: 'exclude', all: true }]); + } else { + updateRecordingRules(room.sid, [{ type: 'include', all: true }]); + } } }} data-cy-recording-button diff --git a/src/components/MenuBar/MenuBar.tsx b/src/components/MenuBar/MenuBar.tsx index fa03fd176..07affb62b 100644 --- a/src/components/MenuBar/MenuBar.tsx +++ b/src/components/MenuBar/MenuBar.tsx @@ -1,18 +1,17 @@ -import React from 'react'; import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; +import { Grid, Hidden, Typography } from '@material-ui/core'; import Button from '@material-ui/core/Button'; -import EndCallButton from '../Buttons/EndCallButton/EndCallButton'; -import { isMobile } from '../../utils'; -import Menu from './Menu/Menu'; import useParticipants from '../../hooks/useParticipants/useParticipants'; import useRoomState from '../../hooks/useRoomState/useRoomState'; import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; -import { Typography, Grid, Hidden } from '@material-ui/core'; +import { isMobile } from '../../utils'; +import EndCallButton from '../Buttons/EndCallButton/EndCallButton'; import ToggleAudioButton from '../Buttons/ToggleAudioButton/ToggleAudioButton'; import ToggleChatButton from '../Buttons/ToggleChatButton/ToggleChatButton'; import ToggleVideoButton from '../Buttons/ToggleVideoButton/ToggleVideoButton'; import ToggleScreenShareButton from '../Buttons/ToogleScreenShareButton/ToggleScreenShareButton'; +import Menu from './Menu/Menu'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -71,6 +70,10 @@ export default function MenuBar() { const { room } = useVideoContext(); const participants = useParticipants(); + if (!room) { + return <>; + } + return ( <> {isSharingScreen && ( @@ -84,7 +87,7 @@ export default function MenuBar() { - {room!.name} | {participants.length + 1} participant{participants.length ? 's' : ''} + {room.name} | {participants.length + 1} participant{participants.length ? 's' : ''} diff --git a/src/components/MobileTopMenuBar/MobileTopMenuBar.tsx b/src/components/MobileTopMenuBar/MobileTopMenuBar.tsx index 6ce27c0b3..ae697eeee 100644 --- a/src/components/MobileTopMenuBar/MobileTopMenuBar.tsx +++ b/src/components/MobileTopMenuBar/MobileTopMenuBar.tsx @@ -1,5 +1,4 @@ import { Grid, makeStyles, Theme, Typography } from '@material-ui/core'; -import React from 'react'; import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; import EndCallButton from '../Buttons/EndCallButton/EndCallButton'; import Menu from '../MenuBar/Menu/Menu'; @@ -34,9 +33,11 @@ export default function MobileTopMenuBar() { const classes = useStyles(); const { room } = useVideoContext(); + if (!room) return <>; + return ( - {room!.name} + {room.name}
diff --git a/src/components/ParticipantList/ParticipantList.tsx b/src/components/ParticipantList/ParticipantList.tsx index d4779117b..10b716e77 100644 --- a/src/components/ParticipantList/ParticipantList.tsx +++ b/src/components/ParticipantList/ParticipantList.tsx @@ -1,12 +1,11 @@ -import React from 'react'; +import { Theme, createStyles, makeStyles } from '@material-ui/core/styles'; import clsx from 'clsx'; -import Participant from '../Participant/Participant'; -import { makeStyles, createStyles, Theme } from '@material-ui/core/styles'; import useMainParticipant from '../../hooks/useMainParticipant/useMainParticipant'; import useParticipantsContext from '../../hooks/useParticipantsContext/useParticipantsContext'; +import useScreenShareParticipant from '../../hooks/useScreenShareParticipant/useScreenShareParticipant'; import useVideoContext from '../../hooks/useVideoContext/useVideoContext'; +import Participant from '../Participant/Participant'; import useSelectedParticipant from '../VideoProvider/useSelectedParticipant/useSelectedParticipant'; -import useScreenShareParticipant from '../../hooks/useScreenShareParticipant/useScreenShareParticipant'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -44,7 +43,7 @@ const useStyles = makeStyles((theme: Theme) => export default function ParticipantList() { const classes = useStyles(); const { room } = useVideoContext(); - const localParticipant = room!.localParticipant; + const localParticipant = room && room.localParticipant; const { speakerViewParticipants } = useParticipantsContext(); const [selectedParticipant, setSelectedParticipant] = useSelectedParticipant(); const screenShareParticipant = useScreenShareParticipant(); @@ -53,6 +52,10 @@ export default function ParticipantList() { if (speakerViewParticipants.length === 0) return null; // Don't render this component if there are no remote participants. + if (!localParticipant) { + return <>; + } + return (