From e99675cf57d3c1a4e276f73e1915525a75d381fb Mon Sep 17 00:00:00 2001 From: Ravi theja Date: Mon, 11 Mar 2024 22:10:58 +0530 Subject: [PATCH 01/28] fix: video tile size when res changes in sidebar (#2688) --- .../src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx index 9ed895d2d8..428ea1b49e 100644 --- a/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/ProminenceLayout.tsx @@ -70,8 +70,8 @@ const SecondarySection = ({ rootCSS={{ padding: 0, maxWidth: 240, - h: hasSidebar ? undefined : '100%', aspectRatio: 16 / 9, + ...(hasSidebar ? { w: '100%' } : { h: '100%' }), '@md': { aspectRatio: 1 }, }} objectFit="contain" From a81e46c1952d5a366f738bafbcc904bfc955e5c9 Mon Sep 17 00:00:00 2001 From: Ravi theja Date: Tue, 12 Mar 2024 10:50:40 +0530 Subject: [PATCH 02/28] Merge main to dev (#2691) * fix: video tile size when res changes in sidebar * revert: type 'module' as it causing some build issues --- packages/hls-player/package.json | 1 - packages/hls-stats/package.json | 1 - packages/hms-video-store/jest.config.js | 2 +- packages/hms-video-store/package.json | 1 - packages/hms-virtual-background/package.json | 1 - packages/react-icons/package.json | 1 - packages/react-sdk/package.json | 1 - packages/roomkit-react/package.json | 1 - packages/roomkit-web/package.json | 1 - 9 files changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/hls-player/package.json b/packages/hls-player/package.json index 47db5083c0..22051e3eb0 100644 --- a/packages/hls-player/package.json +++ b/packages/hls-player/package.json @@ -5,7 +5,6 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "type": "module", "repository": { "type": "git", "url": "https://github.com/100mslive/web-sdks.git", diff --git a/packages/hls-stats/package.json b/packages/hls-stats/package.json index 9607bdfa84..5da917ca71 100644 --- a/packages/hls-stats/package.json +++ b/packages/hls-stats/package.json @@ -5,7 +5,6 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "type": "module", "repository": { "type": "git", "url": "https://github.com/100mslive/web-sdks.git", diff --git a/packages/hms-video-store/jest.config.js b/packages/hms-video-store/jest.config.js index b78c73e9ad..5c19a40542 100644 --- a/packages/hms-video-store/jest.config.js +++ b/packages/hms-video-store/jest.config.js @@ -10,4 +10,4 @@ const config = { testEnvironment: 'jsdom', }; -export default config; +module.exports = config; diff --git a/packages/hms-video-store/package.json b/packages/hms-video-store/package.json index 1e71e69d55..5c07956221 100644 --- a/packages/hms-video-store/package.json +++ b/packages/hms-video-store/package.json @@ -9,7 +9,6 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "type": "module", "files": [ "dist", "src" diff --git a/packages/hms-virtual-background/package.json b/packages/hms-virtual-background/package.json index f510bdba38..4f105d174b 100755 --- a/packages/hms-virtual-background/package.json +++ b/packages/hms-virtual-background/package.json @@ -6,7 +6,6 @@ "module": "dist/index.js", "main": "dist/index.cjs.js", "typings": "dist/index.d.ts", - "type": "module", "repository": { "type": "git", "url": "https://github.com/100mslive/web-sdks.git", diff --git a/packages/react-icons/package.json b/packages/react-icons/package.json index 4ecb487707..5b3b3ed759 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -4,7 +4,6 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "type": "module", "version": "0.10.1", "author": "100ms", "license": "MIT", diff --git a/packages/react-sdk/package.json b/packages/react-sdk/package.json index f42ecf9431..2bb16efd01 100644 --- a/packages/react-sdk/package.json +++ b/packages/react-sdk/package.json @@ -4,7 +4,6 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "type": "module", "version": "0.10.1", "author": "100ms", "license": "MIT", diff --git a/packages/roomkit-react/package.json b/packages/roomkit-react/package.json index 0ede82f759..05ac1996c9 100644 --- a/packages/roomkit-react/package.json +++ b/packages/roomkit-react/package.json @@ -4,7 +4,6 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "type": "module", "keywords": [ "100mslive", "react", diff --git a/packages/roomkit-web/package.json b/packages/roomkit-web/package.json index 4f4837b032..ca0e5f035e 100644 --- a/packages/roomkit-web/package.json +++ b/packages/roomkit-web/package.json @@ -11,7 +11,6 @@ "main": "dist/index.cjs.js", "module": "dist/index.js", "typings": "dist/index.d.ts", - "type": "module", "author": "100ms", "license": "MIT", "repository": { From 7d6852ddab56b7ab045d3a50d5d9569ff12c8a99 Mon Sep 17 00:00:00 2001 From: Kaustubh Kumar Date: Tue, 12 Mar 2024 14:06:58 +0530 Subject: [PATCH 03/28] fix: rename downlink to cqs --- packages/roomkit-react/src/Stats/Stats.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roomkit-react/src/Stats/Stats.tsx b/packages/roomkit-react/src/Stats/Stats.tsx index 5b37d1c70b..453b9275ee 100644 --- a/packages/roomkit-react/src/Stats/Stats.tsx +++ b/packages/roomkit-react/src/Stats/Stats.tsx @@ -120,7 +120,7 @@ export function VideoTileStats({ videoTrackID, audioTrackID, peerID, isLocal = f value={formatBytes(audioTrackStats?.bitrate, 'b/s')} /> - + From 99513a1371a50a309a4776f9edbfde19d12ed41e Mon Sep 17 00:00:00 2001 From: Kaustubh Kumar Date: Tue, 12 Mar 2024 16:01:00 +0530 Subject: [PATCH 04/28] fix: radio/checkbox shrink for longer options --- packages/roomkit-react/src/Prebuilt/common/utils.js | 2 +- .../components/Polls/common/MultipleChoiceOptions.jsx | 3 ++- .../components/Polls/common/OptionInputWithDelete.tsx | 1 + .../Prebuilt/components/Polls/common/SingleChoiceOptions.jsx | 4 +++- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/roomkit-react/src/Prebuilt/common/utils.js b/packages/roomkit-react/src/Prebuilt/common/utils.js index f49820e37b..10f5981840 100644 --- a/packages/roomkit-react/src/Prebuilt/common/utils.js +++ b/packages/roomkit-react/src/Prebuilt/common/utils.js @@ -112,7 +112,7 @@ export const checkCorrectAnswer = (answer, localPeerResponse, type) => { } }; -export const isValidTextInput = (text, minLength = 1, maxLength = 100) => { +export const isValidTextInput = (text, minLength = 1, maxLength = 1024) => { return text && text.length >= minLength && text.length <= maxLength; }; diff --git a/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx b/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx index 4b01c78070..88eed72912 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +++ b/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx @@ -33,7 +33,7 @@ export const MultipleChoiceOptions = ({ {options.map(option => { return ( - + {!isStopped || !isQuiz ? ( handleCheckedChange(checked, option.index)} css={{ cursor: canRespond ? 'pointer' : 'not-allowed', + flexShrink: 0, }} > diff --git a/packages/roomkit-react/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx b/packages/roomkit-react/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx index 5f4f4b9bbf..d4b5416ae4 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Polls/common/OptionInputWithDelete.tsx @@ -28,6 +28,7 @@ export const OptionInputWithDelete = ({ value={option?.text || ''} key={index} onChange={event => handleOptionTextChange(index, event.target.value.trimStart())} + maxLength={250} /> removeOption(index)} css={{ bg: 'transparent', border: 'none' }}> diff --git a/packages/roomkit-react/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx b/packages/roomkit-react/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx index f821e3a39d..172bf70aa5 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx +++ b/packages/roomkit-react/src/Prebuilt/components/Polls/common/SingleChoiceOptions.jsx @@ -23,7 +23,7 @@ export const SingleChoiceOptions = ({ {options.map(option => { return ( - + {!isStopped || !isQuiz ? ( Date: Tue, 12 Mar 2024 18:08:24 +0530 Subject: [PATCH 05/28] fix: calcuating difference average, av sync and remove unnecessary fields for subscriber stats sample --- .../src/analytics/stats/BaseStatsAnalytics.ts | 4 ++- .../stats/SubscribeStatsAnalytics.ts | 25 +++++++++++++------ .../src/interfaces/webrtc-stats.ts | 1 + 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/hms-video-store/src/analytics/stats/BaseStatsAnalytics.ts b/packages/hms-video-store/src/analytics/stats/BaseStatsAnalytics.ts index 179815a397..b0d16adb0c 100644 --- a/packages/hms-video-store/src/analytics/stats/BaseStatsAnalytics.ts +++ b/packages/hms-video-store/src/analytics/stats/BaseStatsAnalytics.ts @@ -85,6 +85,7 @@ export abstract class RunningTrackAnalytics { protected samples: (LocalBaseSample | LocalVideoSample | RemoteAudioSample | RemoteVideoSample)[] = []; protected tempStats: TempPublishStats[] = []; + protected prevLatestStat?: TempPublishStats; constructor({ track, @@ -118,6 +119,7 @@ export abstract class RunningTrackAnalytics { } this.samples.push(this.collateSample()); + this.prevLatestStat = this.getLatestStat(); this.tempStats.length = 0; } @@ -160,7 +162,7 @@ export abstract class RunningTrackAnalytics { } protected calculateDifferenceForSample(key: keyof TempPublishStats) { - const firstValue = Number(this.tempStats[0][key]) || 0; + const firstValue = Number(this.prevLatestStat?.[key]) || 0; const latestValue = Number(this.getLatestStat()[key]) || 0; return latestValue - firstValue; diff --git a/packages/hms-video-store/src/analytics/stats/SubscribeStatsAnalytics.ts b/packages/hms-video-store/src/analytics/stats/SubscribeStatsAnalytics.ts index 5c44720388..1ca7567d5f 100644 --- a/packages/hms-video-store/src/analytics/stats/SubscribeStatsAnalytics.ts +++ b/packages/hms-video-store/src/analytics/stats/SubscribeStatsAnalytics.ts @@ -92,12 +92,16 @@ export class SubscribeStatsAnalytics extends BaseStatsAnalytics { // eslint-disable-next-line complexity private calculateAvSyncForStat(trackStats: HMSTrackStats, hmsStats: HMSWebrtcStats) { - if (!trackStats.peerID || !(trackStats.kind === 'video')) { + if (!trackStats.peerID || !trackStats.estimatedPlayoutTimestamp || trackStats.kind !== 'video') { return; } const peer = this.store.getPeerById(trackStats.peerID); const audioTrack = peer?.audioTrack; const videoTrack = peer?.videoTrack; + /** + * 1. Send value as MAX_SAFE_INTEGER when either audio or value track is muted for the entire window + * 2. When both audio and video are unmuted for a part of window , then divide the difference by those many number of samples only + */ const areBothTracksEnabled = audioTrack && videoTrack && audioTrack.enabled && videoTrack.enabled; if (!areBothTracksEnabled) { return MAX_SAFE_INTEGER; @@ -106,7 +110,12 @@ export class SubscribeStatsAnalytics extends BaseStatsAnalytics { if (!audioStats) { return MAX_SAFE_INTEGER; } - return audioStats.timestamp - trackStats.timestamp; + if (!audioStats.estimatedPlayoutTimestamp) { + return; + } + + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-estimatedplayouttimestamp + return audioStats.estimatedPlayoutTimestamp - trackStats.estimatedPlayoutTimestamp; } } @@ -119,14 +128,9 @@ class RunningRemoteTrackAnalytics extends RunningTrackAnalytics { const baseSample = { timestamp: Date.now(), - fec_packets_discarded: this.calculateDifferenceForSample('fecPacketsDiscarded'), - fec_packets_received: this.calculateDifferenceForSample('fecPacketsReceived'), - total_samples_duration: this.calculateDifferenceForSample('totalSamplesDuration'), - total_packets_received: this.calculateDifferenceForSample('packetsReceived'), - total_packets_lost: this.calculateDifferenceForSample('packetsLost'), total_pli_count: this.calculateDifferenceForSample('pliCount'), total_nack_count: this.calculateDifferenceForSample('nackCount'), - avg_jitter_buffer_delay: this.calculateAverage('calculatedJitterBufferDelay'), + avg_jitter_buffer_delay: this.calculateAverage('calculatedJitterBufferDelay', false), }; if (latestStat.kind === 'video') { @@ -155,6 +159,11 @@ class RunningRemoteTrackAnalytics extends RunningTrackAnalytics { audio_concealed_samples, audio_total_samples_received: this.calculateDifferenceForSample('totalSamplesReceived'), audio_concealment_events: this.calculateDifferenceForSample('concealmentEvents'), + fec_packets_discarded: this.calculateDifferenceForSample('fecPacketsDiscarded'), + fec_packets_received: this.calculateDifferenceForSample('fecPacketsReceived'), + total_samples_duration: this.calculateDifferenceForSample('totalSamplesDuration'), + total_packets_received: this.calculateDifferenceForSample('packetsReceived'), + total_packets_lost: this.calculateDifferenceForSample('packetsLost'), }); } }; diff --git a/packages/hms-video-store/src/interfaces/webrtc-stats.ts b/packages/hms-video-store/src/interfaces/webrtc-stats.ts index edcf6452d5..05d5613e25 100644 --- a/packages/hms-video-store/src/interfaces/webrtc-stats.ts +++ b/packages/hms-video-store/src/interfaces/webrtc-stats.ts @@ -57,6 +57,7 @@ export interface MissingInboundStats extends RTCInboundRtpStreamStats, MissingCo totalFreezesDuration?: number; jitterBufferDelay?: number; jitterBufferEmittedCount?: number; + estimatedPlayoutTimestamp?: DOMHighResTimeStamp; } export type PeerConnectionType = 'publish' | 'subscribe'; From aa32029cb32b3505017622d9ddd774f6a4c026f1 Mon Sep 17 00:00:00 2001 From: Kaustubh Kumar Date: Fri, 15 Mar 2024 11:37:20 +0530 Subject: [PATCH 06/28] fix: save correct answers in draft quizzes --- .../HMSInteractivityCenter.ts | 14 +++++++++++++- .../Polls/CreateQuestions/CreateQuestions.jsx | 12 ++++++------ .../Polls/CreateQuestions/SavedQuestion.tsx | 16 ++++++++++++++-- .../Polls/common/MultipleChoiceOptions.jsx | 2 +- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/packages/hms-video-store/src/session-store/interactivity-center/HMSInteractivityCenter.ts b/packages/hms-video-store/src/session-store/interactivity-center/HMSInteractivityCenter.ts index 4e4d93a5ed..95fc81de7a 100644 --- a/packages/hms-video-store/src/session-store/interactivity-center/HMSInteractivityCenter.ts +++ b/packages/hms-video-store/src/session-store/interactivity-center/HMSInteractivityCenter.ts @@ -195,10 +195,23 @@ export class InteractivityCenter implements HMSInteractivityCenter { return polls; } + // eslint-disable-next-line complexity private createQuestionSetParams(questionParams: HMSPollQuestionCreateParams, index: number): PollQuestionParams { + // early return if the question has been saved before in a draft + if (questionParams.index) { + const optionsWithIndex = questionParams.options?.map((option, index) => { + return { ...option, index: index + 1 }; + }); + return { + question: { ...questionParams, index: index + 1 }, + options: optionsWithIndex, + answer: questionParams.answer, + }; + } const question: PollQuestionParams['question'] = { ...questionParams, index: index + 1 }; let options: HMSPollQuestionOption[] | undefined; const answer: HMSPollQuestionAnswer = questionParams.answer || { hidden: false }; + if ( Array.isArray(questionParams.options) && [HMSPollQuestionType.SINGLE_CHOICE, HMSPollQuestionType.MULTIPLE_CHOICE].includes(questionParams.type) @@ -209,7 +222,6 @@ export class InteractivityCenter implements HMSInteractivityCenter { weight: option.weight, })); - delete answer?.text; if (questionParams.type === HMSPollQuestionType.SINGLE_CHOICE) { answer.option = questionParams.options.findIndex(option => option.isCorrectAnswer) + 1 || undefined; } else { diff --git a/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx b/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx index 23d5319ee5..a97b83f2e3 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx +++ b/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/CreateQuestions.jsx @@ -75,15 +75,15 @@ export function CreateQuestions() { const updatedQuestions = [...questions.slice(0, index), questionParams, ...questions.slice(index + 1)]; setQuestions(updatedQuestions); const validQuestions = updatedQuestions.filter(question => isValidQuestion(question)); - await actions.interactivityCenter.addQuestionsToPoll(id, validQuestions); }} isQuiz={isQuiz} - removeQuestion={questionID => - setQuestions(prev => { - return prev.filter(questionFromSet => questionID !== questionFromSet?.draftID); - }) - } + removeQuestion={async questionID => { + const updatedQuestions = questions.filter(questionFromSet => questionID !== questionFromSet?.draftID); + setQuestions(updatedQuestions); + const validQuestions = updatedQuestions.filter(question => isValidQuestion(question)); + await actions.interactivityCenter.addQuestionsToPoll(id, validQuestions); + }} convertToDraft={questionID => setQuestions(prev => { const copyOfQuestions = [...prev]; diff --git a/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.tsx b/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.tsx index 526f003734..5878ae2ca9 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Polls/CreateQuestions/SavedQuestion.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { HMSPollQuestion } from '@100mslive/react-sdk'; import { CheckCircleIcon } from '@100mslive/react-icons'; import { Button, Flex, Text } from '../../../../'; @@ -15,6 +15,18 @@ export const SavedQuestion = ({ length: number; convertToDraft: (draftID: number) => void; }) => { + const answerArray = useMemo(() => { + const updatedAnswerArray = []; + const { option, options } = question?.answer ?? {}; + if (option) { + updatedAnswerArray.push(option); + } + if (options) { + updatedAnswerArray.push(...options); + } + return updatedAnswerArray; + }, [question?.answer]); + return ( <> @@ -30,7 +42,7 @@ export const SavedQuestion = ({ {option.text} {/* @ts-ignore */} - {option.isCorrectAnswer && ( + {(answerArray.includes(index + 1) || option.isCorrectAnswer) && ( diff --git a/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx b/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx index 88eed72912..bc779d9120 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx +++ b/packages/roomkit-react/src/Prebuilt/components/Polls/common/MultipleChoiceOptions.jsx @@ -52,7 +52,7 @@ export const MultipleChoiceOptions = ({ ) : null} {isStopped && correctOptionIndexes?.includes(option.index) ? ( - + ) : null} From ea856832343f6038828290fc82a6b40f00c46804 Mon Sep 17 00:00:00 2001 From: Kaustubh Kumar Date: Fri, 15 Mar 2024 11:54:41 +0530 Subject: [PATCH 07/28] feat: calculate total usage for plugin --- .../src/analytics/AnalyticsEventFactory.ts | 18 +++++++ .../src/common/PluginUsageTracker.ts | 53 +++++++++++++++++++ packages/hms-video-store/src/common/index.ts | 1 + packages/hms-video-store/src/index.ts | 1 + .../plugins/audio/AudioPluginsAnalytics.ts | 1 + packages/hms-video-store/src/sdk/index.ts | 5 +- .../hms-video-store/src/transport/index.ts | 3 ++ 7 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 packages/hms-video-store/src/common/PluginUsageTracker.ts create mode 100644 packages/hms-video-store/src/common/index.ts diff --git a/packages/hms-video-store/src/analytics/AnalyticsEventFactory.ts b/packages/hms-video-store/src/analytics/AnalyticsEventFactory.ts index 1c897ab980..8a70bf8259 100644 --- a/packages/hms-video-store/src/analytics/AnalyticsEventFactory.ts +++ b/packages/hms-video-store/src/analytics/AnalyticsEventFactory.ts @@ -3,6 +3,7 @@ import { AdditionalAnalyticsProperties } from './AdditionalAnalyticsProperties'; import AnalyticsEvent from './AnalyticsEvent'; import { AnalyticsEventLevel } from './AnalyticsEventLevel'; import { IAnalyticsPropertiesProvider } from './IAnalyticsPropertiesProvider'; +import { pluginUsageTracker } from '../common'; import { HMSException } from '../error/HMSException'; import { DeviceMap, SelectedDevices } from '../interfaces'; import { HMSTrackSettings } from '../media/settings/HMSTrackSettings'; @@ -235,6 +236,23 @@ export default class AnalyticsEventFactory { }); } + static getKrispUsage(sessionID: string) { + const duration = pluginUsageTracker.getPluginUsage('HMSKrispPlugin', sessionID); + return new AnalyticsEvent({ + name: 'krisp.usage', + level: AnalyticsEventLevel.INFO, + properties: { duration }, + }); + } + + static transportLeave() { + return new AnalyticsEvent({ + name: 'transport.leave', + level: AnalyticsEventLevel.INFO, + properties: {}, + }); + } + private static eventNameFor(name: string, ok: boolean) { const suffix = ok ? 'success' : 'failed'; return `${name}.${suffix}`; diff --git a/packages/hms-video-store/src/common/PluginUsageTracker.ts b/packages/hms-video-store/src/common/PluginUsageTracker.ts new file mode 100644 index 0000000000..248550ff41 --- /dev/null +++ b/packages/hms-video-store/src/common/PluginUsageTracker.ts @@ -0,0 +1,53 @@ +import AnalyticsEvent from '../analytics/AnalyticsEvent'; + +class PluginUsageTracker { + private pluginUsage: Map = new Map(); + private pluginLastAddedAt: Map = new Map(); + + getPluginUsage = (name: string, sessionID: string) => { + const pluginKey = `${sessionID}-${name}`; + + if (!this.pluginUsage.has(pluginKey)) { + this.pluginUsage.set(pluginKey, 0); + } + if (this.pluginLastAddedAt.has(pluginKey)) { + const lastAddedAt = this.pluginLastAddedAt.get(pluginKey) || 0; + const extraDuration = lastAddedAt ? Date.now() - lastAddedAt : 0; + this.pluginUsage.set(pluginKey, (this.pluginUsage.get(pluginKey) || 0) + extraDuration); + this.pluginLastAddedAt.delete(pluginKey); + } + return this.pluginUsage.get(pluginKey); + }; + + updatePluginUsageData = (event: AnalyticsEvent, sessionID: string) => { + // Sent on leave, after krisp usage is sent + if (event.name === 'transport.leave') { + this.cleanup(sessionID); + return; + } + + const name = event.properties.plugin_name; + const pluginKey = `${sessionID}-${name}`; + if (event.name === 'mediaPlugin.added') { + const addedAt = event.properties.added_at; + this.pluginLastAddedAt.set(pluginKey, addedAt); + } else if (event.name === 'mediaPlugin.stats') { + const duration = event.properties.duration; + if (duration > 0) { + this.pluginUsage.set(pluginKey, (this.pluginUsage.get(pluginKey) || 0) + duration * 1000); + this.pluginLastAddedAt.delete(pluginKey); + } + } + }; + + private cleanup = (sessionID: string) => { + for (const key of this.pluginUsage.keys()) { + if (sessionID.length && key.includes(sessionID)) { + this.pluginUsage.delete(key); + this.pluginLastAddedAt.delete(key); + } + } + }; +} + +export const pluginUsageTracker = new PluginUsageTracker(); diff --git a/packages/hms-video-store/src/common/index.ts b/packages/hms-video-store/src/common/index.ts new file mode 100644 index 0000000000..44f3432f1e --- /dev/null +++ b/packages/hms-video-store/src/common/index.ts @@ -0,0 +1 @@ +export { pluginUsageTracker } from './PluginUsageTracker'; diff --git a/packages/hms-video-store/src/index.ts b/packages/hms-video-store/src/index.ts index dfc3890b9f..2cce36b2dd 100644 --- a/packages/hms-video-store/src/index.ts +++ b/packages/hms-video-store/src/index.ts @@ -53,6 +53,7 @@ export type { HMSQuizLeaderboardSummary, } from './internal'; +export { pluginUsageTracker } from './common'; export { HMSReactiveStore } from './reactive-store/HMSReactiveStore'; export { HMSPluginUnsupportedTypes, HMSRecordingState, HLSPlaylistType } from './internal'; export type { diff --git a/packages/hms-video-store/src/plugins/audio/AudioPluginsAnalytics.ts b/packages/hms-video-store/src/plugins/audio/AudioPluginsAnalytics.ts index 08801d07eb..30629c4bdf 100644 --- a/packages/hms-video-store/src/plugins/audio/AudioPluginsAnalytics.ts +++ b/packages/hms-video-store/src/plugins/audio/AudioPluginsAnalytics.ts @@ -24,6 +24,7 @@ export class AudioPluginsAnalytics { this.addedTimestamps[name] = Date.now(); this.initTime[name] = 0; this.pluginSampleRate[name] = sampleRate; + this.eventBus.analytics.publish(MediaPluginsAnalyticsFactory.added(name, this.addedTimestamps[name])); } removed(name: string) { diff --git a/packages/hms-video-store/src/sdk/index.ts b/packages/hms-video-store/src/sdk/index.ts index f5c263a5d4..5d8de0e8ea 100644 --- a/packages/hms-video-store/src/sdk/index.ts +++ b/packages/hms-video-store/src/sdk/index.ts @@ -12,6 +12,7 @@ import { HMSAnalyticsLevel } from '../analytics/AnalyticsEventLevel'; import { AnalyticsEventsService } from '../analytics/AnalyticsEventsService'; import { AnalyticsTimer, TimedEvent } from '../analytics/AnalyticsTimer'; import { AudioSinkManager } from '../audio-sink-manager'; +import { pluginUsageTracker } from '../common/PluginUsageTracker'; import { DeviceManager } from '../device-manager'; import { AudioOutputManager } from '../device-manager/AudioOutputManager'; import { DeviceStorageManager } from '../device-manager/DeviceStorage'; @@ -181,7 +182,6 @@ export class HMSSdk implements HMSInterface { ); this.sessionStore = new SessionStore(this.transport); this.interactivityCenter = new InteractivityCenter(this.transport, this.store, this.listener); - /** * Note: Subscribe to events here right after creating stores and managers * to not miss events that are published before the handlers are subscribed. @@ -498,6 +498,7 @@ export class HMSSdk implements HMSInterface { this.errorListener?.onError(error); }; + // eslint-disable-next-line complexity async join(config: HMSConfig, listener: HMSUpdateListener) { validateMediaDevicesExistence(); validateRTCPeerConnection(); @@ -571,6 +572,8 @@ export class HMSSdk implements HMSInterface { throw error; } HMSLogger.timeEnd(`join-room-${roomId}`); + const sessionID = this.store.getRoom()?.sessionId || ''; + this.eventBus.analytics.subscribe(e => pluginUsageTracker.updatePluginUsageData(e, sessionID)); } private stringifyMetadata(config: HMSConfig) { diff --git a/packages/hms-video-store/src/transport/index.ts b/packages/hms-video-store/src/transport/index.ts index fc6a168e99..a4f45f8a5d 100644 --- a/packages/hms-video-store/src/transport/index.ts +++ b/packages/hms-video-store/src/transport/index.ts @@ -495,6 +495,9 @@ export default class HMSTransport { this.joinParameters = undefined; HMSLogger.d(TAG, 'leaving in transport'); try { + const sessionID = this.store.getRoom()?.sessionId || ''; + this.eventBus.analytics.publish(AnalyticsEventFactory.getKrispUsage(sessionID)); + this.eventBus.analytics.publish(AnalyticsEventFactory.transportLeave()); this.state = TransportState.Leaving; this.publishStatsAnalytics?.stop(); this.subscribeStatsAnalytics?.stop(); From aeadfc9452709564f74f0a87ee51421a3b79ae6f Mon Sep 17 00:00:00 2001 From: Kaustubh Kumar Date: Fri, 15 Mar 2024 14:46:17 +0530 Subject: [PATCH 08/28] fix: use more efficient preset for effects vb (#2706) --- packages/hms-virtual-background/src/HMSEffectsPlugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hms-virtual-background/src/HMSEffectsPlugin.ts b/packages/hms-virtual-background/src/HMSEffectsPlugin.ts index b8a3ed5f5b..372d167ff3 100644 --- a/packages/hms-virtual-background/src/HMSEffectsPlugin.ts +++ b/packages/hms-virtual-background/src/HMSEffectsPlugin.ts @@ -11,7 +11,7 @@ export class HMSEffectsPlugin implements HMSMediaStreamPlugin { private blurAmount = 0; private background: HMSEffectsBackground = HMSVirtualBackgroundTypes.NONE; private backgroundType = HMSVirtualBackgroundTypes.NONE; - private preset = 'balanced'; + private preset = 'lightning'; constructor(effectsSDKKey: string) { this.effects = new tsvb(effectsSDKKey); From 0ba911f260153022ba63be1330af058167a2cfab Mon Sep 17 00:00:00 2001 From: Kaustubh Kumar Date: Fri, 15 Mar 2024 14:46:51 +0530 Subject: [PATCH 09/28] fix: casing for lower all hands --- .../src/Prebuilt/components/Footer/RoleAccordion.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx b/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx index 0eec2bef23..ce931ae60c 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx @@ -153,7 +153,7 @@ export const RoleAccordion = ({ {canBringToStage && ( - {shouldShowStageRoleChange && !isInStage && ( + {!isInStage && ( diff --git a/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx b/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx index 34269539a9..5bb8bf5c80 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Footer/RoleAccordion.tsx @@ -149,18 +149,17 @@ export const RoleAccordion = ({ ) : null} - {isHandRaisedAccordion && ( + {isHandRaisedAccordion && canBringToStage && ( <> - {canBringToStage && ( - - )} + + )} From 09e61ffc49a576d87ddf34005d2ca6526011726f Mon Sep 17 00:00:00 2001 From: Kaustubh Kumar Date: Tue, 19 Mar 2024 13:03:30 +0530 Subject: [PATCH 16/28] ci: reduce publish timeout --- .github/workflows/publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e0d8c94841..37314fb1be 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -64,8 +64,8 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} PUBLISH_FLAG: ${{ github.event.inputs.publishFlag || github.event.client_payload.slash_command.args.unnamed.arg1 || 'alpha' }} - - name: Delay for 2 minutes - run: sleep 120 + - name: Delay for 1 minute + run: sleep 60 - name: Notify slack success if: github.event.inputs.publishFlag == 'latest' && success() From ab349b42c7bba2768676665b35290fb76c4a8fb3 Mon Sep 17 00:00:00 2001 From: "Eswar Prasad Clinton. A" <64120992+eswarclynn@users.noreply.github.com> Date: Tue, 19 Mar 2024 18:11:20 +0530 Subject: [PATCH 17/28] fix: whiteboard layout, toggle with screenshare --- .../src/IconButton/IconButton.tsx | 9 +- .../roomkit-react/src/Prebuilt/IconButton.tsx | 16 ++-- .../components/Footer/WhiteboardToggle.tsx | 20 +++- .../IconButtonWithOptions.tsx | 14 +-- .../components/VideoLayouts/GridLayout.tsx | 31 +++++- .../VideoLayouts/WhiteboardLayout.tsx | 95 +++++++++++++++++++ .../hooks/useCloseScreenshareWhiteboard.tsx | 18 +++- .../layouts/VideoStreamingSection.tsx | 18 +--- .../src/Prebuilt/layouts/WhiteboardView.tsx | 69 -------------- 9 files changed, 168 insertions(+), 122 deletions(-) create mode 100644 packages/roomkit-react/src/Prebuilt/components/VideoLayouts/WhiteboardLayout.tsx delete mode 100644 packages/roomkit-react/src/Prebuilt/layouts/WhiteboardView.tsx diff --git a/packages/roomkit-react/src/IconButton/IconButton.tsx b/packages/roomkit-react/src/IconButton/IconButton.tsx index c66cc5b0a8..297137cfb6 100644 --- a/packages/roomkit-react/src/IconButton/IconButton.tsx +++ b/packages/roomkit-react/src/IconButton/IconButton.tsx @@ -25,19 +25,14 @@ export const IconButton = styled('button', { '&[disabled]': { opacity: 0.5, cursor: 'not-allowed', + backgroundColor: '$secondary_dim', + color: '$on_primary_high', }, '&:focus': { outline: 'none', }, variants: { active: { - false: { - backgroundColor: '$secondary_dim', - color: '$on_primary_high', - '&:not([disabled]):hover': { - backgroundColor: '$secondary_default', - }, - }, true: { '&:not([disabled]):hover': { backgroundColor: '$on_surface_low', diff --git a/packages/roomkit-react/src/Prebuilt/IconButton.tsx b/packages/roomkit-react/src/Prebuilt/IconButton.tsx index 3694779ad8..e66d9a6330 100644 --- a/packages/roomkit-react/src/Prebuilt/IconButton.tsx +++ b/packages/roomkit-react/src/Prebuilt/IconButton.tsx @@ -7,22 +7,18 @@ const IconButton = styled(BaseIconButton, { border: '1px solid $border_bright', bg: '$background_dim', r: '$1', + '&[disabled]': { + opacity: 0.5, + cursor: 'not-allowed', + backgroundColor: '$secondary_dim', + color: '$on_primary_high', + }, variants: { active: { true: { color: '$on_surface_high', backgroundColor: 'transparent', }, - false: { - border: '1px solid transparent', - color: '$on_primary_high', - }, - }, - disabled: { - true: { - backgroundColor: '$surface_brighter', - color: '$on_surface_low', - }, }, }, }); diff --git a/packages/roomkit-react/src/Prebuilt/components/Footer/WhiteboardToggle.tsx b/packages/roomkit-react/src/Prebuilt/components/Footer/WhiteboardToggle.tsx index a6abde09c3..0cb9efd262 100644 --- a/packages/roomkit-react/src/Prebuilt/components/Footer/WhiteboardToggle.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/Footer/WhiteboardToggle.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useWhiteboard } from '@100mslive/react-sdk'; +import { useScreenShare, useWhiteboard } from '@100mslive/react-sdk'; import { PencilDrawIcon } from '@100mslive/react-icons'; import { Tooltip } from '../../..'; // @ts-ignore: No implicit Any @@ -9,14 +9,28 @@ import { ToastManager } from '../Toast/ToastManager'; export const WhiteboardToggle = () => { const { toggle, open, isOwner } = useWhiteboard(); + const { screenSharingPeerId, amIScreenSharing } = useScreenShare(); + const remoteScreenShare = screenSharingPeerId && !amIScreenSharing; + const disabled = remoteScreenShare || (open && !isOwner); + if (!toggle) { return null; } return ( - + { + if (disabled) { + return; + } try { await toggle(); } catch (error) { @@ -24,7 +38,7 @@ export const WhiteboardToggle = () => { } }} active={!open} - disabled={open && !isOwner} + disabled={disabled} data-testid="whiteboard_btn" > diff --git a/packages/roomkit-react/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx b/packages/roomkit-react/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx index e6e9be3b62..cf36bc6c3f 100644 --- a/packages/roomkit-react/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/IconButtonWithOptions/IconButtonWithOptions.tsx @@ -25,9 +25,9 @@ const IconSection = styled(IconButton, { p: '$4', r: '$1', bg: 'transparent', - borderTopRightRadius: 0, + borderTopRightRadius: '0 !important', borderColor: '$border_bright', - borderBottomRightRadius: 0, + borderBottomRightRadius: '0 !important', position: 'relative', '&:not([disabled]):focus-visible': { zIndex: 1, @@ -41,8 +41,8 @@ const IconSection = styled(IconButton, { ...variants, hideOptions: { true: { - borderTopRightRadius: '$1', - borderBottomRightRadius: '$1', + borderTopRightRadius: '$1 !important', + borderBottomRightRadius: '$1 !important', }, }, }, @@ -53,10 +53,10 @@ const OptionsSection = styled(IconButton, { h: '$14', p: '$4 $2', r: '$1', - borderTopLeftRadius: 0, + borderTopLeftRadius: '0 !important', borderColor: '$border_bright', - borderBottomLeftRadius: 0, - borderLeftWidth: 0, + borderBottomLeftRadius: '0 !important', + borderLeftWidth: '0 !important', position: 'relative', '&:not([disabled]):focus-visible': { zIndex: 1, diff --git a/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/GridLayout.tsx b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/GridLayout.tsx index 6d64429aea..82b1d47186 100644 --- a/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/GridLayout.tsx +++ b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/GridLayout.tsx @@ -5,12 +5,14 @@ import { selectLocalPeerRoleName, selectPeers, selectPeerScreenSharing, + selectWhiteboard, useHMSStore, useHMSVanillaStore, } from '@100mslive/react-sdk'; import { EqualProminence } from './EqualProminence'; import { RoleProminence } from './RoleProminence'; import { ScreenshareLayout } from './ScreenshareLayout'; +import { WhiteboardLayout } from './WhiteboardLayout'; // @ts-ignore: No implicit Any import { usePinnedTrack, useSetAppDataByKey } from '../AppData/useUISettings'; import { VideoTileContext } from '../hooks/useVideoTileLayout'; @@ -40,6 +42,7 @@ export const GridLayout = ({ hide_metadata_on_tile = false, }: GridLayoutProps) => { const peerSharing = useHMSStore(selectPeerScreenSharing); + const whiteboard = useHMSStore(selectWhiteboard); const pinnedTrack = usePinnedTrack(); const peers = useHMSStore(selectPeers); const localPeerRole = useHMSStore(selectLocalPeerRoleName); @@ -53,9 +56,9 @@ export const GridLayout = ({ )) || pinnedTrack; const updatedPeers = useMemo(() => { - // remove screenshare peer from active speaker sorting - if (activeScreensharePeerId) { - return peers.filter(peer => peer.id !== activeScreensharePeerId); + // remove screenshare/whiteboard peer from active speaker sorting + if (activeScreensharePeerId || whiteboard?.open) { + return peers.filter(peer => peer.id !== activeScreensharePeerId || peer.customerUserId !== whiteboard?.owner); } if (isInsetEnabled) { const isLocalPeerPinned = localPeerID === pinnedTrack?.peerId; @@ -67,7 +70,16 @@ export const GridLayout = ({ } } return peers; - }, [isInsetEnabled, activeScreensharePeerId, localPeerRole, localPeerID, prominentRoles, peers, pinnedTrack]); + }, [ + isInsetEnabled, + whiteboard, + activeScreensharePeerId, + localPeerRole, + localPeerID, + prominentRoles, + peers, + pinnedTrack, + ]); const vanillaStore = useHMSVanillaStore(); const [sortedPeers, setSortedPeers] = useState(updatedPeers); const peersSorter = useMemo(() => new PeersSorter(vanillaStore), [vanillaStore]); @@ -104,6 +116,17 @@ export const GridLayout = ({ /> ); + } else if (whiteboard?.open) { + return ( + + + + ); } else if (isRoleProminence) { return ( diff --git a/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/WhiteboardLayout.tsx b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/WhiteboardLayout.tsx new file mode 100644 index 0000000000..56155fec88 --- /dev/null +++ b/packages/roomkit-react/src/Prebuilt/components/VideoLayouts/WhiteboardLayout.tsx @@ -0,0 +1,95 @@ +import React, { useEffect, useMemo } from 'react'; +import { useMedia } from 'react-use'; +import { selectPeerByCondition, selectWhiteboard, useHMSStore, useWhiteboard } from '@100mslive/react-sdk'; +import { Box } from '../../../Layout'; +import { config as cssConfig } from '../../../Theme'; +import { InsetTile } from '../InsetTile'; +import { SecondaryTiles } from '../SecondaryTiles'; +import { LayoutMode } from '../Settings/LayoutSettings'; +import { LayoutProps } from './interface'; +import { ProminenceLayout } from './ProminenceLayout'; +// @ts-ignore: No implicit Any +import { useSetUiSettings } from '../AppData/useUISettings'; +import { UI_SETTINGS } from '../../common/constants'; + +const WhiteboardEmbed = () => { + const isMobile = useMedia(cssConfig.media.md); + const { iframeRef } = useWhiteboard(isMobile); + + return ( + +