From c8ffb46782d0950bdbff4c274c20beafe6515737 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Thu, 20 Jul 2023 15:58:56 +0200 Subject: [PATCH] Group Calls: More fixes (#3534) --- .../calls/group/GroupCall.module.scss | 7 ++- src/components/calls/group/GroupCall.tsx | 15 +++-- .../group/GroupCallParticipant.module.scss | 1 + .../calls/group/GroupCallParticipant.tsx | 11 ++-- .../calls/group/GroupCallParticipantList.tsx | 20 ++++-- .../calls/group/GroupCallParticipantMenu.tsx | 2 +- .../GroupCallParticipantVideo.module.scss | 36 +---------- .../calls/group/GroupCallParticipantVideo.tsx | 61 ++++++++----------- .../group/helpers/formatGroupCallVolume.ts | 6 ++ src/components/main/Main.scss | 4 +- src/components/middle/composer/DropArea.scss | 2 +- src/components/ui/FloatingActionButton.scss | 4 +- src/styles/_mixins.scss | 5 ++ src/styles/_variables.scss | 1 + src/styles/index.scss | 10 ++- 15 files changed, 88 insertions(+), 97 deletions(-) create mode 100644 src/components/calls/group/helpers/formatGroupCallVolume.ts diff --git a/src/components/calls/group/GroupCall.module.scss b/src/components/calls/group/GroupCall.module.scss index 990ec8d01..6d0a4f1e8 100644 --- a/src/components/calls/group/GroupCall.module.scss +++ b/src/components/calls/group/GroupCall.module.scss @@ -1,3 +1,5 @@ +@import "../../../styles/mixins"; + .root { --group-call-panel-color: #212121; --group-call-panel-header-border-color: #3b3b3b; @@ -53,7 +55,7 @@ flex-direction: column; height: 100%; - overflow: auto; + overflow-y: scroll; position: relative; } @@ -72,6 +74,8 @@ border-bottom: 0.0625rem solid transparent; padding: 0.375rem 0.875rem; + @include adapt-padding-to-scrollbar(0.875rem); + user-select: none; z-index: 1; background: var(--group-call-panel-color); @@ -135,6 +139,7 @@ .participants { position: relative; margin: 0.125rem 0.5rem 0; + @include adapt-margin-to-scrollbar(0.5rem); } .participantVideos { diff --git a/src/components/calls/group/GroupCall.tsx b/src/components/calls/group/GroupCall.tsx index 05f1ad491..718f253f3 100644 --- a/src/components/calls/group/GroupCall.tsx +++ b/src/components/calls/group/GroupCall.tsx @@ -207,7 +207,7 @@ const GroupCall: FC = ({ toggleGroupCallPresentation(); }); - const canPinVideo = videoParticipants.length > 1 && isLandscapeLayout; + const canPinVideo = videoParticipants.length > 1 && !isMobile; const isLandscapeWithVideos = isLandscapeLayout && hasVideoParticipants; const [pinnedVideo, setPinnedVideo] = useState(undefined); const { @@ -221,6 +221,13 @@ const GroupCall: FC = ({ pinnedVideo, }); + const handleSetPinnedVideo = useLastCallback((video: VideoParticipant | undefined) => { + setPinnedVideo(video); + if (video && !isFullscreen) { + openFullscreen(); + } + }); + const handleOpenFirstPresentation = useLastCallback(() => { if (!firstPresentation) return; @@ -405,10 +412,9 @@ const GroupCall: FC = ({ key={`${layout.participantId}_${layout.type}`} layout={layout} canPin={canPinVideo} - setPinned={setPinnedVideo} + setPinned={handleSetPinnedVideo} pinnedVideo={pinnedVideo} participant={participant} - onStopSharing={handleToggleGroupCallPresentation} /> ); })} @@ -448,11 +454,10 @@ const GroupCall: FC = ({ key={`${layout.participantId}_${layout.type}`} layout={layout} canPin={canPinVideo} - setPinned={setPinnedVideo} + setPinned={handleSetPinnedVideo} pinnedVideo={pinnedVideo} participant={participant} className={styles.video} - onStopSharing={handleToggleGroupCallPresentation} /> ); })} diff --git a/src/components/calls/group/GroupCallParticipant.module.scss b/src/components/calls/group/GroupCallParticipant.module.scss index 2945ba2ff..407cb2336 100644 --- a/src/components/calls/group/GroupCallParticipant.module.scss +++ b/src/components/calls/group/GroupCallParticipant.module.scss @@ -15,6 +15,7 @@ .fullName { font-weight: 500; font-size: 1rem; + --emoji-size: 1rem; } } } diff --git a/src/components/calls/group/GroupCallParticipant.tsx b/src/components/calls/group/GroupCallParticipant.tsx index c4bac7219..bd7935d53 100644 --- a/src/components/calls/group/GroupCallParticipant.tsx +++ b/src/components/calls/group/GroupCallParticipant.tsx @@ -8,10 +8,12 @@ import { withGlobal } from '../../../global'; import type { ApiChat, ApiUser } from '../../../api/types'; -import { GROUP_CALL_DEFAULT_VOLUME, GROUP_CALL_VOLUME_MULTIPLIER } from '../../../config'; +import { GROUP_CALL_DEFAULT_VOLUME } from '../../../config'; import buildClassName from '../../../util/buildClassName'; import renderText from '../../common/helpers/renderText'; import { selectChat, selectUser } from '../../../global/selectors'; +import formatGroupCallVolume from './helpers/formatGroupCallVolume'; + import useLang from '../../../hooks/useLang'; import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers'; import useMenuPosition from '../../../hooks/useMenuPosition'; @@ -99,8 +101,7 @@ const GroupCallParticipant: FC = ({ if (hasCustomVolume) { return [ - lang('SpeakingWithVolume', - (participant.volume! / GROUP_CALL_VOLUME_MULTIPLIER).toString()) + lang('SpeakingWithVolume', formatGroupCallVolume(participant)) .replace('%%', '%'), styles.subtitleGreen, ]; @@ -118,9 +119,7 @@ const GroupCallParticipant: FC = ({ } return participant.about ? [participant.about, ''] : [lang('Listening'), styles.subtitleBlue]; - }, [ - isMutedByMe, isRaiseHand, isSelf, hasCustomVolume, isMuted, isSpeaking, participant.about, participant.volume, lang, - ]); + }, [isMutedByMe, isRaiseHand, hasCustomVolume, isMuted, isSpeaking, isSelf, participant, lang]); if (!peer) { return undefined; diff --git a/src/components/calls/group/GroupCallParticipantList.tsx b/src/components/calls/group/GroupCallParticipantList.tsx index fe2cc1b87..61f8404e9 100644 --- a/src/components/calls/group/GroupCallParticipantList.tsx +++ b/src/components/calls/group/GroupCallParticipantList.tsx @@ -35,8 +35,8 @@ const GroupCallParticipantList: FC = ({ loadMoreGroupCallParticipants, } = getActions(); - const participantsIds = useMemo(() => { - return Object.keys(participants || {}); + const orderedParticipantIds = useMemo(() => { + return Object.values(participants || {}).sort(compareParticipants).map((participant) => participant.id); }, [participants]); const handleLoadMoreGroupCallParticipants = useLastCallback(() => { @@ -45,8 +45,8 @@ const GroupCallParticipantList: FC = ({ const [viewportIds, getMore] = useInfiniteScroll( handleLoadMoreGroupCallParticipants, - participantsIds, - participantsIds.length >= participantsCount, + orderedParticipantIds, + orderedParticipantIds.length >= participantsCount, ); return ( @@ -61,6 +61,7 @@ const GroupCallParticipantList: FC = ({ participants[participantId] && ( ) @@ -70,6 +71,17 @@ const GroupCallParticipantList: FC = ({ ); }; +function compareFields(a: T, b: T) { + return Number(b) - Number(a); +} + +function compareParticipants(a: TypeGroupCallParticipant, b: TypeGroupCallParticipant) { + return compareFields(!a.isMuted, !b.isMuted) + || compareFields(a.presentation, b.presentation) + || compareFields(a.video, b.video) + || compareFields(a.raiseHandRating, b.raiseHandRating); +} + export default memo(withGlobal( (global): StateProps => { const { participantsCount, participants } = selectActiveGroupCall(global) || {}; diff --git a/src/components/calls/group/GroupCallParticipantMenu.tsx b/src/components/calls/group/GroupCallParticipantMenu.tsx index a492da704..6aff3fdd2 100644 --- a/src/components/calls/group/GroupCallParticipantMenu.tsx +++ b/src/components/calls/group/GroupCallParticipantMenu.tsx @@ -226,7 +226,7 @@ const GroupCallParticipantMenu: FC = ({ {!isSelf && ( // TODO cross mic {isAdmin diff --git a/src/components/calls/group/GroupCallParticipantVideo.module.scss b/src/components/calls/group/GroupCallParticipantVideo.module.scss index 37a2f7ec4..c779a04ca 100644 --- a/src/components/calls/group/GroupCallParticipantVideo.module.scss +++ b/src/components/calls/group/GroupCallParticipantVideo.module.scss @@ -141,6 +141,7 @@ overflow: hidden; white-space: nowrap; font-size: 1rem; + --emoji-size: 1rem; } } @@ -153,38 +154,3 @@ .icon { margin-left: auto; } - -.hidePresentation { - visibility: hidden; -} - -.ownPresentation { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 3; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - gap: 0.75rem; - width: 100%; - height: 100%; - background: rgb(0, 0, 0, 0.6); - font-weight: 500; - font-size: 0.875rem; - border-radius: inherit; -} - -.ownPresentationText { - max-width: 70%; - text-align: center; -} - -.stopSharingButton { - width: auto; - min-width: 6rem; - font-weight: 500; - max-height: 2.25rem; -} diff --git a/src/components/calls/group/GroupCallParticipantVideo.tsx b/src/components/calls/group/GroupCallParticipantVideo.tsx index 8b7fd01a4..7a23a5097 100644 --- a/src/components/calls/group/GroupCallParticipantVideo.tsx +++ b/src/components/calls/group/GroupCallParticipantVideo.tsx @@ -8,13 +8,16 @@ import type { VideoLayout, VideoParticipant } from './hooks/useGroupCallVideoLay import type { GroupCallParticipant as TypeGroupCallParticipant } from '../../../lib/secret-sauce'; import type { ApiChat, ApiUser } from '../../../api/types'; -import { GROUP_CALL_DEFAULT_VOLUME, GROUP_CALL_VOLUME_MULTIPLIER } from '../../../config'; +import { IS_CANVAS_FILTER_SUPPORTED } from '../../../util/windowEnvironment'; +import { GROUP_CALL_DEFAULT_VOLUME } from '../../../config'; import { getUserStreams, THRESHOLD } from '../../../lib/secret-sauce'; import buildClassName from '../../../util/buildClassName'; import { selectChat, selectUser } from '../../../global/selectors'; import { animate } from '../../../util/animation'; import { fastRaf } from '../../../util/schedulers'; import { requestMutation } from '../../../lib/fasterdom/fasterdom'; +import formatGroupCallVolume from './helpers/formatGroupCallVolume'; +import fastBlur from '../../../lib/fastBlur'; import useLang from '../../../hooks/useLang'; import useContextMenuHandlers from '../../../hooks/useContextMenuHandlers'; @@ -30,6 +33,8 @@ import Skeleton from '../../ui/Skeleton'; import styles from './GroupCallParticipantVideo.module.scss'; +const BLUR_RADIUS = 2; +const BLUR_ITERATIONS = 2; const VIDEO_FALLBACK_UPDATE_INTERVAL = 1000; type OwnProps = { @@ -39,7 +44,6 @@ type OwnProps = { canPin: boolean; participant: TypeGroupCallParticipant; className?: string; - onStopSharing: VoidFunction; }; type StateProps = { @@ -56,7 +60,6 @@ const GroupCallParticipantVideo: FC = ({ participant, user, chat, - onStopSharing, }) => { const lang = useLang(); @@ -78,7 +81,6 @@ const GroupCallParticipantVideo: FC = ({ const isSpeaking = (participant.amplitude || 0) > THRESHOLD; const isRaiseHand = Boolean(participant.raiseHandRating); const shouldFlipVideo = type === 'video' && participant.isSelf; - const shouldHidePresentation = type === 'screen' && participant.isSelf; const status = useMemo(() => { if (isSelf) { @@ -98,13 +100,12 @@ const GroupCallParticipantVideo: FC = ({ } if (participant.volume && participant.volume !== GROUP_CALL_DEFAULT_VOLUME) { - return lang('SpeakingWithVolume', - (participant.volume / GROUP_CALL_VOLUME_MULTIPLIER).toString()) + return lang('SpeakingWithVolume', formatGroupCallVolume(participant)) .replace('%%', '%'); } return lang('Speaking'); - }, [isSpeaking, participant.volume, lang, isSelf, isMutedByMe, isRaiseHand, isMuted]); + }, [isSelf, isMutedByMe, isRaiseHand, isMuted, isSpeaking, participant, lang]); const prevLayoutRef = useRef(); if (!isRemoved) { @@ -156,9 +157,8 @@ const GroupCallParticipantVideo: FC = ({ // every VIDEO_FALLBACK_UPDATE_INTERVAL milliseconds. useInterval(() => { if (!stream?.active) return; - const video = videoRef.current; - const canvas = videoFallbackRef.current; - if (!video || !canvas) return; + const video = videoRef.current!; + const canvas = videoFallbackRef.current!; requestMutation(() => { canvas.width = video.videoWidth; @@ -188,6 +188,9 @@ const GroupCallParticipantVideo: FC = ({ return false; } ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, thumbnail.width, thumbnail.height); + if (!IS_CANVAS_FILTER_SUPPORTED) { + fastBlur(ctx, 0, 0, thumbnail.width, thumbnail.height, BLUR_RADIUS, BLUR_ITERATIONS); + } return true; }; @@ -271,9 +274,7 @@ const GroupCallParticipantVideo: FC = ({ )} {stream && (