Merge branch 'live-transcription-postrefactor' into live-transcription

This commit is contained in:
Ashley Blewer 2021-10-06 12:49:26 -04:00
commit b51a0786c4
337 changed files with 5347 additions and 657 deletions

View File

@ -1,18 +1,13 @@
{
"extends": ["airbnb", "airbnb/hooks", "prettier"],
"parser": "babel-eslint",
"extends": ["next/core-web-vitals", "prettier"],
"env": {
"browser": true,
"node": true,
"es6": true
},
"rules": {
"no-console": 0,
"react/jsx-props-no-spreading": 0,
"react/forbid-prop-types": 0,
"react/require-default-props": 0,
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"import/no-extraneous-dependencies": 0,
"@next/next/no-img-element": 0,
"import/order": [
"error",
{

View File

@ -14,7 +14,7 @@ Thank you for looking into contributing to`daily-demos`! We want these projects
## Before contributing
### Run prebuilt-ui locally
### Run /prebuilt locally
Please follow the instructions in `README.md`.
@ -35,7 +35,7 @@ _Bug reports_
Before creating a new bug report, please do two things:
1. If you want to report a bug you experienced while on a Daily call, try out these [troubleshooting tips](https://help.daily.co/en/articles/2303117-top-troubleshooting-tips) to see if that takes care of the bug.
2. If you're still seeing the error, check to see if somebody else has [already filed the issue](https://github.com/daily-co/daily-demos/issues) before creating a new one.
2. If you're still seeing the error, check to see if somebody else has [already filed the issue](https://github.com/daily-demos/examples/issues) before creating a new one.
If you've done those two things and need to create an issue, we'll ask you to tell us:

View File

@ -10,7 +10,7 @@ Setup dependencies via `yarn install`.
Add the required environment variables (e.g. your Daily API key) for the demo being used. Each demo's README will list the required environment variables to run it locally.
Run an example via `yarn workspace @dailyjs/basic-call dev` (replacing `basic-call` with the name of the demo).
Run an example via `yarn workspace @custom/basic-call dev` (replacing `basic-call` with the name of the demo).
Please note: these demos are intended as educational resources for using the Daily platform as well as showcasing common usage patterns and best practices. That said, they are not intended to be used as production ready applications.
@ -18,11 +18,11 @@ Please note: these demos are intended as educational resources for using the Dai
## Contents
## [Daily JS (Web)](./dailyjs/)
## [Custom (Web)](./custom/)
Examples that showcase the Daily CallObject using our Javascript library
Examples that showcase the Daily call object using our Javascript library
## [Prebuilt UI](./prebuilt-ui)
## [Prebuilt UI](./prebuilt)
Examples that showcase using and customizing the Daily Prebuilt UI

View File

@ -1,4 +1,4 @@
# Daily JS Examples
# Custom Examples (Daily JS)
### [🤙 Basic call](./basic-call)
@ -30,7 +30,7 @@ Demonstrates using manual track management to support larger call sizes
We recommend starting with the [basic call](./basic-call) example, showcasing the common flow of a call Daily call, device management and error handling.
Run an examples with `yarn workspace @dailyjs/[demo-name] dev` (replacing `[demo-name]` with the name of the demo you'd like to run e.g. `basic-call`.
Run an examples with `yarn workspace @custom/[demo-name] dev` (replacing `[demo-name]` with the name of the demo you'd like to run e.g. `basic-call`.
- Please ensure your Daily rooms are setup to use [web sockets](https://docs.daily.co/reference#domain-configuration)
- Follow the instructions within each demo first, making sure to set all the necassary local environment variables etc

View File

@ -4,7 +4,7 @@
### Live example
**[See it in action here ➡️](https://dailyjs-basic-call.vercel.app)**
**[See it in action here ➡️](https://custom-basic-call.vercel.app)**
---
@ -28,7 +28,7 @@ mv env.example .env.local
# from project root...
yarn
yarn workspace @dailyjs/basic-call dev
yarn workspace @custom/basic-call dev
```
## How does this example work?
@ -47,7 +47,7 @@ Primary call context that manages Daily call state, participant state and call o
**[useCallMachine.js](../shared/contexts/useCallMachine.js)**
Abstraction hook that manages Daily call state and error handling
**[ParticipantProvider.js](../shared/contexts/ParticipantProvider.js)**
**[ParticipantsProvider.js](../shared/contexts/ParticipantsProvider.js)**
Manages participant state and abstracts common selectors / derived data
## Deploy your own on Vercel

View File

@ -1,10 +1,10 @@
import React, { useMemo } from 'react';
import ExpiryTimer from '@dailyjs/shared/components/ExpiryTimer';
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import { useCallUI } from '@dailyjs/shared/hooks/useCallUI';
import ExpiryTimer from '@custom/shared/components/ExpiryTimer';
import { useCallState } from '@custom/shared/contexts/CallProvider';
import { useCallUI } from '@custom/shared/hooks/useCallUI';
import PropTypes from 'prop-types';
import Room from '../Room';
import Room from '../Call/Room';
import { Asides } from './Asides';
import { Modals } from './Modals';
@ -13,7 +13,7 @@ export const App = ({ customComponentForState }) => {
const componentForState = useCallUI({
state,
room: () => <Room />,
room: <Room />,
...customComponentForState,
});

View File

@ -1,6 +1,7 @@
import React from 'react';
import { PeopleAside } from '@dailyjs/shared/components/Aside/PeopleAside';
import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider';
import { NetworkAside } from '@custom/shared/components/Aside';
import { PeopleAside } from '@custom/shared/components/Aside';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
export const Asides = () => {
const { asides } = useUIState();
@ -8,6 +9,7 @@ export const Asides = () => {
return (
<>
<PeopleAside />
<NetworkAside />
{asides.map((AsideComponent) => (
<AsideComponent key={AsideComponent.name} />
))}

View File

@ -1,6 +1,6 @@
import React from 'react';
import DeviceSelectModal from '@dailyjs/shared/components/DeviceSelectModal/DeviceSelectModal';
import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider';
import DeviceSelectModal from '@custom/shared/components/DeviceSelectModal/DeviceSelectModal';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
export const Modals = () => {
const { modals } = useUIState();

View File

@ -1,2 +1 @@
export { App as default } from './App';
export { Modals } from './Modals';

View File

@ -1,12 +1,12 @@
import React, { useMemo } from 'react';
import { Audio } from '@dailyjs/shared/components/Audio';
import { BasicTray } from '@dailyjs/shared/components/Tray';
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
import useJoinSound from '@dailyjs/shared/hooks/useJoinSound';
import { Audio } from '@custom/shared/components/Audio';
import { BasicTray } from '@custom/shared/components/Tray';
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
import useJoinSound from '@custom/shared/hooks/useJoinSound';
import PropTypes from 'prop-types';
import WaitingRoom from '../WaitingRoom';
import { WaitingRoom } from './WaitingRoom';
export const RoomContainer = ({ children }) => {
export const Container = ({ children }) => {
const { isOwner } = useParticipants();
useJoinSound();
@ -42,8 +42,8 @@ export const RoomContainer = ({ children }) => {
);
};
RoomContainer.propTypes = {
Container.propTypes = {
children: PropTypes.node,
};
export default RoomContainer;
export default Container;

View File

@ -1,7 +1,7 @@
import React, { useMemo } from 'react';
import HeaderCapsule from '@dailyjs/shared/components/HeaderCapsule';
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider';
import HeaderCapsule from '@custom/shared/components/HeaderCapsule';
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
export const Header = () => {
const { participantCount } = useParticipants();
@ -10,7 +10,13 @@ export const Header = () => {
return useMemo(
() => (
<header className="room-header">
<img src="assets/daily-logo.svg" alt="Daily" className="logo" />
<img
src="/assets/daily-logo.svg"
alt="Daily"
className="logo"
width="80"
height="32"
/>
<HeaderCapsule>Basic call demo</HeaderCapsule>
<HeaderCapsule>
@ -31,7 +37,8 @@ export const Header = () => {
flex: 0 0 auto;
column-gap: var(--spacing-xxs);
box-sizing: border-box;
padding: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-sm) var(--spacing-xxs)
var(--spacing-sm);
align-items: center;
width: 100%;
}

View File

@ -0,0 +1,19 @@
import React from 'react';
import VideoContainer from '@custom/shared/components/VideoContainer/VideoContainer';
import { Container } from './Container';
import { Header } from './Header';
import { VideoGrid } from './VideoGrid';
export function Room() {
return (
<Container>
<Header />
<VideoContainer>
<VideoGrid />
</VideoContainer>
</Container>
);
}
export default Room;

View File

@ -1,7 +1,7 @@
import React, { useState, useMemo, useEffect, useRef } from 'react';
import Tile from '@dailyjs/shared/components/Tile';
import { DEFAULT_ASPECT_RATIO } from '@dailyjs/shared/constants';
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
import Tile from '@custom/shared/components/Tile';
import { DEFAULT_ASPECT_RATIO } from '@custom/shared/constants';
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
import { useDeepCompareMemo } from 'use-deep-compare';
/**

View File

@ -2,8 +2,8 @@ import React from 'react';
import {
WaitingRoomModal,
WaitingRoomNotification,
} from '@dailyjs/shared/components/WaitingRoom';
import { useWaitingRoom } from '@dailyjs/shared/contexts/WaitingRoomProvider';
} from '@custom/shared/components/WaitingRoom';
import { useWaitingRoom } from '@custom/shared/contexts/WaitingRoomProvider';
export const WaitingRoom = () => {
const { setShowModal, showModal } = useWaitingRoom();

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Card, CardHeader, CardBody } from '@dailyjs/shared/components/Card';
import Loader from '@dailyjs/shared/components/Loader';
import { Well } from '@dailyjs/shared/components/Well';
import { Card, CardHeader, CardBody } from '@custom/shared/components/Card';
import Loader from '@custom/shared/components/Loader';
import Well from '@custom/shared/components/Well';
import PropTypes from 'prop-types';
export const CreatingRoom = ({ onCreated }) => {

View File

@ -1,14 +1,14 @@
import React, { useEffect, useState } from 'react';
import { Button } from '@dailyjs/shared/components/Button';
import Button from '@custom/shared/components/Button';
import {
Card,
CardBody,
CardFooter,
CardHeader,
} from '@dailyjs/shared/components/Card';
import Field from '@dailyjs/shared/components/Field';
import { TextInput, BooleanInput } from '@dailyjs/shared/components/Input';
import { Well } from '@dailyjs/shared/components/Well';
} from '@custom/shared/components/Card';
import Field from '@custom/shared/components/Field';
import { TextInput, BooleanInput } from '@custom/shared/components/Input';
import Well from '@custom/shared/components/Well';
import PropTypes from 'prop-types';
/**

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Card, CardBody, CardHeader } from '@dailyjs/shared/components/Card';
import { Card, CardBody, CardHeader } from '@custom/shared/components/Card';
export const NotConfigured = () => (
<Card>

View File

@ -0,0 +1,11 @@
# Domain excluding 'https://' and 'daily.co' e.g. 'somedomain'
DAILY_DOMAIN=
# Obtained from https://dashboard.daily.co/developers
DAILY_API_KEY=
# Daily REST API endpoint
DAILY_REST_DOMAIN=https://api.daily.co/v1
# Run in demo mode (will create a demo room for you to try)
DAILY_DEMO_MODE=0

View File

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -1,5 +1,5 @@
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')(['@dailyjs/shared']);
const withTM = require('next-transpile-modules')(['@custom/shared']);
const packageJson = require('./package.json');

View File

@ -0,0 +1,22 @@
{
"name": "@custom/basic-call",
"description": "Basic Call Example",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@custom/shared": "*",
"next": "^11.1.2",
"pluralize": "^8.0.0"
},
"devDependencies": {
"babel-plugin-module-resolver": "^4.1.0",
"next-compose-plugins": "^2.2.1",
"next-transpile-modules": "^8.0.0"
}
}

View File

@ -1,6 +1,5 @@
import React from 'react';
import GlobalHead from '@dailyjs/shared/components/GlobalHead';
import GlobalStyle from '@dailyjs/shared/components/GlobalStyle';
import GlobalStyle from '@custom/shared/components/GlobalStyle';
import Head from 'next/head';
import PropTypes from 'prop-types';
@ -10,7 +9,6 @@ function App({ Component, pageProps }) {
<Head>
<title>Daily - {process.env.PROJECT_TITLE}</title>
</Head>
<GlobalHead />
<GlobalStyle />
<Component
asides={App.asides}

View File

@ -0,0 +1,23 @@
import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Rubik:wght@400;500;600&display=optional"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;

View File

@ -1,5 +1,5 @@
export default async function handler(req, res) {
const { privacy, expiryMinutes } = req.body;
const { privacy, expiryMinutes, ...rest } = req.body;
if (req.method === 'POST') {
console.log(`Creating room on domain ${process.env.DAILY_DOMAIN}`);
@ -16,7 +16,7 @@ export default async function handler(req, res) {
exp: Math.round(Date.now() / 1000) + (expiryMinutes || 5) * 60, // expire in x minutes
eject_at_room_exp: true,
enable_knocking: privacy !== 'public',
enable_recording: 'local',
...rest,
},
}),
};

View File

@ -1,15 +1,16 @@
import React, { useState, useCallback } from 'react';
import { CallProvider } from '@dailyjs/shared/contexts/CallProvider';
import { MediaDeviceProvider } from '@dailyjs/shared/contexts/MediaDeviceProvider';
import { ParticipantsProvider } from '@dailyjs/shared/contexts/ParticipantsProvider';
import { TracksProvider } from '@dailyjs/shared/contexts/TracksProvider';
import { UIStateProvider } from '@dailyjs/shared/contexts/UIStateProvider';
import { WaitingRoomProvider } from '@dailyjs/shared/contexts/WaitingRoomProvider';
import getDemoProps from '@dailyjs/shared/lib/demoProps';
import { CallProvider } from '@custom/shared/contexts/CallProvider';
import { MediaDeviceProvider } from '@custom/shared/contexts/MediaDeviceProvider';
import { ParticipantsProvider } from '@custom/shared/contexts/ParticipantsProvider';
import { TracksProvider } from '@custom/shared/contexts/TracksProvider';
import { UIStateProvider } from '@custom/shared/contexts/UIStateProvider';
import { WaitingRoomProvider } from '@custom/shared/contexts/WaitingRoomProvider';
import getDemoProps from '@custom/shared/lib/demoProps';
import PropTypes from 'prop-types';
import App from '../components/App';
import { CreatingRoom } from '../components/CreatingRoom';
import { Intro, NotConfigured } from '../components/Intro';
import CreatingRoom from '../components/Prejoin/CreatingRoom';
import Intro from '../components/Prejoin/Intro';
import NotConfigured from '../components/Prejoin/NotConfigured';
/**
* Index page
@ -22,7 +23,6 @@ import { Intro, NotConfigured } from '../components/Intro';
export default function Index({
domain,
isConfigured = false,
predefinedRoom = '',
forceFetchToken = false,
forceOwner = false,
subscribeToTracksAutomatically = true,
@ -32,7 +32,7 @@ export default function Index({
customTrayComponent,
customAppComponent,
}) {
const [roomName, setRoomName] = useState(predefinedRoom);
const [roomName, setRoomName] = useState();
const [fetchingToken, setFetchingToken] = useState(false);
const [token, setToken] = useState();
const [tokenError, setTokenError] = useState();
@ -137,7 +137,6 @@ export default function Index({
Index.propTypes = {
isConfigured: PropTypes.bool.isRequired,
predefinedRoom: PropTypes.string,
domain: PropTypes.string,
asides: PropTypes.arrayOf(PropTypes.func),
modals: PropTypes.arrayOf(PropTypes.func),

View File

@ -1,5 +1,5 @@
import React from 'react';
import MessageCard from '@dailyjs/shared/components/MessageCard';
import MessageCard from '@custom/shared/components/MessageCard';
export default function RoomNotFound() {
return (

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -4,7 +4,7 @@
### Live example
**[See it in action here ➡️](https://dailyjs-flying-emojis.vercel.app)**
**[See it in action here ➡️](https://custom-flying-emojis.vercel.app)**
---
@ -23,7 +23,7 @@ Please note: this demo is not currently mobile optimised
mv env.example .env.local
yarn
yarn workspace @dailyjs/flying-emojis dev
yarn workspace @custom/flying-emojis dev
```
## Deploy your own on Vercel

View File

@ -1,6 +1,6 @@
import React from 'react';
import App from '@dailyjs/basic-call/components/App';
import FlyingEmojiOverlay from '../FlyingEmojis/FlyingEmojisOverlay';
import App from '@custom/basic-call/components/App';
import FlyingEmojiOverlay from './FlyingEmojisOverlay';
export const AppWithEmojis = () => (
<>

View File

@ -1,5 +1,5 @@
import React, { useEffect, useRef, useCallback } from 'react';
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import { useCallState } from '@custom/shared/contexts/CallProvider';
const EMOJI_MAP = {
fire: '🔥',

View File

@ -1,8 +1,8 @@
import React, { useEffect, useState } from 'react';
import Button from '@dailyjs/shared/components/Button';
import { TrayButton } from '@dailyjs/shared/components/Tray';
import { ReactComponent as IconStar } from '@dailyjs/shared/icons/star-md.svg';
import Button from '@custom/shared/components/Button';
import { TrayButton } from '@custom/shared/components/Tray';
import { ReactComponent as IconStar } from '@custom/shared/icons/star-md.svg';
const COOLDOWN = 1500;

View File

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

View File

@ -1,7 +1,7 @@
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')([
'@dailyjs/shared',
'@dailyjs/basic-call',
'@custom/shared',
'@custom/basic-call',
]);
const packageJson = require('./package.json');

View File

@ -1,5 +1,5 @@
{
"name": "@dailyjs/flying-emojis",
"name": "@custom/flying-emojis",
"description": "Basic Call + Flying Emojis",
"version": "0.1.0",
"private": true,
@ -10,8 +10,8 @@
"lint": "next lint"
},
"dependencies": {
"@dailyjs/basic-call": "*",
"@dailyjs/shared": "*",
"@custom/basic-call": "*",
"@custom/shared": "*",
"next": "^11.0.0",
"pluralize": "^8.0.0",
"react": "^17.0.2",

View File

@ -1,5 +1,5 @@
import React from 'react';
import App from '@dailyjs/basic-call/pages/_app';
import App from '@custom/basic-call/pages/_app';
import AppWithEmojis from '../components/App';
import Tray from '../components/Tray';

View File

@ -0,0 +1,3 @@
import Doc from '@custom/basic-call/pages/_document';
export default Doc;

View File

@ -1,5 +1,5 @@
import Index from '@dailyjs/basic-call/pages';
import getDemoProps from '@dailyjs/shared/lib/demoProps';
import Index from '@custom/basic-call/pages';
import getDemoProps from '@custom/shared/lib/demoProps';
export async function getStaticProps() {
const defaultProps = getDemoProps();

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -4,7 +4,7 @@
### Live example
**[See it in action here ➡️](https://dailyjs-live-streaming.vercel.app)**
**[See it in action here ➡️](https://custom-live-streaming.vercel.app)**
---
@ -25,7 +25,7 @@ Please note: this demo is not currently mobile optimised
mv env.example .env.local
yarn
yarn workspace @dailyjs/live-streaming dev
yarn workspace @custom/live-streaming dev
```
## How does this example work?

View File

@ -1,7 +1,7 @@
import React from 'react';
import App from '@dailyjs/basic-call/components/App';
import { LiveStreamingProvider } from '../../contexts/LiveStreamingProvider';
import App from '@custom/basic-call/components/App';
import { LiveStreamingProvider } from '../contexts/LiveStreamingProvider';
// Extend our basic call app component with the live streaming context
export const AppWithLiveStreaming = () => (

View File

@ -1,14 +1,14 @@
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';
import { Button } from '@custom/shared/components/Button';
import { CardBody } from '@custom/shared/components/Card';
import Field from '@custom/shared/components/Field';
import { TextInput, SelectInput } from '@custom/shared/components/Input';
import Modal from '@custom/shared/components/Modal';
import { Well } from '@custom/shared/components/Well';
import { useCallState } from '@custom/shared/contexts/CallProvider';
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
import { useLiveStreaming } from '../contexts/LiveStreamingProvider';
export const LIVE_STREAMING_MODAL = 'live-streaming';
@ -55,7 +55,7 @@ export const LiveStreamingModal = () => {
isOpen={currentModals[LIVE_STREAMING_MODAL]}
onClose={() => closeModal(LIVE_STREAMING_MODAL)}
actions={[
<Button fullWidth variant="outline">
<Button key="close" fullWidth variant="outline">
Close
</Button>,
!isStreaming ? (

View File

@ -1,11 +1,11 @@
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 { TrayButton } from '@custom/shared/components/Tray';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
import { ReactComponent as IconStream } from '@custom/shared/icons/streaming-md.svg';
import { useLiveStreaming } from '../../contexts/LiveStreamingProvider';
import { LIVE_STREAMING_MODAL } from '../LiveStreamingModal';
import { useLiveStreaming } from '../contexts/LiveStreamingProvider';
import { LIVE_STREAMING_MODAL } from './LiveStreamingModal';
export const Tray = () => {
const { openModal } = useUIState();

View File

@ -5,8 +5,8 @@ import React, {
useEffect,
useCallback,
} from 'react';
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import { useUIState } from '@dailyjs/shared/contexts/UIStateProvider';
import { useCallState } from '@custom/shared/contexts/CallProvider';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
import PropTypes from 'prop-types';
export const LiveStreamingContext = createContext();

View File

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 270 KiB

View File

@ -1,7 +1,7 @@
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')([
'@dailyjs/shared',
'@dailyjs/basic-call',
'@custom/shared',
'@custom/basic-call',
]);
const packageJson = require('./package.json');

View File

@ -1,5 +1,5 @@
{
"name": "@dailyjs/live-streaming",
"name": "@custom/live-streaming",
"description": "Basic Call + Live Streaming",
"version": "0.1.0",
"private": true,
@ -10,8 +10,8 @@
"lint": "next lint"
},
"dependencies": {
"@dailyjs/shared": "*",
"@dailyjs/basic-call": "*",
"@custom/shared": "*",
"@custom/basic-call": "*",
"next": "^11.0.0",
"pluralize": "^8.0.0",
"react": "^17.0.2",

View File

@ -1,5 +1,5 @@
import React from 'react';
import App from '@dailyjs/basic-call/pages/_app';
import App from '@custom/basic-call/pages/_app';
import AppWithLiveStreaming from '../components/App';
import { LiveStreamingModal } from '../components/LiveStreamingModal';

View File

@ -0,0 +1,3 @@
import Doc from '@custom/basic-call/pages/_document';
export default Doc;

View File

@ -1,5 +1,5 @@
import Index from '@dailyjs/basic-call/pages';
import getDemoProps from '@dailyjs/shared/lib/demoProps';
import Index from '@custom/basic-call/pages';
import getDemoProps from '@custom/shared/lib/demoProps';
export async function getStaticProps() {
const defaultProps = getDemoProps();

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -0,0 +1,46 @@
# Live Transcription
![Live Transcription](./image.png)
### Live example
**[See it in action here ➡️](https://custom-live-transcription.vercel.app)**
---
## What does this demo do?
- Use `startTranscription()` and `stopTranscription()` methods to create a transcript of a call
- Integrates Deepgram transcription service into Daily calls
- Listen for incoming transcription messages using the call object `app-message` event
- Extend the basic call demo with a transcription provider and aside
- Show a notification bubble on transcript tray button when a new message is received
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 @custom/live-transcription dev
```
### Setting up transcription
For testing the transcription service, you will have to register for a Deepgram API key and configure your Daily domain with that key.
TODO: Add more instructions here, or point to docs when ready.
## How does this example work?
In this example we extend the [basic call demo](../basic-call) with the ability to generate transcription of the meeting in real time and log that in a side panel.
We pass a custom tray object, a custom app object (wrapping the original in a new `TranscriptionProvider`) as well as add our `TranscriptionAside` panel. We also symlink both the `public` and `pages/api` folders from the basic call.
## Deploy your own on Vercel
[![Deploy with Vercel](https://vercel.com/button)](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)

View File

@ -0,0 +1,13 @@
import React from 'react';
import App from '@custom/basic-call/components/App';
import { TranscriptionProvider } from '../contexts/TranscriptionProvider';
// Extend our basic call app component with the Live Transcription context
export const AppWithTranscription = () => (
<TranscriptionProvider>
<App />
</TranscriptionProvider>
);
export default AppWithTranscription;

View File

@ -0,0 +1,97 @@
import React, { useEffect, useRef, useState } from 'react';
import { Aside } from '@custom/shared/components/Aside';
import Button from '@custom/shared/components/Button';
import { useCallState } from '@custom/shared/contexts/CallProvider';
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
import { useTranscription } from '../contexts/TranscriptionProvider';
export const TRANSCRIPTION_ASIDE = 'transcription';
export const TranscriptionAside = () => {
const { callObject } = useCallState();
const { showAside, setShowAside } = useUIState();
const { transcriptionHistory } = useTranscription();
const [isTranscribing, setIsTranscribing] = useState(false);
const { isOwner } = useParticipants();
const msgWindowRef = useRef();
useEffect(() => {
if (msgWindowRef.current) {
msgWindowRef.current.scrollTop = msgWindowRef.current.scrollHeight;
}
}, [transcriptionHistory?.length]);
if (!showAside || showAside !== TRANSCRIPTION_ASIDE) {
return null;
}
async function startTranscription() {
setIsTranscribing(true);
await callObject.startTranscription();
}
async function stopTranscription() {
setIsTranscribing(false);
await callObject.stopTranscription();
}
return (
<Aside onClose={() => setShowAside(false)}>
{isOwner && (
<div className="owner-actions">
<Button
fullWidth
size="tiny"
disabled={isTranscribing}
onClick={() =>
startTranscription()
}
>
{isTranscribing ? 'Transcribing' : 'Start transcribing'}
</Button>
<Button
fullWidth
size="tiny"
disabled={!isTranscribing}
onClick={() =>
stopTranscription()
}
>
Stop transcribing
</Button>
</div>
)}
<div className="messages-container" ref={msgWindowRef}>
{transcriptionHistory.map((msg) => (
<div key={msg.id}>
<span className="sender">{msg.sender}: </span>
<span className="content">{msg.message}</span>
</div>
))}
</div>
<style jsx>{`
.owner-actions {
display: flex;
font-weight: bold;
align-self: center;
gap: var(--spacing-xxxs);
margin: var(--spacing-xs) var(--spacing-xxs);
}
.messages-container {
flex: 1;
overflow-y: scroll;
margin-left: var(--spacing-xs);
}
.messages-container .sender {
font-weight: bold;
}
`}</style>
</Aside>
);
};
export default TranscriptionAside;

View File

@ -0,0 +1,26 @@
import React from 'react';
import { TrayButton } from '@custom/shared/components/Tray';
import { useUIState } from '@custom/shared/contexts/UIStateProvider';
import { ReactComponent as IconTranscription } from '@custom/shared/icons/chat-md.svg';
import { useTranscription } from '../contexts/TranscriptionProvider';
import { TRANSCRIPTION_ASIDE } from './TranscriptionAside';
export const Tray = () => {
const { toggleAside } = useUIState();
const { hasNewMessages } = useTranscription();
return (
<TrayButton
label="Transcript"
bubble={hasNewMessages}
onClick={() => {
toggleAside(TRANSCRIPTION_ASIDE);
}}
>
<IconTranscription />
</TrayButton>
);
};
export default Tray;

View File

@ -0,0 +1,92 @@
import React, {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from 'react';
import { useCallState } from '@custom/shared/contexts/CallProvider';
import { nanoid } from 'nanoid';
import PropTypes from 'prop-types';
export const TranscriptionContext = createContext();
export const TranscriptionProvider = ({ children }) => {
const { callObject } = useCallState();
const [transcriptionHistory, setTranscriptionHistory] = useState([]);
const [hasNewMessages, setHasNewMessages] = useState(false);
const [isTranscribing, setIsTranscribing] = useState(false);
const handleNewMessage = useCallback(
(e) => {
const participants = callObject.participants();
// Collect only transcription messages, and gather enough
// words to be able to post messages at sentence intervals
if (e.fromId === 'transcription' && e.data?.is_final) {
// Get the sender's current display name or the local name
const sender = e.data?.session_id !== participants.local.session_id
? participants[e.data.session_id].user_name
: participants.local.user_name;
setTranscriptionHistory((oldState) => [
...oldState,
{ sender, message: e.data.text, id: nanoid() },
]);
}
setHasNewMessages(true);
},
[callObject]
);
const handleTranscriptionStarted = useCallback(() => {
console.log('💬 Transcription started');
setIsTranscribing(true);
}, []);
const handleTranscriptionStopped = useCallback(() => {
console.log('💬 Transcription stopped');
setIsTranscribing(false);
}, []);
const handleTranscriptionError = useCallback(() => {
console.log('❗ Transcription error!');
setIsTranscribing(false);
}, []);
useEffect(() => {
if (!callObject) {
return false;
}
console.log(`💬 Transcription provider listening for app messages`);
callObject.on('app-message', handleNewMessage);
callObject.on('transcription-started', handleTranscriptionStarted);
callObject.on('transcription-stopped', handleTranscriptionStopped);
callObject.on('transcription-error', handleTranscriptionError);
return () => callObject.off('app-message', handleNewMessage);
}, [callObject, handleNewMessage, handleTranscriptionStarted, handleTranscriptionStopped, handleTranscriptionError]);
return (
<TranscriptionContext.Provider
value={{
isTranscribing,
transcriptionHistory,
hasNewMessages,
setHasNewMessages,
}}
>
{children}
</TranscriptionContext.Provider>
);
};
TranscriptionProvider.propTypes = {
children: PropTypes.node,
};
export const useTranscription = () => useContext(TranscriptionContext);

View File

@ -1,7 +1,7 @@
const withPlugins = require('next-compose-plugins');
const withTM = require('next-transpile-modules')([
'@dailyjs/shared',
'@dailyjs/basic-call',
'@custom/shared',
'@custom/basic-call',
]);
const packageJson = require('./package.json');

View File

@ -1,6 +1,6 @@
{
"name": "@dailyjs/basic-call",
"description": "Basic Call Example",
"name": "@custom/live-transcription",
"description": "Basic Call + Transcription Example",
"version": "0.1.0",
"private": true,
"scripts": {
@ -10,7 +10,8 @@
"lint": "next lint"
},
"dependencies": {
"@dailyjs/shared": "*",
"@custom/shared": "*",
"@custom/basic-call": "*",
"next": "^11.0.0",
"pluralize": "^8.0.0",
"react": "^17.0.2",

View File

@ -0,0 +1,12 @@
import React from 'react';
import App from '@custom/basic-call/pages/_app';
import AppWithTranscription from '../components/App';
import TranscriptionAside from '../components/TranscriptionAside';
import Tray from '../components/Tray';
App.asides = [TranscriptionAside];
App.customAppComponent = <AppWithTranscription />;
App.customTrayComponent = <Tray />;
export default App;

View File

@ -1,5 +1,5 @@
import Index from '@dailyjs/basic-call/pages';
import getDemoProps from '@dailyjs/shared/lib/demoProps';
import Index from '@custom/basic-call/pages';
import getDemoProps from '@custom/shared/lib/demoProps';
export async function getStaticProps() {
const defaultProps = getDemoProps();

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,7 +1,7 @@
import React from 'react';
import App from '@dailyjs/basic-call/components/App';
import Room from '@dailyjs/basic-call/components/Room';
import App from '@custom/basic-call/components/App';
import Room from '@custom/basic-call/components/Room';
import PaginatedVideoGrid from '../PaginatedVideoGrid';
export const AppWithPagination = () => (

View File

@ -4,7 +4,7 @@
### Live example
**[See it in action here ➡️](https://dailyjs-pagination.vercel.app)**
**[See it in action here ➡️](https://custom-pagination.vercel.app)**
---
@ -22,7 +22,7 @@ Please note: this demo is not currently mobile optimised
mv env.example .env.local
yarn
yarn workspace @dailyjs/live-streaming dev
yarn workspace @custom/live-streaming dev
```
Note: this example uses an additional env `MANUAL_TRACK_SUBS=1` that will disable [automatic track management](https://docs.daily.co/reference#%EF%B8%8F-setsubscribetotracksautomatically).

View File

@ -0,0 +1,15 @@
import React from 'react';
import App from '@custom/basic-call/components/App';
import Room from '@custom/basic-call/components/Call/Room';
import PaginatedVideoGrid from './PaginatedVideoGrid';
export const AppWithPagination = () => (
<App
customComponentForState={{
room: <Room MainComponent={PaginatedVideoGrid} />,
}}
/>
);
export default AppWithPagination;

View File

@ -5,19 +5,19 @@ import React, {
useEffect,
useState,
} from 'react';
import { Button } from '@dailyjs/shared/components/Button';
import Tile from '@dailyjs/shared/components/Tile';
import { Button } from '@custom/shared/components/Button';
import Tile from '@custom/shared/components/Tile';
import {
DEFAULT_ASPECT_RATIO,
MEETING_STATE_JOINED,
} from '@dailyjs/shared/constants';
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import { useParticipants } from '@dailyjs/shared/contexts/ParticipantsProvider';
import { isLocalId } from '@dailyjs/shared/contexts/participantsState';
import { useActiveSpeaker } from '@dailyjs/shared/hooks/useActiveSpeaker';
import { useCamSubscriptions } from '@dailyjs/shared/hooks/useCamSubscriptions';
import { ReactComponent as IconArrow } from '@dailyjs/shared/icons/raquo-md.svg';
import sortByKey from '@dailyjs/shared/lib/sortByKey';
} from '@custom/shared/constants';
import { useCallState } from '@custom/shared/contexts/CallProvider';
import { useParticipants } from '@custom/shared/contexts/ParticipantsProvider';
import { isLocalId } from '@custom/shared/contexts/participantsState';
import { useActiveSpeaker } from '@custom/shared/hooks/useActiveSpeaker';
import { useCamSubscriptions } from '@custom/shared/hooks/useCamSubscriptions';
import { ReactComponent as IconArrow } from '@custom/shared/icons/raquo-md.svg';
import sortByKey from '@custom/shared/lib/sortByKey';
import PropTypes from 'prop-types';
import { useDeepCompareMemo } from 'use-deep-compare';

View File

@ -1,8 +1,8 @@
import React from 'react';
import { TrayButton } from '@dailyjs/shared/components/Tray';
import { useCallState } from '@dailyjs/shared/contexts/CallProvider';
import { ReactComponent as IconAdd } from '@dailyjs/shared/icons/add-md.svg';
import { TrayButton } from '@custom/shared/components/Tray';
import { useCallState } from '@custom/shared/contexts/CallProvider';
import { ReactComponent as IconAdd } from '@custom/shared/icons/add-md.svg';
export const Tray = () => {
const { callObject } = useCallState();

Some files were not shown because too many files have changed in this diff Show More