From 2785991fe2aab450570f82ef17d7c65cf36cb1ac Mon Sep 17 00:00:00 2001 From: J Taylor Date: Thu, 24 Jun 2021 17:50:44 +0100 Subject: [PATCH] functionally complete --- dailyjs/basic-call/components/Intro/Intro.js | 18 ++-- dailyjs/basic-call/components/Room/Header.js | 38 +++++++- dailyjs/basic-call/pages/index.js | 6 ++ dailyjs/live-streaming/components/App/App.js | 12 +-- .../live-streaming/components/App/index.js | 2 +- .../LiveStreamingModal/LiveStreamingModal.js | 49 +++++++--- .../live-streaming/components/Tray/Tray.js | 5 +- .../live-streaming/contexts/ChatProvider.js | 90 ------------------- .../contexts/LiveStreamingProvider.js | 70 +++++++++++++++ .../live-streaming/hooks/useMessageSound.js | 19 ---- dailyjs/live-streaming/pages/_app.js | 4 +- dailyjs/live-streaming/pages/index.js | 2 + .../components/GlobalStyle/GlobalStyle.js | 1 + dailyjs/shared/contexts/UIStateProvider.js | 3 + dailyjs/shared/styles/defaultTheme.js | 1 + 15 files changed, 183 insertions(+), 137 deletions(-) delete mode 100644 dailyjs/live-streaming/contexts/ChatProvider.js create mode 100644 dailyjs/live-streaming/contexts/LiveStreamingProvider.js delete mode 100644 dailyjs/live-streaming/hooks/useMessageSound.js diff --git a/dailyjs/basic-call/components/Intro/Intro.js b/dailyjs/basic-call/components/Intro/Intro.js index 301509e..2fe43bb 100644 --- a/dailyjs/basic-call/components/Intro/Intro.js +++ b/dailyjs/basic-call/components/Intro/Intro.js @@ -23,10 +23,12 @@ export const Intro = ({ onJoin, title, fetching = false, + forceFetchToken = false, + forceOwner = false, }) => { const [roomName, setRoomName] = useState(); - const [owner, setOwner] = useState(false); - const [fetchToken, setFetchToken] = useState(false); + const [fetchToken, setFetchToken] = useState(forceFetchToken); + const [owner, setOwner] = useState(forceOwner); useEffect(() => { setRoomName(room); @@ -51,10 +53,12 @@ export const Intro = ({ required /> - - setFetchToken(e.target.checked)} /> - - {fetchToken && ( + {!forceFetchToken && ( + + setFetchToken(e.target.checked)} /> + + )} + {fetchToken && !forceOwner && ( setOwner(e.target.checked)} /> @@ -79,6 +83,8 @@ Intro.propTypes = { domain: PropTypes.string.isRequired, onJoin: PropTypes.func.isRequired, fetching: PropTypes.bool, + forceFetchToken: PropTypes.bool, + forceOwner: PropTypes.bool, }; export default Intro; diff --git a/dailyjs/basic-call/components/Room/Header.js b/dailyjs/basic-call/components/Room/Header.js index 401d272..a18e5d5 100644 --- a/dailyjs/basic-call/components/Room/Header.js +++ b/dailyjs/basic-call/components/Room/Header.js @@ -1,8 +1,10 @@ import React, { useMemo } from 'react'; import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider'; +import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; export const Header = () => { const { participantCount } = useParticipants(); + const { customCapsule } = useUIState(); return useMemo( () => ( @@ -14,6 +16,12 @@ export const Header = () => { participantCount > 1 ? 'participants' : 'participant' }`} + {customCapsule && ( +
+ {customCapsule.variant === 'recording' && } + {customCapsule.label} +
+ )} ), - [participantCount] + [participantCount, customCapsule] ); }; diff --git a/dailyjs/basic-call/pages/index.js b/dailyjs/basic-call/pages/index.js index d950ebe..67cdc83 100644 --- a/dailyjs/basic-call/pages/index.js +++ b/dailyjs/basic-call/pages/index.js @@ -21,6 +21,8 @@ export default function Index({ domain, isConfigured = false, predefinedRoom = false, + forceFetchToken = false, + forceOwner = false, asides, modals, customTrayComponent, @@ -74,6 +76,8 @@ export default function Index({ ) : ( ( - +// Extend our basic call app component with the live streaming context +export const AppWithLiveStreaming = () => ( + - + ); -export default AppWithChat; +export default AppWithLiveStreaming; diff --git a/dailyjs/live-streaming/components/App/index.js b/dailyjs/live-streaming/components/App/index.js index 770f031..c46acf2 100644 --- a/dailyjs/live-streaming/components/App/index.js +++ b/dailyjs/live-streaming/components/App/index.js @@ -1 +1 @@ -export { AppWithChat as default } from './App'; +export { AppWithLiveStreaming as default } from './App'; diff --git a/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js b/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js index 337e1ed..6280635 100644 --- a/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js +++ b/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js @@ -1,25 +1,49 @@ -import React, { useState } from 'react'; +import React, { useEffect, 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 { Well } from '@dailyjs/shared/components/Well'; import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; +import { useLiveStreaming } from '../../contexts/LiveStreamingProvider'; export const LIVE_STREAMING_MODAL = 'live-streaming'; export const LiveStreamingModal = () => { const { callObject } = useCallState(); const { currentModals, closeModal } = useUIState(); + const { isStreaming, streamError } = useLiveStreaming(); + const [pending, setPending] = useState(false); const [rtmpUrl, setRtmpUrl] = useState(''); + useEffect(() => { + // Reset pending state whenever stream state changes + setPending(false); + }, [isStreaming]); + + function startLiveStream() { + setPending(true); + callObject.startLiveStreaming({ rtmpUrl }); + } + + function stopLiveStreaming() { + setPending(true); + callObject.stopLiveStreaming(); + } + return ( closeModal(LIVE_STREAMING_MODAL)} > - + {streamError && ( + + Unable to start stream. Error message: {streamError} + + )} + { onChange={(e) => setRtmpUrl(e.target.value)} /> - - + {!isStreaming ? ( + + ) : ( + + )} ); }; diff --git a/dailyjs/live-streaming/components/Tray/Tray.js b/dailyjs/live-streaming/components/Tray/Tray.js index d9ae3f5..b185aa3 100644 --- a/dailyjs/live-streaming/components/Tray/Tray.js +++ b/dailyjs/live-streaming/components/Tray/Tray.js @@ -4,15 +4,18 @@ import { TrayButton } from '@dailyjs/shared/components/Tray'; import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; import { ReactComponent as IconStream } from '@dailyjs/shared/icons/streaming-md.svg'; +import { useLiveStreaming } from '../../contexts/LiveStreamingProvider'; import { LIVE_STREAMING_MODAL } from '../LiveStreamingModal'; export const Tray = () => { const { openModal } = useUIState(); + const { isStreaming } = useLiveStreaming(); return ( <> openModal(LIVE_STREAMING_MODAL)} > diff --git a/dailyjs/live-streaming/contexts/ChatProvider.js b/dailyjs/live-streaming/contexts/ChatProvider.js deleted file mode 100644 index 6ad611a..0000000 --- a/dailyjs/live-streaming/contexts/ChatProvider.js +++ /dev/null @@ -1,90 +0,0 @@ -import React, { - createContext, - useCallback, - useContext, - useEffect, - useState, -} from 'react'; -import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; -import { nanoid } from 'nanoid'; -import PropTypes from 'prop-types'; - -export const ChatContext = createContext(); - -export const ChatProvider = ({ children }) => { - const { callObject } = useCallState(); - const [chatHistory, setChatHistory] = useState([]); - const [hasNewMessages, setHasNewMessages] = useState(false); - - const handleNewMessage = useCallback( - (e) => { - const participants = callObject.participants(); - const sender = participants[e.fromId].user_name - ? participants[e.fromId].user_name - : 'Guest'; - - setChatHistory((oldState) => [ - ...oldState, - { sender, message: e.data.message, id: nanoid() }, - ]); - - setHasNewMessages(true); - }, - [callObject] - ); - - const sendMessage = useCallback( - (message) => { - if (!callObject) { - return false; - } - - console.log('💬 Sending app message'); - - callObject.sendAppMessage({ message }, '*'); - - // Get the sender (local participant) name - const sender = callObject.participants().local.user_name - ? callObject.participants().local.user_name - : 'Guest'; - - // Update local chat history - return setChatHistory((oldState) => [ - ...oldState, - { sender, message, id: nanoid(), isLocal: true }, - ]); - }, - [callObject] - ); - - useEffect(() => { - if (!callObject) { - return false; - } - - console.log(`💬 Chat provider listening for app messages`); - - callObject.on('app-message', handleNewMessage); - - return () => callObject.off('app-message', handleNewMessage); - }, [callObject, handleNewMessage]); - - return ( - - {children} - - ); -}; - -ChatProvider.propTypes = { - children: PropTypes.node, -}; - -export const useChat = () => useContext(ChatContext); diff --git a/dailyjs/live-streaming/contexts/LiveStreamingProvider.js b/dailyjs/live-streaming/contexts/LiveStreamingProvider.js new file mode 100644 index 0000000..16e1e33 --- /dev/null +++ b/dailyjs/live-streaming/contexts/LiveStreamingProvider.js @@ -0,0 +1,70 @@ +import React, { + useState, + createContext, + useContext, + useEffect, + useCallback, +} from 'react'; +import { useCallState } from '@dailyjs/shared/contexts/CallProvider'; +import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider'; +import PropTypes from 'prop-types'; + +export const LiveStreamingContext = createContext(); + +export const LiveStreamingProvider = ({ children }) => { + const [isStreaming, setIsStreaming] = useState(false); + const [streamError, setStreamError] = useState(); + const { setCustomCapsule } = useUIState(); + const { callObject } = useCallState(); + + const handleStreamStarted = useCallback(() => { + console.log('📺 Live stream started'); + setIsStreaming(true); + setCustomCapsule({ variant: 'recording', label: 'Live streaming' }); + }, [setCustomCapsule]); + + const handleStreamStopped = useCallback(() => { + console.log('📺 Live stream stopped'); + setIsStreaming(false); + setCustomCapsule(null); + }, [setCustomCapsule]); + + const handleStreamError = useCallback( + (e) => { + setIsStreaming(false); + setCustomCapsule(null); + setStreamError(e.errorMsg); + }, + [setCustomCapsule] + ); + + useEffect(() => { + if (!callObject) { + return false; + } + + console.log('📺 Live streaming provider listening for stream events'); + + callObject.on('live-streaming-started', handleStreamStarted); + callObject.on('live-streaming-stopped', handleStreamStopped); + callObject.on('live-streaming-error', handleStreamError); + + return () => { + callObject.off('live-streaming-started', handleStreamStarted); + callObject.off('live-streaming-stopped', handleStreamStopped); + callObject.on('live-streaming-error', handleStreamError); + }; + }, [callObject, handleStreamStarted, handleStreamStopped, handleStreamError]); + + return ( + + {children} + + ); +}; + +LiveStreamingProvider.propTypes = { + children: PropTypes.node, +}; + +export const useLiveStreaming = () => useContext(LiveStreamingContext); diff --git a/dailyjs/live-streaming/hooks/useMessageSound.js b/dailyjs/live-streaming/hooks/useMessageSound.js deleted file mode 100644 index e894a1c..0000000 --- a/dailyjs/live-streaming/hooks/useMessageSound.js +++ /dev/null @@ -1,19 +0,0 @@ -import { useEffect, useMemo } from 'react'; - -import { useSound } from '@dailyjs/shared/hooks/useSound'; -import { debounce } from 'debounce'; - -/** - * Convenience hook to play `join.mp3` when participants join the call - */ -export const useMessageSound = () => { - const { load, play } = useSound('message.mp3'); - - useEffect(() => { - load(); - }, [load]); - - return useMemo(() => debounce(() => play(), 5000, true), [play]); -}; - -export default useMessageSound; diff --git a/dailyjs/live-streaming/pages/_app.js b/dailyjs/live-streaming/pages/_app.js index 1ff7cc2..7a097c4 100644 --- a/dailyjs/live-streaming/pages/_app.js +++ b/dailyjs/live-streaming/pages/_app.js @@ -1,12 +1,12 @@ import React from 'react'; import App from '@dailyjs/basic-call/pages/_app'; -import AppWithChat from '../components/App'; +import AppWithLiveStreaming from '../components/App'; import { LiveStreamingModal } from '../components/LiveStreamingModal'; import Tray from '../components/Tray'; App.modals = [LiveStreamingModal]; -App.customAppComponent = ; +App.customAppComponent = ; App.customTrayComponent = ; export default App; diff --git a/dailyjs/live-streaming/pages/index.js b/dailyjs/live-streaming/pages/index.js index 5f31f95..b82698b 100644 --- a/dailyjs/live-streaming/pages/index.js +++ b/dailyjs/live-streaming/pages/index.js @@ -10,6 +10,8 @@ export async function getStaticProps() { props: { domain: process.env.DAILY_DOMAIN || null, isConfigured, + forceFetchToken: true, + forceOwner: true, }, }; } diff --git a/dailyjs/shared/components/GlobalStyle/GlobalStyle.js b/dailyjs/shared/components/GlobalStyle/GlobalStyle.js index 26be0c5..c54fe71 100644 --- a/dailyjs/shared/components/GlobalStyle/GlobalStyle.js +++ b/dailyjs/shared/components/GlobalStyle/GlobalStyle.js @@ -12,6 +12,7 @@ export const GlobalStyle = () => ( --primary-dark: ${theme.primary.dark}; --secondary-default: ${theme.secondary.default}; --secondary-dark: ${theme.secondary.dark}; + --secondary-light: ${theme.secondary.light}; --blue-light: ${theme.blue.light}; --blue-default: ${theme.blue.default}; --blue-dark: ${theme.blue.dark}; diff --git a/dailyjs/shared/contexts/UIStateProvider.js b/dailyjs/shared/contexts/UIStateProvider.js index ab6c483..2aab79b 100644 --- a/dailyjs/shared/contexts/UIStateProvider.js +++ b/dailyjs/shared/contexts/UIStateProvider.js @@ -12,6 +12,7 @@ export const UIStateProvider = ({ }) => { const [showAside, setShowAside] = useState(); const [activeModals, setActiveModals] = useState({}); + const [customCapsule, setCustomCapsule] = useState(); const openModal = useCallback((modalName) => { setActiveModals((prevState) => ({ @@ -45,6 +46,8 @@ export const UIStateProvider = ({ toggleAside, showAside, setShowAside, + customCapsule, + setCustomCapsule, }} > {children} diff --git a/dailyjs/shared/styles/defaultTheme.js b/dailyjs/shared/styles/defaultTheme.js index bce6547..ba37156 100644 --- a/dailyjs/shared/styles/defaultTheme.js +++ b/dailyjs/shared/styles/defaultTheme.js @@ -10,6 +10,7 @@ export const defaultTheme = { secondary: { default: '#FF9254', dark: '#FB651E', + light: '#FF9254', }, blue: {