Skip to content
This repository has been archived by the owner on Aug 8, 2024. It is now read-only.

Commit

Permalink
feat: sync webapp
Browse files Browse the repository at this point in the history
  • Loading branch information
raviteja83 authored May 30, 2023
2 parents 63822fe + 13c13d0 commit b654ef4
Show file tree
Hide file tree
Showing 19 changed files with 299 additions and 104 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
"src"
],
"dependencies": {
"@100mslive/hls-player": "0.1.3",
"@100mslive/hms-noise-suppression": "0.9.3",
"@100mslive/hms-virtual-background": "1.11.3",
"@100mslive/react-icons": "0.8.3",
"@100mslive/react-sdk": "0.8.3",
"@100mslive/react-ui": "0.8.3",
"@100mslive/hls-player": "0.1.5",
"@100mslive/hms-noise-suppression": "0.9.5",
"@100mslive/hms-virtual-background": "1.11.5",
"@100mslive/react-icons": "0.8.5",
"@100mslive/react-sdk": "0.8.5",
"@100mslive/react-ui": "0.8.5",
"@emoji-mart/data": "^1.0.6",
"@emoji-mart/react": "^1.0.1",
"@tldraw/tldraw": "^1.18.4",
Expand Down
97 changes: 97 additions & 0 deletions src/common/PeersSorter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { selectDominantSpeaker } from "@100mslive/hms-video-store";

class PeersSorter {
listeners = new Set();
storeUnsubscribe;

constructor(store) {
this.store = store;
this.peers = new Map();
this.lruPeers = new Set();
this.speaker = undefined;
}

setPeersAndTilesPerPage = ({ peers, tilesPerPage }) => {
this.tilesPerPage = tilesPerPage;
const peerIds = new Set(peers.map(peer => peer.id));
// remove existing peers which are no longer provided
this.peers.forEach((_, key) => {
if (!peerIds.has(key)) {
this.peers.delete(key);
}
});
this.lruPeers = new Set(
[...this.lruPeers].filter(peerId => peerIds.has(peerId))
);
peers.forEach(peer => {
this.peers.set(peer.id, peer);
if (this.lruPeers.size < tilesPerPage) {
this.lruPeers.add(peer.id);
}
});
if (!this.storeUnsubscribe) {
this.storeUnsubscribe = this.store.subscribe(
this.onDominantSpeakerChange,
selectDominantSpeaker
);
}
this.moveSpeakerToFront(this.speaker);
};

onUpdate = cb => {
this.listeners.add(cb);
};

stop = () => {
this.updateListeners();
this.listeners.clear();
this.storeUnsubscribe?.();
};

moveSpeakerToFront = speaker => {
if (!speaker) {
this.updateListeners();
return;
}
if (
this.lruPeers.has(speaker.id) &&
this.lruPeers.size <= this.tilesPerPage
) {
this.updateListeners();
return;
}
// delete to insert at beginning
this.lruPeers.delete(speaker.id);
let lruPeerArray = Array.from(this.lruPeers);
while (lruPeerArray.length >= this.tilesPerPage) {
lruPeerArray.pop();
}
this.lruPeers = new Set([speaker.id, ...lruPeerArray]);
this.updateListeners();
};

onDominantSpeakerChange = speaker => {
if (speaker && speaker.id !== this?.speaker?.id) {
this.speaker = speaker;
this.moveSpeakerToFront(speaker);
}
};

updateListeners = () => {
const orderedPeers = [];
this.lruPeers.forEach(key => {
const peer = this.peers.get(key);
if (peer) {
orderedPeers.push(peer);
}
});
this.peers.forEach(peer => {
if (!this.lruPeers.has(peer.id) && peer) {
orderedPeers.push(peer);
}
});
this.listeners.forEach(listener => listener?.(orderedPeers));
};
}

export default PeersSorter;
1 change: 1 addition & 0 deletions src/common/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ export const UI_SETTINGS = {
showStatsOnTiles: "showStatsOnTiles",
enableAmbientMusic: "enableAmbientMusic",
mirrorLocalVideo: "mirrorLocalVideo",
activeSpeakerSorting: "activeSpeakerSorting",
hideLocalVideo: "hideLocalVideo",
};

Expand Down
28 changes: 28 additions & 0 deletions src/common/useSortedPeers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useEffect, useRef, useState } from "react";
import { useHMSVanillaStore } from "@100mslive/react-sdk";
import PeersSorter from "./PeersSorter";
import { useActiveSpeakerSorting } from "../components/AppData/useUISettings";

function useSortedPeers({ peers, maxTileCount = 9 }) {
const [sortedPeers, setSortedPeers] = useState([]);
const store = useHMSVanillaStore();
const activeSpeakerSorting = useActiveSpeakerSorting();
const peerSortedRef = useRef(new PeersSorter(store));
peerSortedRef.current.onUpdate(setSortedPeers);

useEffect(() => {
const peersSorter = peerSortedRef.current;
if (peers?.length > 0 && maxTileCount && activeSpeakerSorting) {
peersSorter.setPeersAndTilesPerPage({
peers,
tilesPerPage: maxTileCount,
});
} else if (!activeSpeakerSorting) {
peersSorter.stop();
}
}, [maxTileCount, peers, activeSpeakerSorting]);

return activeSpeakerSorting ? sortedPeers : peers;
}

export default useSortedPeers;
1 change: 1 addition & 0 deletions src/components/AppData/AppData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const initialAppData = {
[UI_SETTINGS.enableAmbientMusic]: false,
[UI_SETTINGS.uiViewMode]: UI_MODE_GRID,
[UI_SETTINGS.mirrorLocalVideo]: true,
[UI_SETTINGS.activeSpeakerSorting]: process.env.REACT_APP_ENV === "qa",
[UI_SETTINGS.hideLocalVideo]: false,
},
[APP_DATA.subscribedNotifications]: {
Expand Down
5 changes: 5 additions & 0 deletions src/components/AppData/useUISettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ export const useIsHeadless = () => {
return isHeadless;
};

export const useActiveSpeakerSorting = () => {
const activeSpeakerSorting = useUISettings(UI_SETTINGS.activeSpeakerSorting);
return activeSpeakerSorting;
};

export const useHLSViewerRole = () => {
return useHMSStore(selectAppData(APP_DATA.hlsViewerRole));
};
Expand Down
3 changes: 3 additions & 0 deletions src/components/Chat/ChatBody.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ const ChatMessage = React.memo(
r: messageType ? "$1" : undefined,
px: messageType ? "$4" : "$2",
py: messageType ? "$4" : 0,
userSelect: "none",
}}
key={message.time}
data-testid="chat_msg"
Expand Down Expand Up @@ -274,7 +275,9 @@ const ChatMessage = React.memo(
mt: "$2",
wordBreak: "break-word",
whiteSpace: "pre-wrap",
userSelect: "all",
}}
onClick={e => e.stopPropagation()}
>
<AnnotisedMessage message={message.message} />
</Text>
Expand Down
50 changes: 44 additions & 6 deletions src/components/Notifications/ReconnectNotifications.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useEffect } from "react";
import { useEffect, useState } from "react";
import { logMessage } from "zipyai";
import {
HMSNotificationTypes,
useHMSNotifications,
} from "@100mslive/react-sdk";
import { Dialog, Flex, Loading, Text } from "@100mslive/react-ui";
import { ToastConfig } from "../Toast/ToastConfig";
import { ToastManager } from "../Toast/ToastManager";

Expand All @@ -12,22 +13,59 @@ const notificationTypes = [
HMSNotificationTypes.RECONNECTING,
];
let notificationId = null;

const isQA = process.env.REACT_APP_ENV === "qa";
export const ReconnectNotifications = () => {
const notification = useHMSNotifications(notificationTypes);
const [open, setOpen] = useState(false);
useEffect(() => {
if (notification?.type === HMSNotificationTypes.RECONNECTED) {
logMessage("Reconnected");
notificationId = ToastManager.replaceToast(
notificationId,
ToastConfig.RECONNECTED.single()
);
setOpen(false);
} else if (notification?.type === HMSNotificationTypes.RECONNECTING) {
logMessage("Reconnecting");
notificationId = ToastManager.replaceToast(
notificationId,
ToastConfig.RECONNECTING.single(notification.data.message)
);
if (isQA) {
ToastManager.removeToast(notificationId);
setOpen(true);
} else {
notificationId = ToastManager.replaceToast(
notificationId,
ToastConfig.RECONNECTING.single(notification.data.message)
);
}
}
}, [notification]);
return null;
if (!open || !isQA) return null;
return (
<Dialog.Root open={open} modal={true}>
<Dialog.Portal container={document.getElementById("conferencing")}>
<Dialog.Overlay />
<Dialog.Content
css={{
width: "fit-content",
maxWidth: "80%",
p: "$4 $8",
position: "relative",
top: "unset",
bottom: "$9",
transform: "translate(-50%, -100%)",
animation: "none !important",
}}
>
<Flex align="center">
<div style={{ display: "inline", margin: "0.25rem" }}>
<Loading size={16} />
</div>
<Text css={{ fontSize: "$space$8", color: "$textHighEmp" }}>
You lost your network connection. Trying to reconnect.
</Text>
</Flex>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
};
19 changes: 18 additions & 1 deletion src/components/Settings/LayoutSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,14 @@ export const LayoutSettings = () => {
const isLocalVideoEnabled = useHMSStore(selectIsLocalVideoEnabled);
const isLocalScreenShared = useHMSStore(selectIsLocalScreenShared);
const [
{ isAudioOnly, uiViewMode, maxTileCount, mirrorLocalVideo, hideLocalVideo },
{
isAudioOnly,
uiViewMode,
maxTileCount,
mirrorLocalVideo,
activeSpeakerSorting,
hideLocalVideo,
},
setUISettings,
] = useSetUiSettings();
const toggleIsAudioOnly = useCallback(
Expand Down Expand Up @@ -49,6 +56,16 @@ export const LayoutSettings = () => {
id="activeSpeakerMode"
label="Active Speaker Mode"
/>
<SwitchWithLabel
label="Active Speaker Sorting"
id="activeSpeakerSortingMode"
checked={activeSpeakerSorting}
onChange={value => {
setUISettings({
[UI_SETTINGS.activeSpeakerSorting]: value,
});
}}
/>
<SwitchWithLabel
label="Audio Only Mode"
id="audioOnlyMode"
Expand Down
2 changes: 1 addition & 1 deletion src/components/Streaming/RTMPStreaming.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ const StartRTMP = () => {
value => `${value.rtmpURL}/${value.streamKey}`
)
: [];
hmsActions.startRTMPOrRecording({
await hmsActions.startRTMPOrRecording({
rtmpURLs: urls,
meetingURL: recordingUrl,
resolution: getResolution(resolution),
Expand Down
44 changes: 18 additions & 26 deletions src/components/VideoList.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useEffect, useState } from "react";
import React, { Fragment, useEffect, useState } from "react";
import {
selectLocalPeerID,
useHMSStore,
useVideoList,
} from "@100mslive/react-sdk";
import { getLeft, StyledVideoList, useTheme } from "@100mslive/react-ui";
import { StyledVideoList, useTheme } from "@100mslive/react-ui";
import { Pagination } from "./Pagination";
import ScreenshareTile from "./ScreenshareTile";
import VideoTile from "./VideoTile";
import useSortedPeers from "../common/useSortedPeers";
import { useAppConfig } from "./AppData/useAppConfig";
import { useIsHeadless, useUISettings } from "./AppData/useUISettings";
import { UI_SETTINGS } from "../common/constants";
Expand All @@ -24,11 +25,12 @@ const List = ({
const isHeadless = useIsHeadless();
const hideLocalVideo = useUISettings(UI_SETTINGS.hideLocalVideo);
const localPeerId = useHMSStore(selectLocalPeerID);
if (hideLocalVideo && peers.length > 1) {
peers = filterPeerId(peers, localPeerId);
let sortedPeers = useSortedPeers({ peers, maxTileCount });
if (hideLocalVideo && sortedPeers.length > 1) {
sortedPeers = filterPeerId(sortedPeers, localPeerId);
}
const { ref, pagesWithTiles } = useVideoList({
peers,
peers: sortedPeers,
maxTileCount,
maxColCount,
maxRowCount,
Expand All @@ -45,40 +47,30 @@ const List = ({
}, [pagesWithTiles.length, page]);
return (
<StyledVideoList.Root ref={ref}>
<StyledVideoList.Container>
<StyledVideoList.Container
css={{ flexWrap: "wrap", placeContent: "center" }}
>
{pagesWithTiles && pagesWithTiles.length > 0
? pagesWithTiles.map((tiles, pageNo) => (
<StyledVideoList.View
key={pageNo}
css={{
left: getLeft(pageNo, page),
transition: "left 0.3s ease-in-out",
}}
>
{tiles.map(tile => {
if (tile.width === 0 || tile.height === 0) {
return null;
}
return tile.track?.source === "screen" ? (
? pagesWithTiles[page]?.map(tile => {
return (
<Fragment key={tile.track?.id || tile.peer.id}>
{tile.track?.source === "screen" ? (
<ScreenshareTile
key={tile.track.id}
width={tile.width}
height={tile.height}
peerId={tile.peer.id}
/>
) : (
<VideoTile
key={tile.track?.id || tile.peer.id}
width={tile.width}
height={tile.height}
peerId={tile.peer?.id}
trackId={tile.track?.id}
visible={pageNo === page}
/>
);
})}
</StyledVideoList.View>
))
)}
</Fragment>
);
})
: null}
</StyledVideoList.Container>
{!isHeadless && pagesWithTiles.length > 1 ? (
Expand Down
Loading

3 comments on commit b654ef4

@vercel
Copy link

@vercel vercel bot commented on b654ef4 May 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

100ms-web – ./

100ms-web-git-main-akash1234.vercel.app
100ms-web-akash1234.vercel.app
100ms-web-taupe.vercel.app

@vercel
Copy link

@vercel vercel bot commented on b654ef4 May 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on b654ef4 May 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.