Skip to content

Commit f08865c

Browse files
timmydozaolipyskotyseancoleman2
authored
Release recording start stop feature (#518)
* 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]>
1 parent 2c3248e commit f08865c

32 files changed

+808
-87
lines changed

.circleci/config.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ jobs:
4646
- run:
4747
name: 'Build app with Firebase auth disabled'
4848
command: npm run build
49-
environment:
50-
REACT_APP_TOKEN_ENDPOINT: 'http://localhost:8081/token'
5149

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

60+
- store_artifacts:
61+
path: cypress/screenshots
62+
6263
- store_test_results:
6364
path: test-reports
6465

.gcloudignore

+3-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
# Node.js dependencies:
1717
node_modules/
18+
19+
# Application source and tests
1820
src/
21+
cypress/
1922
coverage/
20-
cypress/

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 0.4.0 (May 12, 2021)
2+
3+
### New Feature
4+
5+
- 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).
6+
17
## 0.3.2 (May 11, 2021)
28

39
### New Feature

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,11 @@ If any errors occur after running a [Twilio CLI RTC Plugin](https://github.com/t
8787

8888
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.
8989

90-
_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.
90+
_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.
9191

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

94-
_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.
94+
_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.
9595

9696
_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.
9797

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

113114
## Browser Support
114115

cypress/integration/twilio-video.spec.js

+28-2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,32 @@ context('A video app user', () => {
8282
cy.getParticipant('test1').should('not.exist');
8383
cy.get('[data-cy-main-participant]').should('contain', 'testuser');
8484
});
85+
86+
describe('the recording start/stop feature', () => {
87+
before(() => {
88+
cy.get('footer [data-cy-more-button]').click();
89+
cy.get('[data-cy-recording-button]').click();
90+
cy.wait(2000);
91+
});
92+
93+
after(() => {
94+
cy.wait(3000);
95+
});
96+
97+
it('should see the recording indicator and notification after clicking "Start Recording"', () => {
98+
cy.get('[data-cy-recording-indicator]').should('be.visible');
99+
cy.contains('Recording has started').should('be.visible');
100+
cy.get('footer [data-cy-more-button]').click();
101+
cy.get('[data-cy-recording-button]').click();
102+
});
103+
104+
it('should see "Recording Complete" notification, and not the recording indicator after clicking "Stop Recording"', () => {
105+
cy.get('footer [data-cy-more-button]').click();
106+
cy.get('[data-cy-recording-button]').click();
107+
cy.get('[data-cy-recording-indicator]').should('not.exist');
108+
cy.contains('Recording Complete').should('be.visible');
109+
});
110+
});
85111
});
86112

87113
describe('when entering a room with one participant', () => {
@@ -112,7 +138,7 @@ context('A video app user', () => {
112138
// to make the message list taller than its container so that we can test the scrolling behavior:
113139
before(() => {
114140
cy.get('[data-cy-chat-button]').click();
115-
// Create an array with 15 values, then send a message when looping over each of them:
141+
// Create an array with 15 values, then send a message when looping over each of them:
116142
Array(15)
117143
.fill(true)
118144
.forEach((_, i) => {
@@ -121,7 +147,7 @@ context('A video app user', () => {
121147
message: 'welcome to the chat! - ' + i,
122148
});
123149
});
124-
// Wait 1 second for the above to complete:
150+
// Wait 1 second for the above to complete:
125151
cy.wait(1000);
126152
cy.contains('welcome to the chat! - 14');
127153
});

package-lock.json

+29-36
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"dependencies": {
77
"@material-ui/core": "^4.9.1",
88
"@material-ui/icons": "^4.9.1",
9-
"@twilio-labs/plugin-rtc": "^0.8.1",
9+
"@twilio-labs/plugin-rtc": "^0.8.2",
1010
"@twilio/conversations": "^1.1.0",
1111
"@types/d3-timer": "^1.0.9",
1212
"@types/dotenv": "^8.2.0",

server/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,16 @@ app.use(express.json());
1414
const tokenFunction: ServerlessFunction = require('@twilio-labs/plugin-rtc/src/serverless/functions/token').handler;
1515
const tokenEndpoint = createExpressHandler(tokenFunction);
1616

17+
const recordingRulesFunction: ServerlessFunction = require('@twilio-labs/plugin-rtc/src/serverless/functions/recordingrules')
18+
.handler;
19+
const recordingRulesEndpoint = createExpressHandler(recordingRulesFunction);
20+
1721
const noopMiddleware: RequestHandler = (_, __, next) => next();
1822
const authMiddleware =
1923
process.env.REACT_APP_SET_AUTH === 'firebase' ? require('./firebaseAuthMiddleware') : noopMiddleware;
2024

2125
app.all('/token', authMiddleware, tokenEndpoint);
26+
app.all('/recordingrules', authMiddleware, recordingRulesEndpoint);
2227

2328
app.use((req, res, next) => {
2429
// Here we add Cache-Control headers in accordance with the create-react-app best practices.

src/App.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import MenuBar from './components/MenuBar/MenuBar';
55
import MobileTopMenuBar from './components/MobileTopMenuBar/MobileTopMenuBar';
66
import PreJoinScreens from './components/PreJoinScreens/PreJoinScreens';
77
import ReconnectingNotification from './components/ReconnectingNotification/ReconnectingNotification';
8+
import RecordingNotifications from './components/RecordingNotifications/RecordingNotifications';
89
import Room from './components/Room/Room';
910

1011
import useHeight from './hooks/useHeight/useHeight';
@@ -41,6 +42,7 @@ export default function App() {
4142
) : (
4243
<Main>
4344
<ReconnectingNotification />
45+
<RecordingNotifications />
4446
<MobileTopMenuBar />
4547
<Room />
4648
<MenuBar />

src/components/MainParticipantInfo/MainParticipantInfo.test.tsx

+21-1
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import React from 'react';
33
import MainParticipantInfo from './MainParticipantInfo';
44
import AvatarIcon from '../../icons/AvatarIcon';
55
import { shallow } from 'enzyme';
6+
7+
import useIsRecording from '../../hooks/useIsRecording/useIsRecording';
68
import useIsTrackSwitchedOff from '../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff';
9+
import useParticipantIsReconnecting from '../../hooks/useParticipantIsReconnecting/useParticipantIsReconnecting';
710
import usePublications from '../../hooks/usePublications/usePublications';
811
import useTrack from '../../hooks/useTrack/useTrack';
9-
import useParticipantIsReconnecting from '../../hooks/useParticipantIsReconnecting/useParticipantIsReconnecting';
1012
import useVideoContext from '../../hooks/useVideoContext/useVideoContext';
1113

1214
jest.mock('../../hooks/useParticipantNetworkQualityLevel/useParticipantNetworkQualityLevel', () => () => 4);
@@ -15,12 +17,14 @@ jest.mock('../../hooks/useIsTrackSwitchedOff/useIsTrackSwitchedOff');
1517
jest.mock('../../hooks/useTrack/useTrack');
1618
jest.mock('../../hooks/useVideoContext/useVideoContext');
1719
jest.mock('../../hooks/useParticipantIsReconnecting/useParticipantIsReconnecting');
20+
jest.mock('../../hooks/useIsRecording/useIsRecording');
1821

1922
const mockUsePublications = usePublications as jest.Mock<any>;
2023
const mockUseIsTrackSwitchedOff = useIsTrackSwitchedOff as jest.Mock<any>;
2124
const mockUseTrack = useTrack as jest.Mock<any>;
2225
const mockUseVideoContext = useVideoContext as jest.Mock<any>;
2326
const mockUseParticipantIsReconnecting = useParticipantIsReconnecting as jest.Mock<boolean>;
27+
const mockUseIsRecording = useIsRecording as jest.Mock<boolean>;
2428

2529
describe('the MainParticipantInfo component', () => {
2630
beforeEach(jest.clearAllMocks);
@@ -115,4 +119,20 @@ describe('the MainParticipantInfo component', () => {
115119
);
116120
expect(wrapper.text()).toContain('mockIdentity - Screen');
117121
});
122+
123+
it('should not render the recording indicator when isRecording is false', () => {
124+
mockUseIsRecording.mockImplementationOnce(() => false);
125+
const wrapper = shallow(
126+
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
127+
);
128+
expect(wrapper.text()).not.toContain('Recording');
129+
});
130+
131+
it('should render the recording indicator when isRecording is true', () => {
132+
mockUseIsRecording.mockImplementationOnce(() => true);
133+
const wrapper = shallow(
134+
<MainParticipantInfo participant={{ identity: 'mockIdentity' } as any}>mock children</MainParticipantInfo>
135+
);
136+
expect(wrapper.text()).toContain('Recording');
137+
});
118138
});

0 commit comments

Comments
 (0)