├── .gitignore
├── .npmignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── UPGRADING.md
├── package-lock.json
├── package.json
├── prettier.config.js
├── src
├── components
│ └── SipProvider
│ │ └── index.ts
├── index.ts
└── lib
│ ├── dummyLogger.ts
│ ├── enums.ts
│ └── types.ts
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .git*
3 | .travis.yml
4 | prettier.config.js
5 | tsconfig.json
6 | tslint.json
7 | src
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '8'
4 | - '10'
5 | cache:
6 | directories:
7 | - node_modules
8 | script:
9 | - npm run lint
10 | - npm run build
11 | env:
12 | global:
13 | - COMMIT=${TRAVIS_COMMIT::8}
14 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # `react-sip` changelog
2 |
3 | ## [0.8.1](https://github.com/callthemonline/react-sip/tree/0.8.1) (2018-10-20)
4 |
5 | [All Commits](https://github.com/callthemonline/react-sip/compare/0.8.0...0.8.1)
6 |
7 | - Upgrade dependencies including JsSIP 3.2.15 ([3564b47b](https://github.com/callthemonline/react-sip/commit/3564b47ba0215d9ef7fc4a542e8a66bf80ec5981))
8 | - Fix TypeScript types in SipProvider ([a26ab823](https://github.com/callthemonline/react-sip/commit/a26ab823367eda93123422fdd9526aab574ac836))
9 |
10 | ## [0.8.0](https://github.com/callthemonline/react-sip/tree/0.8.0) (2018-08-04)
11 |
12 | [All Commits](https://github.com/callthemonline/react-sip/compare/0.7.0...0.8.0)
13 |
14 | - Guard against multiple SipProviders ([#22](https://github.com/callthemonline/react-sip/pull/22))
15 | - Switch to TypeScript, TSLint and Prettier ([#21](https://github.com/callthemonline/react-sip/pull/21))
16 |
17 | ## [0.7.0](https://github.com/callthemonline/react-sip/tree/0.7.0) (2018-04-19)
18 |
19 | [All Commits](https://github.com/callthemonline/react-sip/compare/0.6.0...0.7.0)
20 |
21 | - Add pathname prop ([#18](https://github.com/callthemonline/react-sip/pull/18))
22 |
23 | ## [0.6.0](https://github.com/callthemonline/react-sip/tree/0.6.0) (2018-03-24)
24 |
25 | [All Commits](https://github.com/callthemonline/react-sip/compare/0.5.0...0.6.0)
26 |
27 | - Allow force restart ICE ([#17](https://github.com/callthemonline/react-sip/pull/17))
28 |
29 | ## [0.5.0](https://github.com/callthemonline/react-sip/tree/0.5.0) (2017-12-02)
30 |
31 | [All Commits](https://github.com/callthemonline/react-sip/compare/0.4.0...0.5.0)
32 |
33 | - Refactor `` component by changing the shape of its context and by making it possible to modify all props on fly
34 | - Expose `sipType`, `callType`, `extraHeadersType` and `iceServersType` `PropTypes`
35 | - Improve docs in `README`
36 | - add `LICENSE,`CHANGELOG`and`UPGRADING`
37 | - Automatically publish to npm from travis
38 |
39 | See [UPGRADING.md](./UPGRADING.md#04--05) for how to upgrade from 0.4
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017-present Alexander Kachkaev and Denis Nikulin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React SIP
2 |
3 | [](https://github.com/callthemonline/react-sip/blob/master/LICENSE)
4 | [](https://www.npmjs.com/package/react-sip)
5 | [](https://www.npmjs.com/package/react-sip)
6 | [](https://travis-ci.org/callthemonline/react-sip)
7 |
8 | React wrapper for [jssip](https://github.com/versatica/JsSIP).
9 |
10 | ## Installation
11 |
12 | ```bash
13 | npm install react-sip
14 | ```
15 |
16 | There is no need to install `jssip` as it is a dependency of `react-sip`.
17 |
18 | ## Usage
19 |
20 | ```js
21 | import { SipProvider } from 'react-sip';
22 | import App from './components/App';
23 |
24 | ReactDOM.render(
25 |
45 |
46 |
47 | document.getElementById('root'),
48 | );
49 | ```
50 |
51 | Child components get access to this context:
52 |
53 | ```js
54 | {
55 | sip: sipType,
56 | call: callType,
57 |
58 | registerSip: PropTypes.func,
59 | unregisterSip: PropTypes.func,
60 |
61 | answerCall: PropTypes.func,
62 | startCall: PropTypes.func,
63 | stopCall: PropTypes.func,
64 | }
65 | ```
66 |
67 | See [lib/types.ts](./src/lib/types.ts) for technical details of what `sipType` and `callType` are.
68 | An overview is given below:
69 |
70 | ### sip
71 |
72 | `sip.status` represents SIP connection status and equals to one of these values:
73 |
74 | - `'sipStatus/DISCONNECTED'` when `host`, `port` or `user` is not defined
75 | - `'sipStatus/CONNECTING'`
76 | - `'sipStatus/CONNECTED'`
77 | - `'sipStatus/REGISTERED'` after calling `registerSip` or after `'sipStatus/CONNECTED'` when `autoRegister` is true
78 | - `'sipStatus/ERROR'` in case of configuration, connection or registration problems
79 |
80 | `sip.errorType`:
81 |
82 | - `null` when `sip.status` is not `'sipStatus/ERROR'`
83 | - `'sipErrorType/CONFIGURATION'`
84 | - `'sipErrorType/CONNECTION'`
85 | - `'sipErrorType/REGISTRATION'`
86 |
87 | `sip.host`, `sip.port`, `sip.user`, `...` – ``’s props (to make them easy to be displayed in the UI).
88 |
89 | ### call
90 |
91 | `call.id` is a unique session id of the actual established voice call; `undefined` between calls
92 |
93 | `call.status` represents the status of the call:
94 |
95 | - `'callStatus/IDLE'` between calls (even when disconnected)
96 | - `'callStatus/STARTING'` active incoming or outgoing call request
97 | - `'callStatus/ACTIVE'` during ongoing call
98 | - `'callStatus/STOPPING'` during call cancelation request
99 |
100 | `call.direction` indicates the direction of the ongoing call:
101 |
102 | - `null` between calls
103 | - `'callDirection/INCOMING'`
104 | - `'callDirection/OUTGOING'`
105 |
106 | `call.counterpart` represents the call _destination_ in case of outgoing call and _caller_ for
107 | incoming calls.
108 | The format depends on the configuration of the SIP server (e.g. `"bob" <+441234567890@sip.example.com>`, `+441234567890@sip.example.com` or `bob@sip.example.com`).
109 |
110 | ### methods
111 |
112 | When `autoRegister` is set to `false`, you can call `sipRegister()` and `sipUnregister()` manually for advanced registration scenarios.
113 |
114 | To make calls, simply use these functions:
115 |
116 | - `answerCall()`
117 | - `startCall(destination)`
118 | - `stopCall()`
119 |
120 | The value for `destination` argument equals to the target SIP user without the host part (e.g. `+441234567890` or `bob`).
121 | The omitted host part is equal to host you’ve defined in `SipProvider` props (e.g. `sip.example.com`).
122 |
123 | ---
124 |
125 | The values for `sip.status`, `sip.errorType`, `call.status` and `call.direction` can be imported as constants to make typos easier to detect:
126 |
127 | ```js
128 | import {
129 | SIP_STATUS_DISCONNECTED,
130 | //SIP_STATUS_...,
131 | CALL_STATUS_IDLE,
132 | //CALL_STATUS_...,
133 | SIP_ERROR_TYPE_CONFIGURATION,
134 | //SIP_ERROR_TYPE_...,
135 | CALL_DIRECTION_INCOMING,
136 | CALL_DIRECTION_OUTGOING,
137 | } from "react-sip";
138 | ```
139 |
140 | Custom PropTypes types are also provided by the library:
141 |
142 | ```js
143 | import { callType, extraHeadersType, iceServersType, sipType } from "react-sip";
144 | ```
145 |
--------------------------------------------------------------------------------
/UPGRADING.md:
--------------------------------------------------------------------------------
1 | # Upgrading `react-sip`
2 |
3 | ## 0.4 → 0.5
4 |
5 | There are several breaking changes in `` due to its refactoring.
6 | The component now supports prop updates, such as new host, port, user and password already after the component has been initialised.
7 | It is now not necessary to know the connection details in advance and so they can be defined lazily.
8 | A change in connection details creates a new instance of `JsSIP` and the old object (if any) is stopped.
9 |
10 | Things to take into account while refactoring:
11 |
12 | - The shape of context has changed, so your child components that consume ``’s info or trigger callbacks require revision.
13 | - ``’s props are now passed down the tree as context to make UI easier to implement. If you're passing them to child components yourself, you can now remove this custom code in favour of what `` puts into context.
14 | - When you specify `contextTypes` in your child components, you can now `import { sipType, callType } from 'react-sip'` to make your code shorter (see [lib/types.js](./src/lib/types.js)).
15 | - ``’s `port` prop type has changed from `string` to `number`.
16 | - previously undocumented experimental `uri` prop was removed
17 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-sip",
3 | "version": "0.8.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/prop-types": {
8 | "version": "15.5.6",
9 | "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.5.6.tgz",
10 | "integrity": "sha512-ZBFR7TROLVzCkswA3Fmqq+IIJt62/T7aY/Dmz+QkU7CaW2QFqAitCE8Ups7IzmGhcN1YWMBT4Qcoc07jU9hOJQ==",
11 | "dev": true
12 | },
13 | "@types/react": {
14 | "version": "16.4.18",
15 | "resolved": "https://registry.npmjs.org/@types/react/-/react-16.4.18.tgz",
16 | "integrity": "sha512-eFzJKEg6pdeaukVLVZ8Xb79CTl/ysX+ExmOfAAqcFlCCK5TgFDD9kWR0S18sglQ3EmM8U+80enjUqbfnUyqpdA==",
17 | "dev": true,
18 | "requires": {
19 | "@types/prop-types": "*",
20 | "csstype": "^2.2.0"
21 | }
22 | },
23 | "ansi-regex": {
24 | "version": "2.1.1",
25 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
26 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
27 | "dev": true
28 | },
29 | "ansi-styles": {
30 | "version": "2.2.1",
31 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
32 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
33 | "dev": true
34 | },
35 | "argparse": {
36 | "version": "1.0.10",
37 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
38 | "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
39 | "dev": true,
40 | "requires": {
41 | "sprintf-js": "~1.0.2"
42 | }
43 | },
44 | "babel-code-frame": {
45 | "version": "6.26.0",
46 | "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
47 | "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
48 | "dev": true,
49 | "requires": {
50 | "chalk": "^1.1.3",
51 | "esutils": "^2.0.2",
52 | "js-tokens": "^3.0.2"
53 | },
54 | "dependencies": {
55 | "chalk": {
56 | "version": "1.1.3",
57 | "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
58 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
59 | "dev": true,
60 | "requires": {
61 | "ansi-styles": "^2.2.1",
62 | "escape-string-regexp": "^1.0.2",
63 | "has-ansi": "^2.0.0",
64 | "strip-ansi": "^3.0.0",
65 | "supports-color": "^2.0.0"
66 | }
67 | },
68 | "js-tokens": {
69 | "version": "3.0.2",
70 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
71 | "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
72 | "dev": true
73 | }
74 | }
75 | },
76 | "balanced-match": {
77 | "version": "1.0.0",
78 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
79 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
80 | "dev": true
81 | },
82 | "brace-expansion": {
83 | "version": "1.1.11",
84 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
85 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
86 | "dev": true,
87 | "requires": {
88 | "balanced-match": "^1.0.0",
89 | "concat-map": "0.0.1"
90 | }
91 | },
92 | "builtin-modules": {
93 | "version": "1.1.1",
94 | "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
95 | "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
96 | "dev": true
97 | },
98 | "chalk": {
99 | "version": "2.4.1",
100 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
101 | "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
102 | "dev": true,
103 | "requires": {
104 | "ansi-styles": "^3.2.1",
105 | "escape-string-regexp": "^1.0.5",
106 | "supports-color": "^5.3.0"
107 | },
108 | "dependencies": {
109 | "ansi-styles": {
110 | "version": "3.2.1",
111 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
112 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
113 | "dev": true,
114 | "requires": {
115 | "color-convert": "^1.9.0"
116 | }
117 | },
118 | "supports-color": {
119 | "version": "5.5.0",
120 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
121 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
122 | "dev": true,
123 | "requires": {
124 | "has-flag": "^3.0.0"
125 | }
126 | }
127 | }
128 | },
129 | "color-convert": {
130 | "version": "1.9.3",
131 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
132 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
133 | "dev": true,
134 | "requires": {
135 | "color-name": "1.1.3"
136 | }
137 | },
138 | "color-name": {
139 | "version": "1.1.3",
140 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
141 | "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
142 | "dev": true
143 | },
144 | "commander": {
145 | "version": "2.19.0",
146 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
147 | "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==",
148 | "dev": true
149 | },
150 | "concat-map": {
151 | "version": "0.0.1",
152 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
153 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
154 | "dev": true
155 | },
156 | "cross-spawn": {
157 | "version": "5.1.0",
158 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
159 | "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
160 | "dev": true,
161 | "requires": {
162 | "lru-cache": "^4.0.1",
163 | "shebang-command": "^1.2.0",
164 | "which": "^1.2.9"
165 | }
166 | },
167 | "csstype": {
168 | "version": "2.5.7",
169 | "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.5.7.tgz",
170 | "integrity": "sha512-Nt5VDyOTIIV4/nRFswoCKps1R5CD1hkiyjBE9/thNaNZILLEviVw9yWQw15+O+CpNjQKB/uvdcxFFOrSflY3Yw==",
171 | "dev": true
172 | },
173 | "debug": {
174 | "version": "4.1.0",
175 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
176 | "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
177 | "requires": {
178 | "ms": "^2.1.1"
179 | }
180 | },
181 | "diff": {
182 | "version": "3.5.0",
183 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
184 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
185 | "dev": true
186 | },
187 | "escape-string-regexp": {
188 | "version": "1.0.5",
189 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
190 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
191 | "dev": true
192 | },
193 | "esprima": {
194 | "version": "4.0.1",
195 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
196 | "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
197 | "dev": true
198 | },
199 | "esutils": {
200 | "version": "2.0.2",
201 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
202 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
203 | "dev": true
204 | },
205 | "events": {
206 | "version": "3.0.0",
207 | "resolved": "https://registry.npmjs.org/events/-/events-3.0.0.tgz",
208 | "integrity": "sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA=="
209 | },
210 | "execa": {
211 | "version": "0.6.3",
212 | "resolved": "https://registry.npmjs.org/execa/-/execa-0.6.3.tgz",
213 | "integrity": "sha1-V7aaWU8IF1nGnlNw8NF7nLEWWP4=",
214 | "dev": true,
215 | "requires": {
216 | "cross-spawn": "^5.0.1",
217 | "get-stream": "^3.0.0",
218 | "is-stream": "^1.1.0",
219 | "npm-run-path": "^2.0.0",
220 | "p-finally": "^1.0.0",
221 | "signal-exit": "^3.0.0",
222 | "strip-eof": "^1.0.0"
223 | }
224 | },
225 | "fs.realpath": {
226 | "version": "1.0.0",
227 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
228 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
229 | "dev": true
230 | },
231 | "get-stream": {
232 | "version": "3.0.0",
233 | "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
234 | "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
235 | "dev": true
236 | },
237 | "glob": {
238 | "version": "7.1.3",
239 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
240 | "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
241 | "dev": true,
242 | "requires": {
243 | "fs.realpath": "^1.0.0",
244 | "inflight": "^1.0.4",
245 | "inherits": "2",
246 | "minimatch": "^3.0.4",
247 | "once": "^1.3.0",
248 | "path-is-absolute": "^1.0.0"
249 | }
250 | },
251 | "has-ansi": {
252 | "version": "2.0.0",
253 | "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
254 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
255 | "dev": true,
256 | "requires": {
257 | "ansi-regex": "^2.0.0"
258 | }
259 | },
260 | "has-flag": {
261 | "version": "3.0.0",
262 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
263 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
264 | "dev": true
265 | },
266 | "inflight": {
267 | "version": "1.0.6",
268 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
269 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
270 | "dev": true,
271 | "requires": {
272 | "once": "^1.3.0",
273 | "wrappy": "1"
274 | }
275 | },
276 | "inherits": {
277 | "version": "2.0.3",
278 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
279 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
280 | "dev": true
281 | },
282 | "is-stream": {
283 | "version": "1.1.0",
284 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
285 | "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
286 | "dev": true
287 | },
288 | "isexe": {
289 | "version": "2.0.0",
290 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
291 | "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
292 | "dev": true
293 | },
294 | "js-tokens": {
295 | "version": "4.0.0",
296 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
297 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
298 | },
299 | "js-yaml": {
300 | "version": "3.12.0",
301 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
302 | "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
303 | "dev": true,
304 | "requires": {
305 | "argparse": "^1.0.7",
306 | "esprima": "^4.0.0"
307 | }
308 | },
309 | "jssip": {
310 | "version": "3.2.15",
311 | "resolved": "https://registry.npmjs.org/jssip/-/jssip-3.2.15.tgz",
312 | "integrity": "sha512-kpKekINV3AObe6UFSTYiM5PfL5xKWTstUnG3S65vy6/GRlKhWk6XdivaH0X22RyAFPQuxskfpy99V/4MLhEdWg==",
313 | "requires": {
314 | "debug": "^4.1.0",
315 | "events": "^3.0.0",
316 | "sdp-transform": "^2.4.1"
317 | }
318 | },
319 | "loose-envify": {
320 | "version": "1.4.0",
321 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
322 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
323 | "requires": {
324 | "js-tokens": "^3.0.0 || ^4.0.0"
325 | }
326 | },
327 | "lru-cache": {
328 | "version": "4.1.3",
329 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz",
330 | "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==",
331 | "dev": true,
332 | "requires": {
333 | "pseudomap": "^1.0.2",
334 | "yallist": "^2.1.2"
335 | }
336 | },
337 | "minimatch": {
338 | "version": "3.0.4",
339 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
340 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
341 | "dev": true,
342 | "requires": {
343 | "brace-expansion": "^1.1.7"
344 | }
345 | },
346 | "ms": {
347 | "version": "2.1.1",
348 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
349 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
350 | },
351 | "npm-run-path": {
352 | "version": "2.0.2",
353 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
354 | "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
355 | "dev": true,
356 | "requires": {
357 | "path-key": "^2.0.0"
358 | }
359 | },
360 | "object-assign": {
361 | "version": "4.1.1",
362 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
363 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
364 | },
365 | "once": {
366 | "version": "1.4.0",
367 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
368 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
369 | "dev": true,
370 | "requires": {
371 | "wrappy": "1"
372 | }
373 | },
374 | "p-finally": {
375 | "version": "1.0.0",
376 | "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
377 | "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
378 | "dev": true
379 | },
380 | "path-is-absolute": {
381 | "version": "1.0.1",
382 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
383 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
384 | "dev": true
385 | },
386 | "path-key": {
387 | "version": "2.0.1",
388 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
389 | "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
390 | "dev": true
391 | },
392 | "path-parse": {
393 | "version": "1.0.6",
394 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
395 | "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
396 | "dev": true
397 | },
398 | "prettier": {
399 | "version": "1.14.3",
400 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.14.3.tgz",
401 | "integrity": "sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==",
402 | "dev": true
403 | },
404 | "prettier-check": {
405 | "version": "2.0.0",
406 | "resolved": "https://registry.npmjs.org/prettier-check/-/prettier-check-2.0.0.tgz",
407 | "integrity": "sha512-HZG53XQTJ9Cyi5hi1VFVVFxdlhITJybpZAch3ib9KqI05VUxV+F5Hip0GhSWRItrlDzVyqjSoDQ9KqIn7AHYyw==",
408 | "dev": true,
409 | "requires": {
410 | "execa": "^0.6.0"
411 | }
412 | },
413 | "prop-types": {
414 | "version": "15.6.2",
415 | "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
416 | "integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
417 | "requires": {
418 | "loose-envify": "^1.3.1",
419 | "object-assign": "^4.1.1"
420 | }
421 | },
422 | "pseudomap": {
423 | "version": "1.0.2",
424 | "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
425 | "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
426 | "dev": true
427 | },
428 | "react": {
429 | "version": "16.5.2",
430 | "resolved": "https://registry.npmjs.org/react/-/react-16.5.2.tgz",
431 | "integrity": "sha512-FDCSVd3DjVTmbEAjUNX6FgfAmQ+ypJfHUsqUJOYNCBUp1h8lqmtC+0mXJ+JjsWx4KAVTkk1vKd1hLQPvEviSuw==",
432 | "dev": true,
433 | "requires": {
434 | "loose-envify": "^1.1.0",
435 | "object-assign": "^4.1.1",
436 | "prop-types": "^15.6.2",
437 | "schedule": "^0.5.0"
438 | }
439 | },
440 | "resolve": {
441 | "version": "1.8.1",
442 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
443 | "integrity": "sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA==",
444 | "dev": true,
445 | "requires": {
446 | "path-parse": "^1.0.5"
447 | }
448 | },
449 | "rimraf": {
450 | "version": "2.6.2",
451 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
452 | "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
453 | "dev": true,
454 | "requires": {
455 | "glob": "^7.0.5"
456 | }
457 | },
458 | "schedule": {
459 | "version": "0.5.0",
460 | "resolved": "https://registry.npmjs.org/schedule/-/schedule-0.5.0.tgz",
461 | "integrity": "sha512-HUcJicG5Ou8xfR//c2rPT0lPIRR09vVvN81T9fqfVgBmhERUbDEQoYKjpBxbueJnCPpSu2ujXzOnRQt6x9o/jw==",
462 | "dev": true,
463 | "requires": {
464 | "object-assign": "^4.1.1"
465 | }
466 | },
467 | "sdp-transform": {
468 | "version": "2.4.1",
469 | "resolved": "https://registry.npmjs.org/sdp-transform/-/sdp-transform-2.4.1.tgz",
470 | "integrity": "sha512-dCReSJ6NtCW6goKkKBEHcIknBS1pKejnMw+S3p3jl0PALPFrbjbreCyQ+CABtFxl2GXD2/05+C2q2gZP23dUWQ=="
471 | },
472 | "semver": {
473 | "version": "5.6.0",
474 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
475 | "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
476 | "dev": true
477 | },
478 | "shebang-command": {
479 | "version": "1.2.0",
480 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
481 | "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
482 | "dev": true,
483 | "requires": {
484 | "shebang-regex": "^1.0.0"
485 | }
486 | },
487 | "shebang-regex": {
488 | "version": "1.0.0",
489 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
490 | "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
491 | "dev": true
492 | },
493 | "signal-exit": {
494 | "version": "3.0.2",
495 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
496 | "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
497 | "dev": true
498 | },
499 | "sprintf-js": {
500 | "version": "1.0.3",
501 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
502 | "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
503 | "dev": true
504 | },
505 | "strip-ansi": {
506 | "version": "3.0.1",
507 | "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
508 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
509 | "dev": true,
510 | "requires": {
511 | "ansi-regex": "^2.0.0"
512 | }
513 | },
514 | "strip-eof": {
515 | "version": "1.0.0",
516 | "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
517 | "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
518 | "dev": true
519 | },
520 | "supports-color": {
521 | "version": "2.0.0",
522 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
523 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
524 | "dev": true
525 | },
526 | "tslib": {
527 | "version": "1.9.3",
528 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
529 | "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
530 | "dev": true
531 | },
532 | "tslint": {
533 | "version": "5.11.0",
534 | "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.11.0.tgz",
535 | "integrity": "sha1-mPMMAurjzecAYgHkwzywi0hYHu0=",
536 | "dev": true,
537 | "requires": {
538 | "babel-code-frame": "^6.22.0",
539 | "builtin-modules": "^1.1.1",
540 | "chalk": "^2.3.0",
541 | "commander": "^2.12.1",
542 | "diff": "^3.2.0",
543 | "glob": "^7.1.1",
544 | "js-yaml": "^3.7.0",
545 | "minimatch": "^3.0.4",
546 | "resolve": "^1.3.2",
547 | "semver": "^5.3.0",
548 | "tslib": "^1.8.0",
549 | "tsutils": "^2.27.2"
550 | }
551 | },
552 | "tslint-config-prettier": {
553 | "version": "1.15.0",
554 | "resolved": "https://registry.npmjs.org/tslint-config-prettier/-/tslint-config-prettier-1.15.0.tgz",
555 | "integrity": "sha512-06CgrHJxJmNYVgsmeMoa1KXzQRoOdvfkqnJth6XUkNeOz707qxN0WfxfhYwhL5kXHHbYJRby2bqAPKwThlZPhw==",
556 | "dev": true
557 | },
558 | "tsutils": {
559 | "version": "2.29.0",
560 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
561 | "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
562 | "dev": true,
563 | "requires": {
564 | "tslib": "^1.8.1"
565 | }
566 | },
567 | "typescript": {
568 | "version": "3.1.3",
569 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.3.tgz",
570 | "integrity": "sha512-+81MUSyX+BaSo+u2RbozuQk/UWx6hfG0a5gHu4ANEM4sU96XbuIyAB+rWBW1u70c6a5QuZfuYICn3s2UjuHUpA==",
571 | "dev": true
572 | },
573 | "which": {
574 | "version": "1.3.1",
575 | "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
576 | "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
577 | "dev": true,
578 | "requires": {
579 | "isexe": "^2.0.0"
580 | }
581 | },
582 | "wrappy": {
583 | "version": "1.0.2",
584 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
585 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
586 | "dev": true
587 | },
588 | "yallist": {
589 | "version": "2.1.2",
590 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
591 | "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
592 | "dev": true
593 | }
594 | }
595 | }
596 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-sip",
3 | "version": "0.8.1",
4 | "description": "React wrapper for jssip",
5 | "main": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "scripts": {
8 | "build": "rimraf dist && tsc --project .",
9 | "build:watch": "rimraf dist && tsc --project . --watch",
10 | "format": "prettier --write --ignore-path .gitignore \"{.,src/**}/{*.js,*.ts,*.md,*.json}\"",
11 | "lint": "tsc --project . --noEmit && tslint --project . && prettier-check --ignore-path .gitignore \"{.,src/**}/{*.js,*.ts,*.md,*.json}\"",
12 | "prepublish": "npm run build",
13 | "test": "echo \"Error: no test specified\" && exit 1"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+ssh://git@github.com/callthemonline/react-sip.git"
18 | },
19 | "keywords": [
20 | "react",
21 | "sip",
22 | "jssip",
23 | "webrtc"
24 | ],
25 | "author": "callthemonline (https://callthem.online/)",
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/callthemonline/react-sip/issues"
29 | },
30 | "homepage": "https://github.com/callthemonline/react-sip#readme",
31 | "dependencies": {
32 | "jssip": "^3.2.15",
33 | "prop-types": "^15.6.2"
34 | },
35 | "peerDependencies": {
36 | "react": ">=15.0"
37 | },
38 | "devDependencies": {
39 | "@types/prop-types": "^15.5.6",
40 | "@types/react": "^16.4.18",
41 | "prettier": "^1.14.3",
42 | "prettier-check": "^2.0.0",
43 | "react": "^16.5.2",
44 | "rimraf": "^2.6.2",
45 | "tslint": "^5.11.0",
46 | "tslint-config-prettier": "^1.15.0",
47 | "typescript": "^3.1.3"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: "always",
3 | trailingComma: "all",
4 | };
5 |
--------------------------------------------------------------------------------
/src/components/SipProvider/index.ts:
--------------------------------------------------------------------------------
1 | import * as JsSIP from "jssip";
2 | import * as PropTypes from "prop-types";
3 | import * as React from "react";
4 | import dummyLogger from "../../lib/dummyLogger";
5 | import {
6 | CALL_DIRECTION_INCOMING,
7 | CALL_DIRECTION_OUTGOING,
8 | CALL_STATUS_ACTIVE,
9 | CALL_STATUS_IDLE,
10 | CALL_STATUS_STARTING,
11 | CALL_STATUS_STOPPING,
12 | SIP_ERROR_TYPE_CONFIGURATION,
13 | SIP_ERROR_TYPE_CONNECTION,
14 | SIP_ERROR_TYPE_REGISTRATION,
15 | SIP_STATUS_CONNECTED,
16 | SIP_STATUS_CONNECTING,
17 | SIP_STATUS_DISCONNECTED,
18 | SIP_STATUS_ERROR,
19 | SIP_STATUS_REGISTERED,
20 | } from "../../lib/enums";
21 | import {
22 | CallDirection,
23 | CallStatus,
24 | SipErrorType,
25 | SipStatus,
26 | } from "../../lib/enums";
27 | import {
28 | callPropType,
29 | ExtraHeaders,
30 | extraHeadersPropType,
31 | IceServers,
32 | iceServersPropType,
33 | sipPropType,
34 | } from "../../lib/types";
35 |
36 | export default class SipProvider extends React.Component<
37 | {
38 | host: string;
39 | port: number;
40 | pathname: string;
41 | user: string;
42 | password: string;
43 | autoRegister: boolean;
44 | autoAnswer: boolean;
45 | iceRestart: boolean;
46 | sessionTimersExpires: number;
47 | extraHeaders: ExtraHeaders;
48 | iceServers: IceServers;
49 | debug: boolean;
50 | },
51 | {
52 | sipStatus: SipStatus;
53 | sipErrorType: SipErrorType | null;
54 | sipErrorMessage: string | null;
55 | callStatus: CallStatus;
56 | callDirection: CallDirection | null;
57 | callCounterpart: string | null;
58 | rtcSession;
59 | }
60 | > {
61 | public static childContextTypes = {
62 | sip: sipPropType,
63 | call: callPropType,
64 | registerSip: PropTypes.func,
65 | unregisterSip: PropTypes.func,
66 |
67 | answerCall: PropTypes.func,
68 | startCall: PropTypes.func,
69 | stopCall: PropTypes.func,
70 | };
71 |
72 | public static propTypes = {
73 | host: PropTypes.string,
74 | port: PropTypes.number,
75 | pathname: PropTypes.string,
76 | user: PropTypes.string,
77 | password: PropTypes.string,
78 | autoRegister: PropTypes.bool,
79 | autoAnswer: PropTypes.bool,
80 | iceRestart: PropTypes.bool,
81 | sessionTimersExpires: PropTypes.number,
82 | extraHeaders: extraHeadersPropType,
83 | iceServers: iceServersPropType,
84 | debug: PropTypes.bool,
85 |
86 | children: PropTypes.node,
87 | };
88 |
89 | public static defaultProps = {
90 | host: null,
91 | port: null,
92 | pathname: "",
93 | user: null,
94 | password: null,
95 | autoRegister: true,
96 | autoAnswer: false,
97 | iceRestart: false,
98 | sessionTimersExpires: 120,
99 | extraHeaders: { register: [], invite: [] },
100 | iceServers: [],
101 | debug: false,
102 |
103 | children: null,
104 | };
105 | private ua;
106 | private remoteAudio;
107 | private logger;
108 |
109 | constructor(props) {
110 | super(props);
111 |
112 | this.state = {
113 | sipStatus: SIP_STATUS_DISCONNECTED,
114 | sipErrorType: null,
115 | sipErrorMessage: null,
116 |
117 | rtcSession: null,
118 | // errorLog: [],
119 | callStatus: CALL_STATUS_IDLE,
120 | callDirection: null,
121 | callCounterpart: null,
122 | };
123 |
124 | this.ua = null;
125 | }
126 |
127 | public getChildContext() {
128 | return {
129 | sip: {
130 | ...this.props,
131 | status: this.state.sipStatus,
132 | errorType: this.state.sipErrorType,
133 | errorMessage: this.state.sipErrorMessage,
134 | },
135 | call: {
136 | id: "??",
137 | status: this.state.callStatus,
138 | direction: this.state.callDirection,
139 | counterpart: this.state.callCounterpart,
140 | },
141 | registerSip: this.registerSip,
142 | unregisterSip: this.unregisterSip,
143 |
144 | answerCall: this.answerCall,
145 | startCall: this.startCall,
146 | stopCall: this.stopCall,
147 | };
148 | }
149 |
150 | public componentDidMount() {
151 | if (window.document.getElementById("sip-provider-audio")) {
152 | throw new Error(
153 | `Creating two SipProviders in one application is forbidden. If that's not the case ` +
154 | `then check if you're using "sip-provider-audio" as id attribute for any existing ` +
155 | `element`,
156 | );
157 | }
158 |
159 | this.remoteAudio = window.document.createElement("audio");
160 | this.remoteAudio.id = "sip-provider-audio";
161 | window.document.body.appendChild(this.remoteAudio);
162 |
163 | this.reconfigureDebug();
164 | this.reinitializeJsSIP();
165 | }
166 |
167 | public componentDidUpdate(prevProps) {
168 | if (this.props.debug !== prevProps.debug) {
169 | this.reconfigureDebug();
170 | }
171 | if (
172 | this.props.host !== prevProps.host ||
173 | this.props.port !== prevProps.port ||
174 | this.props.pathname !== prevProps.pathname ||
175 | this.props.user !== prevProps.user ||
176 | this.props.password !== prevProps.password ||
177 | this.props.autoRegister !== prevProps.autoRegister
178 | ) {
179 | this.reinitializeJsSIP();
180 | }
181 | }
182 |
183 | public componentWillUnmount() {
184 | this.remoteAudio.parentNode.removeChild(this.remoteAudio);
185 | delete this.remoteAudio;
186 | if (this.ua) {
187 | this.ua.stop();
188 | this.ua = null;
189 | }
190 | }
191 |
192 | public registerSip = () => {
193 | if (this.props.autoRegister) {
194 | throw new Error(
195 | "Calling registerSip is not allowed when autoRegister === true",
196 | );
197 | }
198 | if (this.state.sipStatus !== SIP_STATUS_CONNECTED) {
199 | throw new Error(
200 | `Calling registerSip is not allowed when sip status is ${
201 | this.state.sipStatus
202 | } (expected ${SIP_STATUS_CONNECTED})`,
203 | );
204 | }
205 | return this.ua.register();
206 | };
207 |
208 | public unregisterSip = () => {
209 | if (this.props.autoRegister) {
210 | throw new Error(
211 | "Calling registerSip is not allowed when autoRegister === true",
212 | );
213 | }
214 | if (this.state.sipStatus !== SIP_STATUS_REGISTERED) {
215 | throw new Error(
216 | `Calling unregisterSip is not allowed when sip status is ${
217 | this.state.sipStatus
218 | } (expected ${SIP_STATUS_CONNECTED})`,
219 | );
220 | }
221 | return this.ua.unregister();
222 | };
223 |
224 | public answerCall = () => {
225 | if (
226 | this.state.callStatus !== CALL_STATUS_STARTING ||
227 | this.state.callDirection !== CALL_DIRECTION_INCOMING
228 | ) {
229 | throw new Error(
230 | `Calling answerCall() is not allowed when call status is ${
231 | this.state.callStatus
232 | } and call direction is ${
233 | this.state.callDirection
234 | } (expected ${CALL_STATUS_STARTING} and ${CALL_DIRECTION_INCOMING})`,
235 | );
236 | }
237 |
238 | this.state.rtcSession.answer({
239 | mediaConstraints: {
240 | audio: true,
241 | video: false,
242 | },
243 | pcConfig: {
244 | iceServers: this.props.iceServers,
245 | },
246 | });
247 | };
248 |
249 | public startCall = (destination) => {
250 | if (!destination) {
251 | throw new Error(`Destination must be defined (${destination} given)`);
252 | }
253 | if (
254 | this.state.sipStatus !== SIP_STATUS_CONNECTED &&
255 | this.state.sipStatus !== SIP_STATUS_REGISTERED
256 | ) {
257 | throw new Error(
258 | `Calling startCall() is not allowed when sip status is ${
259 | this.state.sipStatus
260 | } (expected ${SIP_STATUS_CONNECTED} or ${SIP_STATUS_REGISTERED})`,
261 | );
262 | }
263 |
264 | if (this.state.callStatus !== CALL_STATUS_IDLE) {
265 | throw new Error(
266 | `Calling startCall() is not allowed when call status is ${
267 | this.state.callStatus
268 | } (expected ${CALL_STATUS_IDLE})`,
269 | );
270 | }
271 |
272 | const { iceServers, sessionTimersExpires } = this.props;
273 | const extraHeaders = this.props.extraHeaders.invite;
274 |
275 | const options = {
276 | extraHeaders,
277 | mediaConstraints: { audio: true, video: false },
278 | rtcOfferConstraints: { iceRestart: this.props.iceRestart },
279 | pcConfig: {
280 | iceServers,
281 | },
282 | sessionTimersExpires,
283 | };
284 |
285 | this.ua.call(destination, options);
286 | this.setState({ callStatus: CALL_STATUS_STARTING });
287 | };
288 |
289 | public stopCall = () => {
290 | this.setState({ callStatus: CALL_STATUS_STOPPING });
291 | this.ua.terminateSessions();
292 | };
293 |
294 | public reconfigureDebug() {
295 | const { debug } = this.props;
296 |
297 | if (debug) {
298 | JsSIP.debug.enable("JsSIP:*");
299 | this.logger = console;
300 | } else {
301 | JsSIP.debug.disable("JsSIP:*");
302 | this.logger = dummyLogger;
303 | }
304 | }
305 |
306 | public reinitializeJsSIP() {
307 | if (this.ua) {
308 | this.ua.stop();
309 | this.ua = null;
310 | }
311 |
312 | const { host, port, pathname, user, password, autoRegister } = this.props;
313 |
314 | if (!host || !port || !user) {
315 | this.setState({
316 | sipStatus: SIP_STATUS_DISCONNECTED,
317 | sipErrorType: null,
318 | sipErrorMessage: null,
319 | });
320 | return;
321 | }
322 |
323 | try {
324 | const socket = new JsSIP.WebSocketInterface(
325 | `wss://${host}:${port}${pathname}`,
326 | );
327 | this.ua = new JsSIP.UA({
328 | uri: `sip:${user}@${host}`,
329 | password,
330 | sockets: [socket],
331 | register: autoRegister,
332 | });
333 | } catch (error) {
334 | this.logger.debug("Error", error.message, error);
335 | this.setState({
336 | sipStatus: SIP_STATUS_ERROR,
337 | sipErrorType: SIP_ERROR_TYPE_CONFIGURATION,
338 | sipErrorMessage: error.message,
339 | });
340 | return;
341 | }
342 |
343 | const { ua } = this;
344 | ua.on("connecting", () => {
345 | this.logger.debug('UA "connecting" event');
346 | if (this.ua !== ua) {
347 | return;
348 | }
349 | this.setState({
350 | sipStatus: SIP_STATUS_CONNECTING,
351 | sipErrorType: null,
352 | sipErrorMessage: null,
353 | });
354 | });
355 |
356 | ua.on("connected", () => {
357 | this.logger.debug('UA "connected" event');
358 | if (this.ua !== ua) {
359 | return;
360 | }
361 | this.setState({
362 | sipStatus: SIP_STATUS_CONNECTED,
363 | sipErrorType: null,
364 | sipErrorMessage: null,
365 | });
366 | });
367 |
368 | ua.on("disconnected", () => {
369 | this.logger.debug('UA "disconnected" event');
370 | if (this.ua !== ua) {
371 | return;
372 | }
373 | this.setState({
374 | sipStatus: SIP_STATUS_ERROR,
375 | sipErrorType: SIP_ERROR_TYPE_CONNECTION,
376 | sipErrorMessage: "disconnected",
377 | });
378 | });
379 |
380 | ua.on("registered", (data) => {
381 | this.logger.debug('UA "registered" event', data);
382 | if (this.ua !== ua) {
383 | return;
384 | }
385 | this.setState({
386 | sipStatus: SIP_STATUS_REGISTERED,
387 | callStatus: CALL_STATUS_IDLE,
388 | });
389 | });
390 |
391 | ua.on("unregistered", () => {
392 | this.logger.debug('UA "unregistered" event');
393 | if (this.ua !== ua) {
394 | return;
395 | }
396 | if (ua.isConnected()) {
397 | this.setState({
398 | sipStatus: SIP_STATUS_CONNECTED,
399 | callStatus: CALL_STATUS_IDLE,
400 | callDirection: null,
401 | });
402 | } else {
403 | this.setState({
404 | sipStatus: SIP_STATUS_DISCONNECTED,
405 | callStatus: CALL_STATUS_IDLE,
406 | callDirection: null,
407 | });
408 | }
409 | });
410 |
411 | ua.on("registrationFailed", (data) => {
412 | this.logger.debug('UA "registrationFailed" event');
413 | if (this.ua !== ua) {
414 | return;
415 | }
416 | this.setState({
417 | sipStatus: SIP_STATUS_ERROR,
418 | sipErrorType: SIP_ERROR_TYPE_REGISTRATION,
419 | sipErrorMessage: data,
420 | });
421 | });
422 |
423 | ua.on(
424 | "newRTCSession",
425 | ({ originator, session: rtcSession, request: rtcRequest }) => {
426 | if (!this || this.ua !== ua) {
427 | return;
428 | }
429 |
430 | // identify call direction
431 | if (originator === "local") {
432 | const foundUri = rtcRequest.to.toString();
433 | const delimiterPosition = foundUri.indexOf(";") || null;
434 | this.setState({
435 | callDirection: CALL_DIRECTION_OUTGOING,
436 | callStatus: CALL_STATUS_STARTING,
437 | callCounterpart:
438 | foundUri.substring(0, delimiterPosition) || foundUri,
439 | });
440 | } else if (originator === "remote") {
441 | const foundUri = rtcRequest.from.toString();
442 | const delimiterPosition = foundUri.indexOf(";") || null;
443 | this.setState({
444 | callDirection: CALL_DIRECTION_INCOMING,
445 | callStatus: CALL_STATUS_STARTING,
446 | callCounterpart:
447 | foundUri.substring(0, delimiterPosition) || foundUri,
448 | });
449 | }
450 |
451 | const { rtcSession: rtcSessionInState } = this.state;
452 |
453 | // Avoid if busy or other incoming
454 | if (rtcSessionInState) {
455 | this.logger.debug('incoming call replied with 486 "Busy Here"');
456 | rtcSession.terminate({
457 | status_code: 486,
458 | reason_phrase: "Busy Here",
459 | });
460 | return;
461 | }
462 |
463 | this.setState({ rtcSession });
464 | rtcSession.on("failed", () => {
465 | if (this.ua !== ua) {
466 | return;
467 | }
468 |
469 | this.setState({
470 | rtcSession: null,
471 | callStatus: CALL_STATUS_IDLE,
472 | callDirection: null,
473 | callCounterpart: null,
474 | });
475 | });
476 |
477 | rtcSession.on("ended", () => {
478 | if (this.ua !== ua) {
479 | return;
480 | }
481 |
482 | this.setState({
483 | rtcSession: null,
484 | callStatus: CALL_STATUS_IDLE,
485 | callDirection: null,
486 | callCounterpart: null,
487 | });
488 | });
489 |
490 | rtcSession.on("accepted", () => {
491 | if (this.ua !== ua) {
492 | return;
493 | }
494 |
495 | [
496 | this.remoteAudio.srcObject,
497 | ] = rtcSession.connection.getRemoteStreams();
498 | // const played = this.remoteAudio.play();
499 | const played = this.remoteAudio.play();
500 |
501 | if (typeof played !== "undefined") {
502 | played
503 | .catch(() => {
504 | /**/
505 | })
506 | .then(() => {
507 | setTimeout(() => {
508 | this.remoteAudio.play();
509 | }, 2000);
510 | });
511 | this.setState({ callStatus: CALL_STATUS_ACTIVE });
512 | return;
513 | }
514 |
515 | setTimeout(() => {
516 | this.remoteAudio.play();
517 | }, 2000);
518 |
519 | this.setState({ callStatus: CALL_STATUS_ACTIVE });
520 | });
521 |
522 | if (
523 | this.state.callDirection === CALL_DIRECTION_INCOMING &&
524 | this.props.autoAnswer
525 | ) {
526 | this.logger.log("Answer auto ON");
527 | this.answerCall();
528 | } else if (
529 | this.state.callDirection === CALL_DIRECTION_INCOMING &&
530 | !this.props.autoAnswer
531 | ) {
532 | this.logger.log("Answer auto OFF");
533 | } else if (this.state.callDirection === CALL_DIRECTION_OUTGOING) {
534 | this.logger.log("OUTGOING call");
535 | }
536 | },
537 | );
538 |
539 | const extraHeadersRegister = this.props.extraHeaders.register || [];
540 | if (extraHeadersRegister.length) {
541 | ua.registrator().setExtraHeaders(extraHeadersRegister);
542 | }
543 | ua.start();
544 | }
545 |
546 | public render() {
547 | return this.props.children;
548 | }
549 | }
550 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import SipProvider from "./components/SipProvider";
2 |
3 | export * from "./lib/enums";
4 | export * from "./lib/types";
5 |
6 | export { SipProvider };
7 |
--------------------------------------------------------------------------------
/src/lib/dummyLogger.ts:
--------------------------------------------------------------------------------
1 | const dummyLogger = {
2 | log: () => {
3 | /**/
4 | },
5 | error: () => {
6 | /**/
7 | },
8 | warn: () => {
9 | /**/
10 | },
11 | debug: () => {
12 | /**/
13 | },
14 | };
15 |
16 | export default dummyLogger;
17 |
--------------------------------------------------------------------------------
/src/lib/enums.ts:
--------------------------------------------------------------------------------
1 | export const SIP_STATUS_DISCONNECTED = "sipStatus/DISCONNECTED";
2 | export const SIP_STATUS_CONNECTING = "sipStatus/CONNECTING";
3 | export const SIP_STATUS_CONNECTED = "sipStatus/CONNECTED";
4 | export const SIP_STATUS_REGISTERED = "sipStatus/REGISTERED";
5 | export const SIP_STATUS_ERROR = "sipStatus/ERROR";
6 | export type SipStatus =
7 | | "sipStatus/DISCONNECTED"
8 | | "sipStatus/CONNECTING"
9 | | "sipStatus/CONNECTED"
10 | | "sipStatus/REGISTERED"
11 | | "sipStatus/ERROR";
12 |
13 | export const SIP_ERROR_TYPE_CONFIGURATION = "sipErrorType/CONFIGURATION";
14 | export const SIP_ERROR_TYPE_CONNECTION = "sipErrorType/CONNECTION";
15 | export const SIP_ERROR_TYPE_REGISTRATION = "sipErrorType/REGISTRATION";
16 | export type SipErrorType =
17 | | "sipErrorType/CONFIGURATION"
18 | | "sipErrorType/CONNECTION"
19 | | "sipErrorType/REGISTRATION";
20 |
21 | export const CALL_STATUS_IDLE = "callStatus/IDLE";
22 | export const CALL_STATUS_STARTING = "callStatus/STARTING";
23 | export const CALL_STATUS_ACTIVE = "callStatus/ACTIVE";
24 | export const CALL_STATUS_STOPPING = "callStatus/STOPPING";
25 | export type CallStatus =
26 | | "callStatus/IDLE"
27 | | "callStatus/STARTING"
28 | | "callStatus/ACTIVE"
29 | | "callStatus/STOPPING";
30 |
31 | export const CALL_DIRECTION_INCOMING = "callDirection/INCOMING";
32 | export const CALL_DIRECTION_OUTGOING = "callDirection/OUTGOING";
33 | export type CallDirection = "callDirection/INCOMING" | "callDirection/OUTGOING";
34 |
--------------------------------------------------------------------------------
/src/lib/types.ts:
--------------------------------------------------------------------------------
1 | import * as PropTypes from "prop-types";
2 |
3 | export interface ExtraHeaders {
4 | register?: string[];
5 | invite?: string[];
6 | }
7 | export const extraHeadersPropType = PropTypes.objectOf(
8 | PropTypes.arrayOf(PropTypes.string),
9 | );
10 |
11 | // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer
12 | export type IceServers = Array<{
13 | urls: string | string[];
14 | username?: string;
15 | credential?: string;
16 | credentialType?: string;
17 | password?: string;
18 | }>;
19 | export const iceServersPropType = PropTypes.arrayOf(PropTypes.object);
20 |
21 | export interface Sip {
22 | status?: string;
23 | errorType?: string;
24 | errorMessage?: string;
25 |
26 | host?: string;
27 | port?: number;
28 | user?: string;
29 | password?: string;
30 | autoRegister?: boolean;
31 | autoAnswer: boolean;
32 | sessionTimersExpires: number;
33 | extraHeaders: ExtraHeaders;
34 | iceServers: IceServers;
35 | debug: boolean;
36 | }
37 | export const sipPropType = PropTypes.shape({
38 | status: PropTypes.string,
39 | errorType: PropTypes.string,
40 | errorMessage: PropTypes.string,
41 |
42 | host: PropTypes.string,
43 | port: PropTypes.number,
44 | user: PropTypes.string,
45 | password: PropTypes.string,
46 | autoRegister: PropTypes.bool,
47 | autoAnswer: PropTypes.bool,
48 | sessionTimersExpires: PropTypes.number,
49 | extraHeaders: extraHeadersPropType,
50 | iceServers: iceServersPropType,
51 | debug: PropTypes.bool,
52 | });
53 |
54 | export interface Call {
55 | id: string;
56 | status: string;
57 | direction: string;
58 | counterpart: string;
59 | }
60 | export const callPropType = PropTypes.shape({
61 | id: PropTypes.string,
62 | status: PropTypes.string,
63 | direction: PropTypes.string,
64 | counterpart: PropTypes.string,
65 | });
66 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "moduleResolution": "node",
6 | "sourceMap": true,
7 | "declaration": true,
8 | "strictNullChecks": true,
9 | "noImplicitAny": false,
10 | "noUnusedLocals": true,
11 | "allowSyntheticDefaultImports": false,
12 | "pretty": true,
13 | "removeComments": true,
14 | "lib": ["es6", "esnext.asynciterable", "dom"],
15 |
16 | "outDir": "dist",
17 | "rootDir": "src"
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": ["tslint:recommended", "tslint-config-prettier"],
4 | "rules": {
5 | "interface-name": [true, "never-prefix"],
6 | "no-implicit-dependencies": true,
7 | "no-string-literal": false,
8 | "object-literal-sort-keys": false,
9 | "prefer-for-of": false
10 | }
11 | }
12 |
--------------------------------------------------------------------------------