react-device-portal
Version:
Simple WebRTC data channel for React.
229 lines (226 loc) • 10.4 kB
JavaScript
import { __awaiter, __generator } from '../_virtual/_tslib.js';
import { delay } from '../delay.js';
import { settings } from '../settings.js';
var Peer = /** @class */ (function () {
function Peer(room, options) {
if (options === void 0) { options = {}; }
var _a, _b, _c;
this.room = room;
this.isDestroyed = false;
this.connection = null;
this.channel = null;
this.value = null;
this.onValue = options.onValue;
this.sendLastValueOnConnectAndReconnect =
(_a = options.sendLastValueOnConnectAndReconnect) !== null && _a !== void 0 ? _a : true;
this.webrtcSignalingServer =
(_b = options.webrtcSignalingServer) !== null && _b !== void 0 ? _b : settings.webrtcSignalingServer;
this.iceServers = (_c = options.iceServers) !== null && _c !== void 0 ? _c : settings.iceServers;
this.run();
}
Peer.prototype.run = function () {
return __awaiter(this, void 0, void 0, function () {
var failed, error_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
failed = false;
_a.label = 1;
case 1:
_a.trys.push([1, 3, 4, 5]);
return [4 /*yield*/, this.connect()];
case 2:
_a.sent();
return [3 /*break*/, 5];
case 3:
error_1 = _a.sent();
failed = true;
queueMicrotask(function () {
throw error_1;
});
return [3 /*break*/, 5];
case 4:
this.close();
return [7 /*endfinally*/];
case 5:
if (this.isDestroyed) {
return [2 /*return*/];
}
if (!failed) return [3 /*break*/, 7];
return [4 /*yield*/, delay(Math.random() * 5000 + 1000)];
case 6:
_a.sent();
_a.label = 7;
case 7: return [4 /*yield*/, this.run()]; // Reestablish new connection
case 8:
_a.sent(); // Reestablish new connection
return [2 /*return*/];
}
});
});
};
Peer.prototype.close = function () {
var _a, _b;
(_a = this.connection) === null || _a === void 0 ? void 0 : _a.close();
this.connection = null;
(_b = this.channel) === null || _b === void 0 ? void 0 : _b.close();
this.channel = null;
};
Peer.prototype.destroy = function () {
this.isDestroyed = true;
this.close();
};
Peer.prototype.getOtherPeerRole = function () {
return this.role === 'initiator' ? 'responder' : 'initiator';
};
Peer.prototype.acquireIceCandidatesLoop = function () {
return __awaiter(this, void 0, void 0, function () {
var lastPeerIceCandidateCreatedAt, response, data, newCandidates, _i, newCandidates_1, candidate;
var _a, _b, _c, _d;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
lastPeerIceCandidateCreatedAt = null;
_e.label = 1;
case 1:
if (!!this.isDestroyed) return [3 /*break*/, 10];
return [4 /*yield*/, fetch("".concat(this.webrtcSignalingServer, "/api/v1/").concat(this.room, "/").concat(this.getOtherPeerRole(), "/ice-candidate"))];
case 2:
response = _e.sent();
return [4 /*yield*/, response.json()];
case 3:
data = _e.sent();
if (!(data.data !== null && data.data.length > 0)) return [3 /*break*/, 8];
newCandidates = data.data
.filter(function (item) {
return lastPeerIceCandidateCreatedAt === null ||
item.createdAt > lastPeerIceCandidateCreatedAt;
})
.map(function (_a) {
var payload = _a.payload;
return new RTCIceCandidate(JSON.parse(payload));
});
_i = 0, newCandidates_1 = newCandidates;
_e.label = 4;
case 4:
if (!(_i < newCandidates_1.length)) return [3 /*break*/, 7];
candidate = newCandidates_1[_i];
return [4 /*yield*/, ((_a = this.connection) === null || _a === void 0 ? void 0 : _a.addIceCandidate(candidate))];
case 5:
_e.sent();
_e.label = 6;
case 6:
_i++;
return [3 /*break*/, 4];
case 7:
lastPeerIceCandidateCreatedAt = data.data.at(-1).createdAt;
_e.label = 8;
case 8: return [4 /*yield*/, delay(((_b = this.connection) === null || _b === void 0 ? void 0 : _b.connectionState) === 'connected' ? 5000 : 2000)];
case 9:
_e.sent();
if (((_c = this.connection) === null || _c === void 0 ? void 0 : _c.connectionState) === 'closed' ||
((_d = this.connection) === null || _d === void 0 ? void 0 : _d.connectionState) === 'failed') {
return [2 /*return*/];
}
return [3 /*break*/, 1];
case 10: return [2 /*return*/];
}
});
});
};
Peer.prototype.getRemoteDescription = function () {
return __awaiter(this, void 0, void 0, function () {
var response, data;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (!!this.isDestroyed) return [3 /*break*/, 4];
return [4 /*yield*/, fetch("".concat(this.webrtcSignalingServer, "/api/v1/").concat(this.room, "/").concat(this.getOtherPeerRole(), "/local-description"))];
case 1:
response = _b.sent();
return [4 /*yield*/, response.json()];
case 2:
data = _b.sent();
if ((_a = data.data) === null || _a === void 0 ? void 0 : _a.payload) {
return [2 /*return*/, JSON.parse(data.data.payload)];
}
return [4 /*yield*/, delay(1000)];
case 3:
_b.sent();
return [3 /*break*/, 0];
case 4: return [2 /*return*/, null];
}
});
});
};
Peer.prototype.setAndShareLocalDescription = function (description) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.connection) {
throw new Error('Connection is not initialized');
}
return [4 /*yield*/, this.connection.setLocalDescription(description)];
case 1:
_a.sent();
return [4 /*yield*/, fetch("".concat(this.webrtcSignalingServer, "/api/v1/").concat(this.room, "/").concat(this.role, "/local-description"), {
method: 'POST',
body: JSON.stringify(description),
})];
case 2:
_a.sent();
return [2 /*return*/];
}
});
});
};
Peer.prototype.shareNewIceCandidate = function (event) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!event.candidate) return [3 /*break*/, 2];
return [4 /*yield*/, fetch("".concat(this.webrtcSignalingServer, "/api/v1/").concat(this.room, "/").concat(this.role, "/ice-candidate"), {
method: 'POST',
body: JSON.stringify(event.candidate.toJSON()),
})];
case 1:
_a.sent();
_a.label = 2;
case 2: return [2 /*return*/];
}
});
});
};
Peer.prototype.send = function (value) {
var _a;
if (((_a = this.channel) === null || _a === void 0 ? void 0 : _a.readyState) === 'open') {
this.channel.send(value);
}
this.value = { value: value };
};
Peer.prototype.initializeConnectionAndChannel = function () {
var _this = this;
this.connection = new RTCPeerConnection();
this.connection.onicecandidate = this.shareNewIceCandidate.bind(this);
this.channel = this.connection.createDataChannel(settings.channel.label, {
negotiated: true,
id: settings.channel.id,
});
this.channel.onopen = function () {
var _a;
if (_this.value && _this.sendLastValueOnConnectAndReconnect) {
(_a = _this.channel) === null || _a === void 0 ? void 0 : _a.send(_this.value.value);
}
};
this.channel.onmessage = function (event) {
var _a;
(_a = _this.onValue) === null || _a === void 0 ? void 0 : _a.call(_this, event.data);
};
};
return Peer;
}());
export { Peer };
//# sourceMappingURL=Peer.js.map