Skip to content

Commit

Permalink
Release recording start stop feature (#518)
Browse files Browse the repository at this point in the history
* Ahoyapps 957 recording start stop (#379)

* Add useIsRecording hook and test

* Add recordingrules route to local development server

* Add info variant to snackbar component

* Add recording indicator to MainParticipantInfo with test

* Add InfoIcon

* Add RecordingButton to menu component

* Upgrade Twilio dependency

* Add RecordingRules type to types.ts

* Install notistack library

* Add new icons

* Add icons and snackbar to Menu component

* Update snackbar to accept React components as message

* Create RecordingButton component

* Add InfoIconOutlined

* Update styles in Menu component

* Add tests for RecordingButton

* Install twilio-video 2.9.0

* Add updateRecordingRules functions

* Fix tests

* Update recording icon animation

* Add tooltip to MainParticipantInfo

* Fix settings menu location

* Add new recording UI to Menu component

* Update Menu tests

* Change Menu Item text

* Some cleanup

* Video 4708 recording start stop UI updates (#491)

* Fix tests after merging in master

* Add recordingrules route to server

* Fix TS errors

* Remove ConfirmRecordingDialog from Menu

* Add RecordingNotificationsComponent to app

* Add RecodringNotificationTests

* Update Menu tests

* Fix linting issues

* Update snapshot tests

* Add firebase recording rules (#493)

* Add updateRecordingRules function to useFirebaseAuth

* Add additional files to .gcloudignore

* Add newline

* Video 4885 recording cypress tests (#495)

* Conditionally render recording rules button (#510)

* Move the RoomType setting to src/state/index.ts. Also report recordingRule errors correctly

* Only render the recording button in group rooms. Add tests

* Remove log

* readme and changelog for recording start stop feature (#509)

* Add date to changelog

Co-authored-by: olipyskoty <[email protected]>
Co-authored-by: Sean Coleman <[email protected]>
  • Loading branch information
3 people authored May 12, 2021
1 parent 2c3248e commit f08865c
Show file tree
Hide file tree
Showing 32 changed files with 808 additions and 87 deletions.
5 changes: 3 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ jobs:
- run:
name: 'Build app with Firebase auth disabled'
command: npm run build
environment:
REACT_APP_TOKEN_ENDPOINT: 'http://localhost:8081/token'

- run:
name: 'Set environment variables for local token server (used by Cypress tests)'
Expand All @@ -59,6 +57,9 @@ jobs:
- run: npm run cypress:ci

- store_artifacts:
path: cypress/screenshots

- store_test_results:
path: test-reports

Expand Down
4 changes: 3 additions & 1 deletion .gcloudignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

# Node.js dependencies:
node_modules/

# Application source and tests
src/
cypress/
coverage/
cypress/
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.4.0 (May 12, 2021)

### New Feature

- This release adds a Start-Stop Recording feature for Group Rooms. This feature allows users to control when to record the contents of the Video Room. Recordings are on a per Track basis and are accessible via the Twilio Console. This feature is powered by the [Twilio Recording Rules API](https://www.twilio.com/docs/video/api/recording-rules). For more information on the Recording Rules API, please see this [blog post](https://www.twilio.com/blog/video-recording-rules-api).

## 0.3.2 (May 11, 2021)

### New Feature
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ If any errors occur after running a [Twilio CLI RTC Plugin](https://github.com/t

After running the command [to deploy the app to Twilio](#deploy-the-app-to-twilio), the room type will be returned in the command line output. Each room type provides a different video experience. More details about these room types can be found [here](https://www.twilio.com/docs/video/tutorials/understanding-video-rooms). The rest of this section explains how these room types affect the behavior of the video app.

_Group_ - The Group room type allows up to fifty participants to join a video room in the app. The Network Quality Level (NQL) indicators and dominant speaker are demonstrated with this room type. Also, the VP8 video codec with simulcast enabled along with a bandwidth profile are set by default in order to provide an optimal group video app experience.
_Group_ - The Group room type allows up to fifty participants to join a video room in the app. The Network Quality Level (NQL) indicators, dominant speaker, and start-stop recordings are demonstrated with this room type. Also, the VP8 video codec with simulcast enabled along with a bandwidth profile are set by default in order to provide an optimal group video app experience.

_Small Group_ - The Small Group room type provides an identical group video app experience except for a smaller limit of four participants.

_Peer-to-peer_ - Although up to ten participants can join a room using the Peer-to-peer (P2P) room type, it is ideal for a one to one video experience. The NQL indicators, bandwidth profiles, and dominant speaker cannot be used with this room type. Thus, they are not demonstrated in the video app. Also, the VP8 video codec with simulcast disabled and 720p minimum video capturing dimensions are also set by default in order to provide an optimal one to one video app experience. If more than ten participants join a room with this room type, then the video app will present an error.
_Peer-to-peer_ - Although up to ten participants can join a room using the Peer-to-peer (P2P) room type, it is ideal for a one to one video experience. The NQL indicators, bandwidth profiles, dominant speaker, and start-stop recordings cannot be used with this room type. Thus, they are not demonstrated in the video app. Also, the VP8 video codec with simulcast disabled and 720p minimum video capturing dimensions are also set by default in order to provide an optimal one to one video app experience. If more than ten participants join a room with this room type, then the video app will present an error.

_Go_ - The Go room type provides a similar Peer-to-peer video app experience except for a smaller limit of two participants. If more than two participants join a room with this room type, then the video app will present an error.

Expand All @@ -109,6 +109,7 @@ The Video app has the following features:
- [x] [Dominant speaker](https://www.twilio.com/docs/video/detecting-dominant-speaker) indicator
- [x] [Network quality](https://www.twilio.com/docs/video/using-network-quality-api) indicator
- [x] Defines participant bandwidth usage with the [Bandwidth Profile API](https://www.twilio.com/docs/video/tutorials/using-bandwidth-profile-api)
- [x] Start and stop recording with the [Recording Rules API](https://www.twilio.com/docs/video/api/recording-rules)

## Browser Support

Expand Down
30 changes: 28 additions & 2 deletions cypress/integration/twilio-video.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,32 @@ context('A video app user', () => {
cy.getParticipant('test1').should('not.exist');
cy.get('[data-cy-main-participant]').should('contain', 'testuser');
});

describe('the recording start/stop feature', () => {
before(() => {
cy.get('footer [data-cy-more-button]').click();
cy.get('[data-cy-recording-button]').click();
cy.wait(2000);
});

after(() => {
cy.wait(3000);
});

it('should see the recording indicator and notification after clicking "Start Recording"', () => {
cy.get('[data-cy-recording-indicator]').should('be.visible');
cy.contains('Recording has started').should('be.visible');
cy.get('footer [data-cy-more-button]').click();
cy.get('[data-cy-recording-button]').click();
});

it('should see "Recording Complete" notification, and not the recording indicator after clicking "Stop Recording"', () => {
cy.get('footer [data-cy-more-button]').click();
cy.get('[data-cy-recording-button]').click();
cy.get('[data-cy-recording-indicator]').should('not.exist');
cy.contains('Recording Complete').should('be.visible');
});
});
});

describe('when entering a room with one participant', () => {
Expand Down Expand Up @@ -112,7 +138,7 @@ context('A video app user', () => {
// to make the message list taller than its container so that we can test the scrolling behavior:
before(() => {
cy.get('[data-cy-chat-button]').click();
// Create an array with 15 values, then send a message when looping over each of them:
// Create an array with 15 values, then send a message when looping over each of them:
Array(15)
.fill(true)
.forEach((_, i) => {
Expand All @@ -121,7 +147,7 @@ context('A video app user', () => {
message: 'welcome to the chat! - ' + i,
});
});
// Wait 1 second for the above to complete:
// Wait 1 second for the above to complete:
cy.wait(1000);
cy.contains('welcome to the chat! - 14');
});
Expand Down
65 changes: 29 additions & 36 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"dependencies": {
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
"@twilio-labs/plugin-rtc": "^0.8.1",
"@twilio-labs/plugin-rtc": "^0.8.2",
"@twilio/conversations": "^1.1.0",
"@types/d3-timer": "^1.0.9",
"@types/dotenv": "^8.2.0",
Expand Down
5 changes: 5 additions & 0 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ app.use(express.json());
const tokenFunction: ServerlessFunction = require('@twilio-labs/plugin-rtc/src/serverless/functions/token').handler;
const tokenEndpoint = createExpressHandler(tokenFunction);

const recordingRulesFunction: ServerlessFunction = require('@twilio-labs/plugin-rtc/src/serverless/functions/recordingrules')
.handler;
const recordingRulesEndpoint = createExpressHandler(recordingRulesFunction);

const noopMiddleware: RequestHandler = (_, __, next) => next();
const authMiddleware =
process.env.REACT_APP_SET_AUTH === 'firebase' ? require('./firebaseAuthMiddleware') : noopMiddleware;

app.all('/token', authMiddleware, tokenEndpoint);
app.all('/recordingrules', authMiddleware, recordingRulesEndpoint);

app.use((req, res, next) => {
// Here we add Cache-Control headers in accordance with the create-react-app best practices.
Expand Down
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import MenuBar from './components/MenuBar/MenuBar';
import MobileTopMenuBar from './components/MobileTopMenuBar/MobileTopMenuBar';
import PreJoinScreens from './components/PreJoinScreens/PreJoinScreens';
import ReconnectingNotification from './components/ReconnectingNotification/ReconnectingNotification';
import RecordingNotifications from './components/RecordingNotifications/RecordingNotifications';
import Room from './components/Room/Room';

import useHeight from './hooks/useHeight/useHeight';
Expand Down Expand Up @@ -41,6 +42,7 @@ export default function App() {
) : (
<Main>
<ReconnectingNotification />
<RecordingNotifications />
<MobileTopMenuBar />
<Room />
<MenuBar />
Expand Down
22 changes: 21 additions & 1 deletion src/components/MainParticipantInfo/MainParticipantInfo.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import React from 'react';
import MainParticipantInfo from './MainParticipantInfo';
import AvatarIcon from '../../icons/AvatarIcon';
import { shallow } from 'enzyme';

import useIsRecording from '../../hooks/useIsRecording/useIsRecording';
import useIsTrackSwitchedOff from '../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff';
import useParticipantIsReconnecting from '../../hooks/useParticipantIsReconnecting/useParticipantIsReconnecting';
import usePublications from '../../hooks/usePublications/usePublications';
import useTrack from '../../hooks/useTrack/useTrack';
import useParticipantIsReconnecting from '../../hooks/useParticipantIsReconnecting/useParticipantIsReconnecting';
import useVideoContext from '../../hooks/useVideoContext/useVideoContext';

jest.mock('../../hooks/useParticipantNetworkQualityLevel/useParticipantNetworkQualityLevel', () => () => 4);
Expand All @@ -15,12 +17,14 @@ jest.mock('../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff');
jest.mock('../../hooks/useTrack/useTrack');
jest.mock('../../hooks/useVideoContext/useVideoContext');
jest.mock('../../hooks/useParticipantIsReconnecting/useParticipantIsReconnecting');
jest.mock('../../hooks/useIsRecording/useIsRecording');

const mockUsePublications = usePublications as jest.Mock<any>;
const mockUseIsTrackSwitchedOff = useIsTrackSwitchedOff as jest.Mock<any>;
const mockUseTrack = useTrack as jest.Mock<any>;
const mockUseVideoContext = useVideoContext as jest.Mock<any>;
const mockUseParticipantIsReconnecting = useParticipantIsReconnecting as jest.Mock<boolean>;
const mockUseIsRecording = useIsRecording as jest.Mock<boolean>;

describe('the MainParticipantInfo component', () => {
beforeEach(jest.clearAllMocks);
Expand Down Expand Up @@ -115,4 +119,20 @@ describe('the MainParticipantInfo component', () => {
);
expect(wrapper.text()).toContain('mockIdentity - Screen');
});

it('should not render the recording indicator when isRecording is false', () => {
mockUseIsRecording.mockImplementationOnce(() => false);
const wrapper = shallow(
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
);
expect(wrapper.text()).not.toContain('Recording');
});

it('should render the recording indicator when isRecording is true', () => {
mockUseIsRecording.mockImplementationOnce(() => true);
const wrapper = shallow(
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
);
expect(wrapper.text()).toContain('Recording');
});
});
Loading

0 comments on commit f08865c

Please sign in to comment.