Skip to content
This repository was archived by the owner on Nov 25, 2022. It is now read-only.

Commit e6ed23a

Browse files
Merge branch 'master' into feature/types
2 parents ef5d6de + d954e18 commit e6ed23a

File tree

12 files changed

+227
-29
lines changed

12 files changed

+227
-29
lines changed

.github/workflows/metrics.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Aggregit
2+
3+
on:
4+
schedule:
5+
- cron: "0 0 * * *"
6+
7+
jobs:
8+
recordMetrics:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: michaeljolley/aggregit@v1
12+
with:
13+
githubToken: ${{ secrets.GITHUB_TOKEN }}
14+
project_id: ${{ secrets.project_id }}
15+
private_key: ${{ secrets.private_key }}
16+
client_email: ${{ secrets.client_email }}
17+
firebaseDbUrl: ${{ secrets.firebaseDbUrl }}

README.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ The `opentok-react` library comprises of:
163163
| apiKey | String | Yes | TokBox API Key
164164
| sessionId | String | Yes | TokBox Session ID
165165
| token | String | Yes | TokBox token
166+
| options | Object | No | TokBox options [options](https://tokbox.com/developer/sdks/js/reference/OT.html#initSession)
166167
| eventHandlers | Object<Function> | No | Event handlers passed into [session.on](https://tokbox.com/developer/sdks/js/reference/Session.html#on)
167168
| onConnect | Function() | No | Invoked when [session.connect](https://tokbox.com/developer/sdks/js/reference/Session.html#connect) successfully completes
168169
| onError | Function(err) | No | Invoked when [session.connect](https://tokbox.com/developer/sdks/js/reference/Session.html#connect) fails
@@ -250,6 +251,41 @@ However, for convenience the `OTPublisher` does watch for changes on a few keys
250251

251252
There are plans to support more Publisher properties but for now you will have to call `getPublisher()` to retrieve the Publisher instance and make the necessary changes yourself.
252253

254+
You can also get access to the `publisher` object by calling the `getPublisher` method using a [Ref](https://reactjs.org/docs/refs-and-the-dom.html). For example:
255+
```js
256+
class MyApp extends React.Component {
257+
constructor(props) {
258+
super(props);
259+
this.state = {
260+
publisher: null,
261+
};
262+
this.otPublisher = React.createRef();
263+
}
264+
265+
componentDidMount() {
266+
this.getPublisher();
267+
}
268+
269+
getPublisher() {
270+
if (this.otPublisher) {
271+
this.setState({
272+
publisher: this.otPublisher.current.getPublisher(),
273+
});
274+
}
275+
}
276+
277+
render() {
278+
return (
279+
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
280+
<OTPublisher
281+
ref={this.otPublisher}
282+
/>
283+
</OTSession>
284+
);
285+
}
286+
}
287+
```
288+
253289
### OTStreams Component
254290

255291
| Prop | Type | Required | Description |
@@ -265,6 +301,9 @@ There are plans to support more Publisher properties but for now you will have t
265301
| session | [Session](https://tokbox.com/developer/sdks/js/reference/Session.html) | No | OpenTok Session instance. This is auto populated by wrapping `OTSubscriber` with `OTStreams`
266302
| stream | [Stream](https://tokbox.com/developer/sdks/js/reference/Stream.html) | No | OpenTok Stream instance. This is auto populated by wrapping `OTSubscriber` with `OTStreams`
267303
| properties | Object | No | Properties passed into `session.subscribe`
304+
| retry | Boolean | No | Set true to retry the subscribe process in case of failure (Default: false)
305+
| maxRetryAttempts | Number | No | Max retry attempts in case of subscribe failure (Default: 5)
306+
| retryAttemptTimeout | Number | No | Timeout value between every subscribe retry attempt, expressed in ms (Default: 1000ms)
268307
| eventHandlers | Object&lt;Function&gt; | No | Event handlers passed into `subscriber.on`
269308
| onSubscribe | Function() | No | Invoked when `session.subscribe` successfully completes
270309
| onError | Function(err) | No | Invoked when `session.subscribe` fails
@@ -337,6 +376,44 @@ However, for convenience the `OTSubscriber` does watch for changes on a few keys
337376

338377
There are plans to support more Subscriber properties but for now you will have to call `getSubscriber()` to retrieve the Subscriber instance and make the necessary changes yourself.
339378

379+
You can also get access to the `subscriber` object by calling the `getSubscriber` method using a Ref. For example:
380+
381+
```js
382+
class MyApp extends React.Component {
383+
constructor(props) {
384+
super(props);
385+
this.state = {
386+
subscriber: null,
387+
};
388+
this.otSubscriber = React.createRef();
389+
}
390+
391+
componentDidMount() {
392+
this.getSubscriber();
393+
}
394+
395+
getSubscriber() {
396+
if (this.otSubscriber) {
397+
this.setState({
398+
subscriber: this.otSubscriber.current.getSubscriber(),
399+
});
400+
}
401+
}
402+
403+
render() {
404+
return (
405+
<OTSession apiKey="your-api-key" sessionId="your-session-id" token="your-session-token">
406+
<OTStreams>
407+
<OTSubscriber
408+
ref={this.otSubscriber}
409+
/>
410+
</OTStreams>
411+
</OTSession>
412+
);
413+
}
414+
}
415+
```
416+
340417
### createSession Helper
341418

342419
The `createSession` helper has been provided to easily create a session and monitor the current list of subscriber streams.
@@ -439,4 +516,4 @@ The unit tests are automatically run on [Travis](https://travis-ci.org/opentok/o
439516
## About
440517

441518
Originally authored by [Aiham Hammami](https://github.com/aiham).
442-
Currently maintained by [TokBox Inc.](https://tokbox.com/).
519+
Currently maintained by OpenTok Engineers and community members. Please note that this is not officially supported by [TokBox](https://tokbox.com/).

example/components/Subscriber.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ export default class Subscriber extends Component {
3636
subscribeToVideo: this.state.video
3737
}}
3838
onError={this.onError}
39+
retry={true}
40+
maxRetryAttempts={3}
41+
retryAttemptTimeout={2000}
3942
/>
4043
<CheckBox
4144
label="Subscribe to Audio"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "opentok-react",
3-
"version": "0.9.0",
3+
"version": "0.10.0",
44
"description": "React components for OpenTok.js",
55
"main": "dist/index.js",
66
"scripts": {

src/.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
},
1515
rules: {
1616
'no-confusing-arrow': ['error', { allowParens: true }],
17-
'react/jsx-filename-extension': 'off'
17+
'react/jsx-filename-extension': 'off',
18+
'react/forbid-prop-types': ['error', { forbid: ['any', 'array'] }]
1819
}
1920
};

src/OTPublisher.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ export default class OTPublisher extends Component {
170170
}
171171

172172
render() {
173-
return <div ref={(node) => { this.node = node; }} />;
173+
const { className, style } = this.props;
174+
return <div className={className} style={style} ref={(node) => { this.node = node; }} />;
174175
}
175176
}
176177

@@ -184,6 +185,8 @@ OTPublisher.propTypes = {
184185
publish: PropTypes.func,
185186
unpublish: PropTypes.func,
186187
}),
188+
className: PropTypes.string,
189+
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
187190
properties: PropTypes.object, // eslint-disable-line react/forbid-prop-types
188191
eventHandlers: PropTypes.objectOf(PropTypes.func),
189192
onInit: PropTypes.func,
@@ -193,6 +196,8 @@ OTPublisher.propTypes = {
193196

194197
OTPublisher.defaultProps = {
195198
session: null,
199+
className: '',
200+
style: {},
196201
properties: {},
197202
eventHandlers: null,
198203
onInit: null,

src/OTSession.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export default class OTSession extends Component {
4444
onStreamsUpdated: (streams) => { this.setState({ streams }); },
4545
onConnect: this.props.onConnect,
4646
onError: this.props.onError,
47+
options: this.props.options,
4748
});
4849

4950
if (
@@ -85,12 +86,14 @@ OTSession.propTypes = {
8586
eventHandlers: PropTypes.objectOf(PropTypes.func),
8687
onConnect: PropTypes.func,
8788
onError: PropTypes.func,
89+
options: PropTypes.object,
8890
};
8991

9092
OTSession.defaultProps = {
9193
eventHandlers: null,
9294
onConnect: null,
9395
onError: null,
96+
options: {},
9497
};
9598

9699
OTSession.childContextTypes = {

src/OTSubscriber.js

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ export default class OTSubscriber extends Component {
1010
subscriber: null,
1111
stream: props.stream || context.stream || null,
1212
session: props.session || context.session || null,
13+
currentRetryAttempt: 0,
1314
};
15+
this.maxRetryAttempts = props.maxRetryAttempts || 5;
16+
this.retryAttemptTimeout = props.retryAttemptTimeout || 1000;
1417
}
1518

1619
componentDidMount() {
@@ -59,41 +62,59 @@ export default class OTSubscriber extends Component {
5962
const { subscriberId } = this;
6063

6164
const subscriber = this.state.session.subscribe(
62-
this.state.stream,
63-
container,
64-
this.props.properties,
65-
(err) => {
66-
if (subscriberId !== this.subscriberId) {
67-
// Either this subscriber has been recreated or the
68-
// component unmounted so don't invoke any callbacks
69-
return;
70-
}
71-
if (err && typeof this.props.onError === 'function') {
72-
this.props.onError(err);
73-
} else if (!err && typeof this.props.onSubscribe === 'function') {
74-
this.props.onSubscribe();
75-
}
76-
},
77-
);
65+
this.state.stream,
66+
container,
67+
this.props.properties,
68+
(err) => {
69+
if (subscriberId !== this.subscriberId) {
70+
// Either this subscriber has been recreated or the
71+
// component unmounted so don't invoke any callbacks
72+
return;
73+
}
74+
if (err &&
75+
this.props.retry &&
76+
this.state.currentRetryAttempt < (this.maxRetryAttempts - 1)) {
77+
// Error during subscribe function
78+
this.handleRetrySubscriber();
79+
// If there is a retry action, do we want to execute the onError props function?
80+
// return;
81+
}
82+
if (err && typeof this.props.onError === 'function') {
83+
this.props.onError(err);
84+
} else if (!err && typeof this.props.onSubscribe === 'function') {
85+
this.props.onSubscribe();
86+
}
87+
},
88+
);
7889

7990
if (
80-
this.props.eventHandlers &&
81-
typeof this.props.eventHandlers === 'object'
82-
) {
91+
this.props.eventHandlers &&
92+
typeof this.props.eventHandlers === 'object'
93+
) {
8394
subscriber.on(this.props.eventHandlers);
8495
}
8596

8697
this.setState({ subscriber });
8798
}
8899

100+
handleRetrySubscriber() {
101+
setTimeout(() => {
102+
this.setState(state => ({
103+
currentRetryAttempt: state.currentRetryAttempt + 1,
104+
subscriber: null,
105+
}));
106+
this.createSubscriber();
107+
}, this.retryAttemptTimeout);
108+
}
109+
89110
destroySubscriber(session = this.props.session) {
90111
delete this.subscriberId;
91112

92113
if (this.state.subscriber) {
93114
if (
94-
this.props.eventHandlers &&
95-
typeof this.props.eventHandlers === 'object'
96-
) {
115+
this.props.eventHandlers &&
116+
typeof this.props.eventHandlers === 'object'
117+
) {
97118
this.state.subscriber.once('destroyed', () => {
98119
this.state.subscriber.off(this.props.eventHandlers);
99120
});
@@ -106,7 +127,8 @@ export default class OTSubscriber extends Component {
106127
}
107128

108129
render() {
109-
return <div ref={(node) => { this.node = node; }} />;
130+
const { className, style } = this.props;
131+
return <div className={className} style={style} ref={(node) => { this.node = node; }} />;
110132
}
111133
}
112134

@@ -118,7 +140,12 @@ OTSubscriber.propTypes = {
118140
subscribe: PropTypes.func,
119141
unsubscribe: PropTypes.func,
120142
}),
143+
className: PropTypes.string,
144+
style: PropTypes.object, // eslint-disable-line react/forbid-prop-types
121145
properties: PropTypes.object, // eslint-disable-line react/forbid-prop-types
146+
retry: PropTypes.bool,
147+
maxRetryAttempts: PropTypes.number,
148+
retryAttemptTimeout: PropTypes.number,
122149
eventHandlers: PropTypes.objectOf(PropTypes.func),
123150
onSubscribe: PropTypes.func,
124151
onError: PropTypes.func,
@@ -127,7 +154,12 @@ OTSubscriber.propTypes = {
127154
OTSubscriber.defaultProps = {
128155
stream: null,
129156
session: null,
157+
className: '',
158+
style: {},
130159
properties: {},
160+
retry: false,
161+
maxRetryAttempts: 5,
162+
retryAttemptTimeout: 1000,
131163
eventHandlers: null,
132164
onSubscribe: null,
133165
onError: null,

src/createSession.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export default function createSession({
55
onStreamsUpdated,
66
onConnect,
77
onError,
8+
options,
89
} = {}) {
910
if (!apiKey) {
1011
throw new Error('Missing apiKey');
@@ -41,7 +42,7 @@ export default function createSession({
4142
streamDestroyed: onStreamDestroyed,
4243
};
4344

44-
let session = OT.initSession(apiKey, sessionId);
45+
let session = OT.initSession(apiKey, sessionId, options);
4546
session.on(eventHandlers);
4647
session.connect(token, (err) => {
4748
if (!session) {

test/OTPublisher.spec.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,32 @@ describe('OTPublisher', () => {
3838
});
3939
});
4040

41+
describe('with className prop', () => {
42+
let wrapper;
43+
44+
beforeEach(() => {
45+
wrapper = mount(<OTPublisher className="myclass" />);
46+
});
47+
48+
it('should render class on wrapper element', () => {
49+
const divWrapper = wrapper.render().find('div.myclass');
50+
expect(divWrapper.length).toBe(1);
51+
});
52+
});
53+
54+
describe('with style prop', () => {
55+
let wrapper;
56+
const style = { color: 'red' };
57+
58+
beforeEach(() => {
59+
wrapper = mount(<OTPublisher style={style} />);
60+
});
61+
62+
it('should render style attribute on wrapper element', () => {
63+
expect(wrapper.containsMatchingElement(<div style={style} />)).toEqual(true);
64+
});
65+
});
66+
4167
describe('with session prop', () => {
4268
let session;
4369

0 commit comments

Comments
 (0)