diff --git a/dailyjs/basic-call/components/App/Modals.js b/dailyjs/basic-call/components/App/Modals.js index ced3862..264ddd7 100644 --- a/dailyjs/basic-call/components/App/Modals.js +++ b/dailyjs/basic-call/components/App/Modals.js @@ -1,10 +1,18 @@ import React from 'react'; import DeviceSelectModal from '@dailyjs/shared/components/DeviceSelectModal/DeviceSelectModal'; +import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; -export const Modals = () => ( - <> - - > -); +export const Modals = () => { + const { modals } = useUIState(); + + return ( + <> + + {modals.map((ModalComponent) => ( + + ))} + > + ); +}; export default Modals; diff --git a/dailyjs/basic-call/pages/_app.js b/dailyjs/basic-call/pages/_app.js index e1c376c..790ea7a 100644 --- a/dailyjs/basic-call/pages/_app.js +++ b/dailyjs/basic-call/pages/_app.js @@ -14,6 +14,7 @@ function App({ Component, pageProps }) { + @@ -120,6 +125,7 @@ Index.propTypes = { predefinedRoom: PropTypes.bool, domain: PropTypes.string, asides: PropTypes.arrayOf(PropTypes.func), + modals: PropTypes.arrayOf(PropTypes.func), customTrayComponent: PropTypes.node, customAppComponent: PropTypes.node, }; diff --git a/dailyjs/live-streaming/components/ChatAside/ChatAside.js b/dailyjs/live-streaming/components/ChatAside/ChatAside.js deleted file mode 100644 index 2c88d3e..0000000 --- a/dailyjs/live-streaming/components/ChatAside/ChatAside.js +++ /dev/null @@ -1,132 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import Aside from '@dailyjs/shared/components/Aside'; -import { Button } from '@dailyjs/shared/components/Button'; -import { TextInput } from '@dailyjs/shared/components/Input'; -import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; -import { useChat } from '../../contexts/ChatProvider'; -import { useMessageSound } from '../../hooks/useMessageSound'; - -export const CHAT_ASIDE = 'chat'; - -export const ChatAside = () => { - const { showAside, setShowAside } = useUIState(); - const { sendMessage, chatHistory, hasNewMessages, setHasNewMessages } = - useChat(); - const [newMessage, setNewMessage] = useState(''); - const playMessageSound = useMessageSound(); - - const chatWindowRef = useRef(); - - useEffect(() => { - // Clear out any new message notifications if we're showing the chat screen - if (showAside === CHAT_ASIDE) { - setHasNewMessages(false); - } - }, [showAside, chatHistory.length, setHasNewMessages]); - - useEffect(() => { - if (hasNewMessages && showAside !== CHAT_ASIDE) { - playMessageSound(); - } - }, [playMessageSound, showAside, hasNewMessages]); - - useEffect(() => { - if (chatWindowRef.current) { - chatWindowRef.current.scrollTop = chatWindowRef.current.scrollHeight; - } - }, [chatHistory?.length]); - - if (!showAside || showAside !== CHAT_ASIDE) { - return null; - } - - return ( - - ); -}; - -export default ChatAside; diff --git a/dailyjs/live-streaming/components/ChatAside/index.js b/dailyjs/live-streaming/components/ChatAside/index.js deleted file mode 100644 index 8e15e50..0000000 --- a/dailyjs/live-streaming/components/ChatAside/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ChatAside as default } from './ChatAside'; diff --git a/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js b/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js new file mode 100644 index 0000000..337e1ed --- /dev/null +++ b/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js @@ -0,0 +1,43 @@ +import React, { useState } from 'react'; +import { Button } from '@dailyjs/shared/components/Button'; +import Field from '@dailyjs/shared/components/Field'; +import { TextInput } from '@dailyjs/shared/components/Input'; +import Modal from '@dailyjs/shared/components/Modal'; +import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; +import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; + +export const LIVE_STREAMING_MODAL = 'live-streaming'; + +export const LiveStreamingModal = () => { + const { callObject } = useCallState(); + const { currentModals, closeModal } = useUIState(); + const [rtmpUrl, setRtmpUrl] = useState(''); + + return ( + closeModal(LIVE_STREAMING_MODAL)} + > + + setRtmpUrl(e.target.value)} + /> + + callObject.startLiveStreaming({ rtmpUrl })} + > + Start live streaming + + callObject.stopLiveStreaming()}> + Stop live streaming + + + ); +}; + +export default LiveStreamingModal; diff --git a/dailyjs/live-streaming/components/LiveStreamingModal/index.js b/dailyjs/live-streaming/components/LiveStreamingModal/index.js new file mode 100644 index 0000000..12ffdf0 --- /dev/null +++ b/dailyjs/live-streaming/components/LiveStreamingModal/index.js @@ -0,0 +1,3 @@ +export { LiveStreamingModal as default } from './LiveStreamingModal'; +export { LiveStreamingModal } from './LiveStreamingModal'; +export { LIVE_STREAMING_MODAL } from './LiveStreamingModal'; diff --git a/dailyjs/live-streaming/components/Tray/Tray.js b/dailyjs/live-streaming/components/Tray/Tray.js index 84290bf..d9ae3f5 100644 --- a/dailyjs/live-streaming/components/Tray/Tray.js +++ b/dailyjs/live-streaming/components/Tray/Tray.js @@ -2,24 +2,20 @@ import React from 'react'; import { TrayButton } from '@dailyjs/shared/components/Tray'; import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; -import { ReactComponent as IconChat } from '@dailyjs/shared/icons/chat-md.svg'; -import { useChat } from '../../contexts/ChatProvider'; -import { CHAT_ASIDE } from '../ChatAside/ChatAside'; +import { ReactComponent as IconStream } from '@dailyjs/shared/icons/streaming-md.svg'; + +import { LIVE_STREAMING_MODAL } from '../LiveStreamingModal'; export const Tray = () => { - const { toggleAside } = useUIState(); - const { hasNewMessages } = useChat(); + const { openModal } = useUIState(); return ( <> { - toggleAside(CHAT_ASIDE); - }} + label="Stream" + onClick={() => openModal(LIVE_STREAMING_MODAL)} > - + > ); diff --git a/dailyjs/live-streaming/pages/_app.js b/dailyjs/live-streaming/pages/_app.js index ad38f4e..1ff7cc2 100644 --- a/dailyjs/live-streaming/pages/_app.js +++ b/dailyjs/live-streaming/pages/_app.js @@ -2,10 +2,10 @@ import React from 'react'; import App from '@dailyjs/basic-call/pages/_app'; import AppWithChat from '../components/App'; -import ChatAside from '../components/ChatAside'; +import { LiveStreamingModal } from '../components/LiveStreamingModal'; import Tray from '../components/Tray'; -App.asides = [ChatAside]; +App.modals = [LiveStreamingModal]; App.customAppComponent = ; App.customTrayComponent = ; diff --git a/dailyjs/shared/components/DeviceSelectModal/DeviceSelectModal.js b/dailyjs/shared/components/DeviceSelectModal/DeviceSelectModal.js index 9f87bc4..926ba85 100644 --- a/dailyjs/shared/components/DeviceSelectModal/DeviceSelectModal.js +++ b/dailyjs/shared/components/DeviceSelectModal/DeviceSelectModal.js @@ -4,14 +4,16 @@ import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; import { Button } from '../Button'; import { DeviceSelect } from '../DeviceSelect'; +export const DEVICE_MODAL = 'device'; + export const DeviceSelectModal = () => { - const { showDeviceModal, setShowDeviceModal } = useUIState(); + const { currentModals, closeModal } = useUIState(); return ( setShowDeviceModal(false)} + isOpen={currentModals[DEVICE_MODAL]} + onClose={() => closeModal(DEVICE_MODAL)} actions={[ Cancel diff --git a/dailyjs/shared/components/DeviceSelectModal/index.js b/dailyjs/shared/components/DeviceSelectModal/index.js index 3ea8747..22feecd 100644 --- a/dailyjs/shared/components/DeviceSelectModal/index.js +++ b/dailyjs/shared/components/DeviceSelectModal/index.js @@ -1,2 +1,3 @@ export { DeviceSelectModal as default } from './DeviceSelectModal'; export { DeviceSelectModal } from './DeviceSelectModal'; +export { DEVICE_MODAL } from './DeviceSelectModal'; diff --git a/dailyjs/shared/components/HairCheck/HairCheck.js b/dailyjs/shared/components/HairCheck/HairCheck.js index 20909cd..a668045 100644 --- a/dailyjs/shared/components/HairCheck/HairCheck.js +++ b/dailyjs/shared/components/HairCheck/HairCheck.js @@ -1,5 +1,6 @@ import React, { useState, useEffect, useMemo } from 'react'; import Button from '@dailyjs/shared/components/Button'; +import { DEVICE_MODAL } from '@dailyjs/shared/components/DeviceSelectModal/DeviceSelectModal'; import { TextInput } from '@dailyjs/shared/components/Input'; import Loader from '@dailyjs/shared/components/Loader'; import { MuteButton } from '@dailyjs/shared/components/MuteButtons'; @@ -33,7 +34,7 @@ export const HairCheck = () => { const { localParticipant } = useParticipants(); const { deviceState, camError, micError, isCamMuted, isMicMuted } = useMediaDevices(); - const { showDeviceModal, setShowDeviceModal } = useUIState(); + const { openModal } = useUIState(); const [waiting, setWaiting] = useState(false); const [joining, setJoining] = useState(false); const [denied, setDenied] = useState(); @@ -143,7 +144,7 @@ export const HairCheck = () => { className="device-button" size="medium-square" variant="blur" - onClick={() => setShowDeviceModal(!showDeviceModal)} + onClick={() => openModal(DEVICE_MODAL)} > diff --git a/dailyjs/shared/components/Tray/BasicTray.js b/dailyjs/shared/components/Tray/BasicTray.js index 45326ae..b7f5aaf 100644 --- a/dailyjs/shared/components/Tray/BasicTray.js +++ b/dailyjs/shared/components/Tray/BasicTray.js @@ -1,5 +1,6 @@ import React from 'react'; import { PEOPLE_ASIDE } from '@dailyjs/shared/components/Aside/PeopleAside'; +import { DEVICE_MODAL } from '@dailyjs/shared/components/DeviceSelectModal'; import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; import { useMediaDevices } from '@dailyjs/shared/contexts/MediaDeviceProvider'; import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; @@ -14,7 +15,7 @@ import { Tray, TrayButton } from './Tray'; export const BasicTray = () => { const { callObject, leave } = useCallState(); - const { customTrayComponent, setShowDeviceModal, toggleAside } = useUIState(); + const { customTrayComponent, openModal, toggleAside } = useUIState(); const { isCamMuted, isMicMuted } = useMediaDevices(); const toggleCamera = (newState) => { @@ -43,7 +44,7 @@ export const BasicTray = () => { > {isMicMuted ? : } - setShowDeviceModal(true)}> + openModal(DEVICE_MODAL)}> diff --git a/dailyjs/shared/contexts/UIStateProvider.js b/dailyjs/shared/contexts/UIStateProvider.js index a9458cb..ab6c483 100644 --- a/dailyjs/shared/contexts/UIStateProvider.js +++ b/dailyjs/shared/contexts/UIStateProvider.js @@ -1,11 +1,33 @@ import React, { useCallback, createContext, useContext, useState } from 'react'; import PropTypes from 'prop-types'; +import { useDeepCompareMemo } from 'use-deep-compare'; export const UIStateContext = createContext(); -export const UIStateProvider = ({ asides, customTrayComponent, children }) => { - const [showDeviceModal, setShowDeviceModal] = useState(false); +export const UIStateProvider = ({ + asides, + modals, + customTrayComponent, + children, +}) => { const [showAside, setShowAside] = useState(); + const [activeModals, setActiveModals] = useState({}); + + const openModal = useCallback((modalName) => { + setActiveModals((prevState) => ({ + ...prevState, + [modalName]: true, + })); + }, []); + + const closeModal = useCallback((modalName) => { + setActiveModals((prevState) => ({ + ...prevState, + [modalName]: false, + })); + }, []); + + const currentModals = useDeepCompareMemo(() => activeModals, [activeModals]); const toggleAside = useCallback((newAside) => { setShowAside((p) => (p === newAside ? null : newAside)); @@ -15,9 +37,11 @@ export const UIStateProvider = ({ asides, customTrayComponent, children }) => { { UIStateProvider.propTypes = { children: PropTypes.node, asides: PropTypes.arrayOf(PropTypes.func), + modals: PropTypes.arrayOf(PropTypes.func), customTrayComponent: PropTypes.node, }; diff --git a/dailyjs/shared/icons/streamin-md.svg b/dailyjs/shared/icons/streaming-md.svg similarity index 100% rename from dailyjs/shared/icons/streamin-md.svg rename to dailyjs/shared/icons/streaming-md.svg