diff --git a/dailyjs/README.md b/dailyjs/README.md
index 885bbc7..7d20ecb 100644
--- a/dailyjs/README.md
+++ b/dailyjs/README.md
@@ -8,6 +8,10 @@ The basic call demo (derived from our prebuilt UI codebase) demonstrates how to
Send messages to other participants using sendAppMessage
+### [📺 Live streaming](./live-streaming)
+
+Broadcast call to a custom RTMP endpoint using a variety of difference layout modes
+
---
## Getting started
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/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/package.json b/dailyjs/basic-call/package.json
index 0bd5ae6..34844a0 100644
--- a/dailyjs/basic-call/package.json
+++ b/dailyjs/basic-call/package.json
@@ -6,7 +6,8 @@
"scripts": {
"dev": "next dev",
"build": "next build",
- "start": "next start"
+ "start": "next start",
+ "lint": "next lint"
},
"dependencies": {
"@dailyjs/shared": "*",
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 }) {
) : (
+
@@ -122,8 +131,11 @@ Index.propTypes = {
predefinedRoom: PropTypes.string,
domain: PropTypes.string,
asides: PropTypes.arrayOf(PropTypes.func),
+ modals: PropTypes.arrayOf(PropTypes.func),
customTrayComponent: PropTypes.node,
customAppComponent: PropTypes.node,
+ forceFetchToken: PropTypes.bool,
+ forceOwner: PropTypes.bool,
};
export async function getStaticProps() {
diff --git a/dailyjs/live-streaming/.babelrc b/dailyjs/live-streaming/.babelrc
new file mode 100644
index 0000000..a6f4434
--- /dev/null
+++ b/dailyjs/live-streaming/.babelrc
@@ -0,0 +1,4 @@
+{
+ "presets": ["next/babel"],
+ "plugins": ["inline-react-svg"]
+}
diff --git a/dailyjs/live-streaming/README.md b/dailyjs/live-streaming/README.md
new file mode 100644
index 0000000..c5e7974
--- /dev/null
+++ b/dailyjs/live-streaming/README.md
@@ -0,0 +1,41 @@
+# Live Streaming
+
+
+
+### Live example
+
+**[See it in action here ➡️](https://dailyjs-live-streaming.vercel.app)**
+
+---
+
+## What does this demo do?
+
+- Use [startLiveStreaming](https://docs.daily.co/reference#%EF%B8%8F-startlivestreaming) to send video and audio to specified RTMP endpoint
+- Listen for stream started / stopped / error events
+- Allows call owner to specify stream layout (grid, single participant or active speaker) and maximum cams
+- Extends the basic call demo with a live streaming provider, tray button and modal
+- Show a notification bubble at the top of the screen when live streaming is in progress
+
+Please note: this demo is not currently mobile optimised
+
+### Getting started
+
+```
+# set both DAILY_API_KEY and DAILY_DOMAIN
+mv env.example .env.local
+
+yarn
+yarn workspace @dailyjs/live-streaming dev
+```
+
+## How does this example work?
+
+In this example we extend the [basic call demo](../basic-call) with live streaming functionality.
+
+We pass a custom tray object, a custom app object (wrapping the original in a new `LiveStreamingProvider`) and a custom modal. We also symlink both the `public` and `pages/api` folders from the basic call.
+
+Single live streaming is only available to call owners, you must create a token when joining the call (for simplicity, we have disabled the abiltiy to join the call as a guest.)
+
+## Deploy your own on Vercel
+
+[](https://vercel.com/new/daily-co/clone-flow?repository-url=https%3A%2F%2Fgithub.com%2Fdaily-demos%2Fexamples.git&env=DAILY_DOMAIN%2CDAILY_API_KEY&envDescription=Your%20Daily%20domain%20and%20API%20key%20can%20be%20found%20on%20your%20account%20dashboard&envLink=https%3A%2F%2Fdashboard.daily.co&project-name=daily-examples&repo-name=daily-examples)
diff --git a/dailyjs/live-streaming/components/App/App.js b/dailyjs/live-streaming/components/App/App.js
new file mode 100644
index 0000000..4749551
--- /dev/null
+++ b/dailyjs/live-streaming/components/App/App.js
@@ -0,0 +1,13 @@
+import React from 'react';
+
+import App from '@dailyjs/basic-call/components/App';
+import { LiveStreamingProvider } from '../../contexts/LiveStreamingProvider';
+
+// Extend our basic call app component with the live streaming context
+export const AppWithLiveStreaming = () => (
+
+
+
+);
+
+export default AppWithLiveStreaming;
diff --git a/dailyjs/live-streaming/components/App/index.js b/dailyjs/live-streaming/components/App/index.js
new file mode 100644
index 0000000..c46acf2
--- /dev/null
+++ b/dailyjs/live-streaming/components/App/index.js
@@ -0,0 +1 @@
+export { AppWithLiveStreaming as default } from './App';
diff --git a/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js b/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js
new file mode 100644
index 0000000..2cee300
--- /dev/null
+++ b/dailyjs/live-streaming/components/LiveStreamingModal/LiveStreamingModal.js
@@ -0,0 +1,148 @@
+import React, { useEffect, useState } from 'react';
+import { Button } from '@dailyjs/shared/components/Button';
+import { CardBody } from '@dailyjs/shared/components/Card';
+import Field from '@dailyjs/shared/components/Field';
+import { TextInput, SelectInput } 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 { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
+import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider';
+import { useLiveStreaming } from '../../contexts/LiveStreamingProvider';
+
+export const LIVE_STREAMING_MODAL = 'live-streaming';
+
+const LAYOUTS = [
+ { label: 'Grid (default)', value: 'default' },
+ { label: 'Single participant', value: 'single-participant' },
+ { label: 'Active participant', value: 'active-participant' },
+];
+
+export const LiveStreamingModal = () => {
+ const { callObject } = useCallState();
+ const { allParticipants } = useParticipants();
+ const { currentModals, closeModal } = useUIState();
+ const { isStreaming, streamError } = useLiveStreaming();
+ const [pending, setPending] = useState(false);
+ const [rtmpUrl, setRtmpUrl] = useState('');
+ const [layout, setLayout] = useState(0);
+ const [maxCams, setMaxCams] = useState(9);
+ const [participant, setParticipant] = useState(0);
+
+ useEffect(() => {
+ // Reset pending state whenever stream state changes
+ setPending(false);
+ }, [isStreaming]);
+
+ function startLiveStream() {
+ setPending(true);
+
+ const opts =
+ layout === 'single-participant'
+ ? { session_id: participant.id }
+ : { max_cam_streams: maxCams };
+ callObject.startLiveStreaming({ rtmpUrl, preset: layout, ...opts });
+ }
+
+ function stopLiveStreaming() {
+ setPending(true);
+ callObject.stopLiveStreaming();
+ }
+
+ return (
+ closeModal(LIVE_STREAMING_MODAL)}
+ actions={[
+ ,
+ !isStreaming ? (
+
+ ) : (
+
+ ),
+ ]}
+ >
+ {streamError && (
+
+ Unable to start stream. Error message: {streamError}
+
+ )}
+
+
+ setLayout(Number(e.target.value))}
+ value={layout}
+ >
+ {LAYOUTS.map((l, i) => (
+
+ ))}
+
+
+
+ {layout !==
+ LAYOUTS.findIndex((l) => l.value === 'single-participant') && (
+
+ setMaxCams(Number(e.target.value))}
+ value={maxCams}
+ >
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {layout ===
+ LAYOUTS.findIndex((l) => l.value === 'single-participant') && (
+
+ setParticipant(e.target.value)}
+ value={participant}
+ >
+ {allParticipants.map((p) => (
+
+ ))}
+
+
+ )}
+
+
+ setRtmpUrl(e.target.value)}
+ />
+
+
+
+ );
+};
+
+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
new file mode 100644
index 0000000..b185aa3
--- /dev/null
+++ b/dailyjs/live-streaming/components/Tray/Tray.js
@@ -0,0 +1,27 @@
+import React from 'react';
+
+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)}
+ >
+
+
+ >
+ );
+};
+
+export default Tray;
diff --git a/dailyjs/live-streaming/components/Tray/index.js b/dailyjs/live-streaming/components/Tray/index.js
new file mode 100644
index 0000000..100bcc8
--- /dev/null
+++ b/dailyjs/live-streaming/components/Tray/index.js
@@ -0,0 +1 @@
+export { Tray as default } from './Tray';
diff --git a/dailyjs/live-streaming/contexts/LiveStreamingProvider.js b/dailyjs/live-streaming/contexts/LiveStreamingProvider.js
new file mode 100644
index 0000000..f0c4a51
--- /dev/null
+++ b/dailyjs/live-streaming/contexts/LiveStreamingProvider.js
@@ -0,0 +1,71 @@
+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);
+ setStreamError(null);
+ 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/image.png b/dailyjs/live-streaming/image.png
new file mode 100644
index 0000000..9781261
Binary files /dev/null and b/dailyjs/live-streaming/image.png differ
diff --git a/dailyjs/live-streaming/next.config.js b/dailyjs/live-streaming/next.config.js
new file mode 100644
index 0000000..9a0a6ee
--- /dev/null
+++ b/dailyjs/live-streaming/next.config.js
@@ -0,0 +1,13 @@
+const withPlugins = require('next-compose-plugins');
+const withTM = require('next-transpile-modules')([
+ '@dailyjs/shared',
+ '@dailyjs/basic-call',
+]);
+
+const packageJson = require('./package.json');
+
+module.exports = withPlugins([withTM], {
+ env: {
+ PROJECT_TITLE: packageJson.description,
+ },
+});
diff --git a/dailyjs/live-streaming/package.json b/dailyjs/live-streaming/package.json
new file mode 100644
index 0000000..a255016
--- /dev/null
+++ b/dailyjs/live-streaming/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@dailyjs/live-streaming",
+ "description": "Basic Call + Live Streaming",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@dailyjs/shared": "*",
+ "@dailyjs/basic-call": "*",
+ "next": "^11.0.0",
+ "pluralize": "^8.0.0",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2"
+ },
+ "devDependencies": {
+ "babel-plugin-module-resolver": "^4.1.0",
+ "next-compose-plugins": "^2.2.1",
+ "next-transpile-modules": "^8.0.0"
+ }
+}
diff --git a/dailyjs/live-streaming/pages/_app.js b/dailyjs/live-streaming/pages/_app.js
new file mode 100644
index 0000000..7a097c4
--- /dev/null
+++ b/dailyjs/live-streaming/pages/_app.js
@@ -0,0 +1,12 @@
+import React from 'react';
+import App from '@dailyjs/basic-call/pages/_app';
+import AppWithLiveStreaming from '../components/App';
+
+import { LiveStreamingModal } from '../components/LiveStreamingModal';
+import Tray from '../components/Tray';
+
+App.modals = [LiveStreamingModal];
+App.customAppComponent = ;
+App.customTrayComponent = ;
+
+export default App;
diff --git a/dailyjs/live-streaming/pages/api b/dailyjs/live-streaming/pages/api
new file mode 120000
index 0000000..999f604
--- /dev/null
+++ b/dailyjs/live-streaming/pages/api
@@ -0,0 +1 @@
+../../basic-call/pages/api
\ No newline at end of file
diff --git a/dailyjs/live-streaming/pages/index.js b/dailyjs/live-streaming/pages/index.js
new file mode 100644
index 0000000..2668138
--- /dev/null
+++ b/dailyjs/live-streaming/pages/index.js
@@ -0,0 +1,16 @@
+import Index from '@dailyjs/basic-call/pages';
+import getDemoProps from '@dailyjs/shared/lib/demoProps';
+
+export async function getStaticProps() {
+ const defaultProps = getDemoProps();
+
+ return {
+ props: {
+ ...defaultProps,
+ forceFetchToken: true,
+ forceOwner: true,
+ },
+ };
+}
+
+export default Index;
diff --git a/dailyjs/live-streaming/public b/dailyjs/live-streaming/public
new file mode 120000
index 0000000..33a6e67
--- /dev/null
+++ b/dailyjs/live-streaming/public
@@ -0,0 +1 @@
+../basic-call/public
\ No newline at end of file
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={[
diff --git a/dailyjs/shared/components/Input/Input.js b/dailyjs/shared/components/Input/Input.js
index 06a8150..5ca32be 100644
--- a/dailyjs/shared/components/Input/Input.js
+++ b/dailyjs/shared/components/Input/Input.js
@@ -269,7 +269,7 @@ export const SelectInput = ({
SelectInput.propTypes = {
onChange: PropTypes.func,
children: PropTypes.node,
- value: PropTypes.number,
+ value: PropTypes.any,
variant: PropTypes.string,
label: PropTypes.string,
};
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..2aab79b 100644
--- a/dailyjs/shared/contexts/UIStateProvider.js
+++ b/dailyjs/shared/contexts/UIStateProvider.js
@@ -1,11 +1,34 @@
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 [customCapsule, setCustomCapsule] = 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,12 +38,16 @@ export const UIStateProvider = ({ asides, customTrayComponent, children }) => {
{children}
@@ -31,6 +58,7 @@ 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
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: {
diff --git a/dailyjs/text-chat/package.json b/dailyjs/text-chat/package.json
index 23b7363..cd719e9 100644
--- a/dailyjs/text-chat/package.json
+++ b/dailyjs/text-chat/package.json
@@ -6,7 +6,8 @@
"scripts": {
"dev": "next dev",
"build": "next build",
- "start": "next start"
+ "start": "next start",
+ "lint": "next lint"
},
"dependencies": {
"@dailyjs/shared": "*",