Changes are listed bellow:
- Fixed the control bar microphone and speaker buttons - Improved the default identity generation (no web worker required now) - Improved voice connection error handling (especially for firefox) - Adding a max reconnect limit for voice connection - Don't show the newcomer guide when directly connection to a server - Fixed default profile initialisationcanary
parent
4a4cbdf38e
commit
6f56150e0b
|
@ -1,4 +1,12 @@
|
||||||
# Changelog:
|
# Changelog:
|
||||||
|
* **16.09.20**
|
||||||
|
- Fixed the control bar microphone and speaker buttons
|
||||||
|
- Improved the default identity generation (no web worker required now)
|
||||||
|
- Improved voice connection error handling (especially for firefox)
|
||||||
|
- Adding a max reconnect limit for voice connection
|
||||||
|
- Don't show the newcomer guide when directly connection to a server
|
||||||
|
- Fixed default profile initialisation
|
||||||
|
|
||||||
* **07.09.20**
|
* **07.09.20**
|
||||||
- Fixed the web client for safari
|
- Fixed the web client for safari
|
||||||
|
|
||||||
|
|
|
@ -1167,6 +1167,39 @@
|
||||||
"fastq": "^1.6.0"
|
"fastq": "^1.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@peculiar/asn1-schema": {
|
||||||
|
"version": "2.0.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.0.17.tgz",
|
||||||
|
"integrity": "sha512-7rJD8bR1r6NFE4skDxXsLsFEO3zM2TfjX9wdq5SERoBNEuxGkAJ3uIH84sIMxvDgJtb3cMfLsv8iNpGN0nAWdw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/asn1js": "^0.0.1",
|
||||||
|
"asn1js": "^2.0.26",
|
||||||
|
"pvtsutils": "^1.0.11",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@peculiar/json-schema": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz",
|
||||||
|
"integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@protobufjs/aspromise": {
|
"@protobufjs/aspromise": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
|
@ -1258,6 +1291,14 @@
|
||||||
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
|
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/asn1js": {
|
||||||
|
"version": "0.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/asn1js/-/asn1js-0.0.1.tgz",
|
||||||
|
"integrity": "sha1-74uflwjLFjKhw6nNJ3F8qr55O8I=",
|
||||||
|
"requires": {
|
||||||
|
"@types/pvutils": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/clean-css": {
|
"@types/clean-css": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/clean-css/-/clean-css-4.2.1.tgz",
|
||||||
|
@ -1439,6 +1480,11 @@
|
||||||
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
"integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@types/pvutils": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/pvutils/-/pvutils-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-CgQAm7pjyeF3Gnv78ty4RBVIfluB+Td+2DR8iPaU0prF18pkzptHHP+DoKPfpsJYknKsVZyVsJEu5AuGgAqQ5w=="
|
||||||
|
},
|
||||||
"@types/react": {
|
"@types/react": {
|
||||||
"version": "16.9.26",
|
"version": "16.9.26",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.26.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.26.tgz",
|
||||||
|
@ -2212,6 +2258,11 @@
|
||||||
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
|
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"asmcrypto.js": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/asmcrypto.js/-/asmcrypto.js-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA=="
|
||||||
|
},
|
||||||
"asn1": {
|
"asn1": {
|
||||||
"version": "0.2.4",
|
"version": "0.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||||
|
@ -2232,6 +2283,14 @@
|
||||||
"minimalistic-assert": "^1.0.0"
|
"minimalistic-assert": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"asn1js": {
|
||||||
|
"version": "2.0.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/asn1js/-/asn1js-2.0.26.tgz",
|
||||||
|
"integrity": "sha512-yG89F0j9B4B0MKIcFyWWxnpZPLaNTjCj4tkE3fjbAoo0qmpGw0PYYqSbX/4ebnd9Icn8ZgK4K1fvDyEtW1JYtQ==",
|
||||||
|
"requires": {
|
||||||
|
"pvutils": "^1.0.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
"assert": {
|
"assert": {
|
||||||
"version": "1.5.0",
|
"version": "1.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
|
||||||
|
@ -2577,8 +2636,7 @@
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
"version": "4.11.8",
|
"version": "4.11.8",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
|
||||||
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
|
"integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"body-parser": {
|
"body-parser": {
|
||||||
"version": "1.19.0",
|
"version": "1.19.0",
|
||||||
|
@ -2757,8 +2815,7 @@
|
||||||
"brorand": {
|
"brorand": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||||
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
|
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"browserify-aes": {
|
"browserify-aes": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
@ -4007,7 +4064,6 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
|
||||||
"integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
|
"integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.1",
|
"inherits": "^2.0.1",
|
||||||
"minimalistic-assert": "^1.0.0"
|
"minimalistic-assert": "^1.0.0"
|
||||||
|
@ -4208,7 +4264,6 @@
|
||||||
"version": "6.5.3",
|
"version": "6.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||||
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"bn.js": "^4.4.0",
|
"bn.js": "^4.4.0",
|
||||||
"brorand": "^1.0.1",
|
"brorand": "^1.0.1",
|
||||||
|
@ -7046,7 +7101,6 @@
|
||||||
"version": "1.1.7",
|
"version": "1.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||||
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"inherits": "^2.0.3",
|
"inherits": "^2.0.3",
|
||||||
"minimalistic-assert": "^1.0.1"
|
"minimalistic-assert": "^1.0.1"
|
||||||
|
@ -7067,7 +7121,6 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"hash.js": "^1.0.3",
|
"hash.js": "^1.0.3",
|
||||||
"minimalistic-assert": "^1.0.0",
|
"minimalistic-assert": "^1.0.0",
|
||||||
|
@ -8953,14 +9006,12 @@
|
||||||
"minimalistic-assert": {
|
"minimalistic-assert": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"minimalistic-crypto-utils": {
|
"minimalistic-crypto-utils": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||||
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
|
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"minimatch": {
|
"minimatch": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
|
@ -10288,6 +10339,26 @@
|
||||||
"escape-goat": "^2.0.0"
|
"escape-goat": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"pvtsutils": {
|
||||||
|
"version": "1.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.0.12.tgz",
|
||||||
|
"integrity": "sha512-fudCcWFUE7WPHMRVdlEDdeaeLf+8hvZFvfJJ+p8GZlwrrdoiVfv7WZaPt6k7k/NZjMxR8yUbbH51hpwlSmLHiQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pvutils": {
|
||||||
|
"version": "1.0.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.0.17.tgz",
|
||||||
|
"integrity": "sha512-wLHYUQxWaXVQvKnwIDWFVKDJku9XDCvyhhxoq8dc5MFdIlRenyPI9eSfEtcvgHgD7FlvCyGAlWgOzRnZD99GZQ=="
|
||||||
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.5.2",
|
"version": "6.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||||
|
@ -13937,6 +14008,49 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"webcrypto-core": {
|
||||||
|
"version": "1.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.1.8.tgz",
|
||||||
|
"integrity": "sha512-hKnFXsqh0VloojNeTfrwFoRM4MnaWzH6vtXcaFcGjPEu+8HmBdQZnps3/2ikOFqS8bJN1RYr6mI2P/FJzyZnXg==",
|
||||||
|
"requires": {
|
||||||
|
"@peculiar/asn1-schema": "^2.0.12",
|
||||||
|
"@peculiar/json-schema": "^1.1.12",
|
||||||
|
"asn1js": "^2.0.26",
|
||||||
|
"pvtsutils": "^1.0.11",
|
||||||
|
"tslib": "^2.0.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"webcrypto-liner": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/webcrypto-liner/-/webcrypto-liner-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-e2P3hMkBVlMo1gMQnkEym0uJh+WVskQbvC3lT278ux3/OnxX7KAVaqROi1nlV9FRQBBvdadjuaWiFSLyESh4mA==",
|
||||||
|
"requires": {
|
||||||
|
"@peculiar/asn1-schema": "^2.0.3",
|
||||||
|
"@peculiar/json-schema": "^1.1.10",
|
||||||
|
"asmcrypto.js": "^2.3.2",
|
||||||
|
"asn1js": "^2.0.26",
|
||||||
|
"core-js": "^3.6.5",
|
||||||
|
"des.js": "^1.0.1",
|
||||||
|
"elliptic": "^6.5.2",
|
||||||
|
"pvtsutils": "^1.0.10",
|
||||||
|
"tslib": "^1.13.0",
|
||||||
|
"webcrypto-core": "^1.1.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": {
|
||||||
|
"version": "1.13.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||||
|
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"webpack": {
|
"webpack": {
|
||||||
"version": "4.42.1",
|
"version": "4.42.1",
|
||||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz",
|
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz",
|
||||||
|
|
|
@ -107,6 +107,7 @@
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"simplebar-react": "^2.2.0",
|
"simplebar-react": "^2.2.0",
|
||||||
"twemoji": "^13.0.0",
|
"twemoji": "^13.0.0",
|
||||||
|
"webcrypto-liner": "^1.2.3",
|
||||||
"webrtc-adapter": "^7.5.1"
|
"webrtc-adapter": "^7.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -781,11 +781,15 @@ export class ConnectionHandler {
|
||||||
if(currentInput) {
|
if(currentInput) {
|
||||||
if(shouldRecord) {
|
if(shouldRecord) {
|
||||||
if(this.getInputHardwareState() !== InputHardwareState.START_FAILED) {
|
if(this.getInputHardwareState() !== InputHardwareState.START_FAILED) {
|
||||||
this.startVoiceRecorder(Date.now() - this._last_record_error_popup > 10 * 1000).then(() => {});
|
this.startVoiceRecorder(Date.now() - this._last_record_error_popup > 10 * 1000).then(() => {
|
||||||
|
this.event_registry.fire("notify_state_updated", { state: "microphone" });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentInput.stop().catch(error => {
|
currentInput.stop().catch(error => {
|
||||||
logWarn(LogCategory.AUDIO, tr("Failed to stop the microphone input recorder: %o"), error);
|
logWarn(LogCategory.AUDIO, tr("Failed to stop the microphone input recorder: %o"), error);
|
||||||
|
}).then(() => {
|
||||||
|
this.event_registry.fire("notify_state_updated", { state: "microphone" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -888,7 +892,7 @@ export class ConnectionHandler {
|
||||||
reconnect_properties(profile?: ConnectionProfile) : ConnectParameters {
|
reconnect_properties(profile?: ConnectionProfile) : ConnectParameters {
|
||||||
const name = (this.getClient() ? this.getClient().clientNickName() : "") ||
|
const name = (this.getClient() ? this.getClient().clientNickName() : "") ||
|
||||||
(this.serverConnection && this.serverConnection.handshake_handler() ? this.serverConnection.handshake_handler().parameters.nickname : "") ||
|
(this.serverConnection && this.serverConnection.handshake_handler() ? this.serverConnection.handshake_handler().parameters.nickname : "") ||
|
||||||
StaticSettings.instance.static(Settings.KEY_CONNECT_USERNAME, profile ? profile.default_username : undefined) ||
|
StaticSettings.instance.static(Settings.KEY_CONNECT_USERNAME, profile ? profile.defaultUsername : undefined) ||
|
||||||
"Another TeaSpeak user";
|
"Another TeaSpeak user";
|
||||||
const channel = (this.getClient() && this.getClient().currentChannel() ? this.getClient().currentChannel().channelId : 0) ||
|
const channel = (this.getClient() && this.getClient().currentChannel() ? this.getClient().currentChannel().channelId : 0) ||
|
||||||
(this.serverConnection && this.serverConnection.handshake_handler() ? (this.serverConnection.handshake_handler().parameters.channel || {} as any).target : "");
|
(this.serverConnection && this.serverConnection.handshake_handler() ? (this.serverConnection.handshake_handler().parameters.channel || {} as any).target : "");
|
||||||
|
@ -1048,6 +1052,7 @@ export class ConnectionHandler {
|
||||||
this.client_status.input_muted = muted;
|
this.client_status.input_muted = muted;
|
||||||
this.sound.play(muted ? Sound.MICROPHONE_MUTED : Sound.MICROPHONE_ACTIVATED);
|
this.sound.play(muted ? Sound.MICROPHONE_MUTED : Sound.MICROPHONE_ACTIVATED);
|
||||||
this.update_voice_status();
|
this.update_voice_status();
|
||||||
|
this.event_registry.fire("notify_state_updated", { state: "microphone" });
|
||||||
}
|
}
|
||||||
toggleMicrophone() { this.setMicrophoneMuted(!this.isMicrophoneMuted()); }
|
toggleMicrophone() { this.setMicrophoneMuted(!this.isMicrophoneMuted()); }
|
||||||
|
|
||||||
|
@ -1058,6 +1063,7 @@ export class ConnectionHandler {
|
||||||
if(this.client_status.output_muted === muted) return;
|
if(this.client_status.output_muted === muted) return;
|
||||||
if(muted) this.sound.play(Sound.SOUND_MUTED); /* play the sound *before* we're setting the muted state */
|
if(muted) this.sound.play(Sound.SOUND_MUTED); /* play the sound *before* we're setting the muted state */
|
||||||
this.client_status.output_muted = muted;
|
this.client_status.output_muted = muted;
|
||||||
|
this.event_registry.fire("notify_state_updated", { state: "speaker" });
|
||||||
if(!muted) this.sound.play(Sound.SOUND_ACTIVATED); /* play the sound *after* we're setting we've unmuted the sound */
|
if(!muted) this.sound.play(Sound.SOUND_ACTIVATED); /* play the sound *after* we're setting we've unmuted the sound */
|
||||||
this.update_voice_status();
|
this.update_voice_status();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as log from "./log";
|
||||||
import {LogCategory} from "./log";
|
import {LogCategory} from "./log";
|
||||||
import {guid} from "./crypto/uid";
|
import {guid} from "./crypto/uid";
|
||||||
import {createErrorModal, createInfoModal, createInputModal} from "./ui/elements/Modal";
|
import {createErrorModal, createInfoModal, createInputModal} from "./ui/elements/Modal";
|
||||||
import {default_profile, find_profile} from "./profiles/ConnectionProfile";
|
import {defaultConnectProfile, findConnectProfile} from "./profiles/ConnectionProfile";
|
||||||
import {server_connections} from "./ui/frames/connection_handlers";
|
import {server_connections} from "./ui/frames/connection_handlers";
|
||||||
import {spawnConnectModal} from "./ui/modal/ModalConnect";
|
import {spawnConnectModal} from "./ui/modal/ModalConnect";
|
||||||
import * as top_menu from "./ui/frames/MenuBar";
|
import * as top_menu from "./ui/frames/MenuBar";
|
||||||
|
@ -10,7 +10,7 @@ import {control_bar_instance} from "./ui/frames/control-bar";
|
||||||
import {ConnectionHandler} from "./ConnectionHandler";
|
import {ConnectionHandler} from "./ConnectionHandler";
|
||||||
|
|
||||||
export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => {
|
export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => {
|
||||||
const profile = find_profile(mark.connect_profile) || default_profile();
|
const profile = findConnectProfile(mark.connect_profile) || defaultConnectProfile();
|
||||||
if(profile.valid()) {
|
if(profile.valid()) {
|
||||||
const connection = (typeof(new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection() : server_connections.spawn_server_connection();
|
const connection = (typeof(new_tab) !== "boolean" || !new_tab) ? server_connections.active_connection() : server_connections.spawn_server_connection();
|
||||||
server_connections.set_active_connection(connection);
|
server_connections.set_active_connection(connection);
|
||||||
|
@ -19,7 +19,7 @@ export const boorkmak_connect = (mark: Bookmark, new_tab?: boolean) => {
|
||||||
profile,
|
profile,
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
nickname: mark.nickname === "Another TeaSpeak user" || !mark.nickname ? profile.connect_username() : mark.nickname,
|
nickname: mark.nickname === "Another TeaSpeak user" || !mark.nickname ? profile.connectUsername() : mark.nickname,
|
||||||
password: mark.server_properties.server_password_hash ? {
|
password: mark.server_properties.server_password_hash ? {
|
||||||
password: mark.server_properties.server_password_hash,
|
password: mark.server_properties.server_password_hash,
|
||||||
hashed: true
|
hashed: true
|
||||||
|
|
|
@ -32,17 +32,18 @@ export class HandshakeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.handshake_handler = this.profile.spawn_identity_handshake_handler(this.connection);
|
this.handshake_handler = this.profile.spawnIdentityHandshakeHandler(this.connection);
|
||||||
if(!this.handshake_handler) {
|
if(!this.handshake_handler) {
|
||||||
this.handshake_failed("failed to create identity handler");
|
this.handshake_failed("failed to create identity handler");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.handshake_handler.register_callback((flag, message) => {
|
this.handshake_handler.register_callback((flag, message) => {
|
||||||
if(flag)
|
if(flag) {
|
||||||
this.handshake_finished();
|
this.handshake_finished();
|
||||||
else
|
} else {
|
||||||
this.handshake_failed(message);
|
this.handshake_failed(message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +56,7 @@ export class HandshakeHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
on_teamspeak() {
|
on_teamspeak() {
|
||||||
const type = this.profile.selected_type();
|
const type = this.profile.selectedType();
|
||||||
if(type == IdentitifyType.TEAMSPEAK)
|
if(type == IdentitifyType.TEAMSPEAK)
|
||||||
this.handshake_finished();
|
this.handshake_finished();
|
||||||
else {
|
else {
|
||||||
|
@ -124,8 +125,8 @@ export class HandshakeHandler {
|
||||||
data.client_platform = (os_mapping[os.platform()] || os.platform());
|
data.client_platform = (os_mapping[os.platform()] || os.platform());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.profile.selected_type() === IdentitifyType.TEAMSPEAK)
|
if(this.profile.selectedType() === IdentitifyType.TEAMSPEAK)
|
||||||
data["client_key_offset"] = (this.profile.selected_identity() as TeaSpeakIdentity).hash_number;
|
data["client_key_offset"] = (this.profile.selectedIdentity() as TeaSpeakIdentity).hash_number;
|
||||||
|
|
||||||
this.connection.send_command("clientinit", data).catch(error => {
|
this.connection.send_command("clientinit", data).catch(error => {
|
||||||
if(error instanceof CommandResult) {
|
if(error instanceof CommandResult) {
|
||||||
|
|
|
@ -11,7 +11,9 @@ export enum VoiceConnectionStatus {
|
||||||
Connecting,
|
Connecting,
|
||||||
Connected,
|
Connected,
|
||||||
Disconnecting,
|
Disconnecting,
|
||||||
Disconnected
|
Disconnected,
|
||||||
|
|
||||||
|
Failed
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VoiceConnectionEvents {
|
export interface VoiceConnectionEvents {
|
||||||
|
@ -55,6 +57,7 @@ export abstract class AbstractVoiceConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getConnectionState() : VoiceConnectionStatus;
|
abstract getConnectionState() : VoiceConnectionStatus;
|
||||||
|
abstract getFailedMessage() : string;
|
||||||
|
|
||||||
abstract encodingSupported(codec: number) : boolean;
|
abstract encodingSupported(codec: number) : boolean;
|
||||||
abstract decodingSupported(codec: number) : boolean;
|
abstract decodingSupported(codec: number) : boolean;
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
import * as loader from "tc-loader";
|
import * as loader from "tc-loader";
|
||||||
import {settings, Settings} from "tc-shared/settings";
|
import {settings, Settings} from "tc-shared/settings";
|
||||||
import * as profiles from "tc-shared/profiles/ConnectionProfile";
|
|
||||||
import * as log from "tc-shared/log";
|
import * as log from "tc-shared/log";
|
||||||
import {LogCategory} from "tc-shared/log";
|
import {LogCategory} from "tc-shared/log";
|
||||||
import * as bipc from "./ipc/BrowserIPC";
|
import * as bipc from "./ipc/BrowserIPC";
|
||||||
import * as sound from "./sound/Sounds";
|
import * as sound from "./sound/Sounds";
|
||||||
import * as i18n from "./i18n/localize";
|
import * as i18n from "./i18n/localize";
|
||||||
import {tra} from "./i18n/localize";
|
import {tra} from "./i18n/localize";
|
||||||
import {ConnectionHandler} from "tc-shared/ConnectionHandler";
|
import {ConnectionHandler, ConnectionState} from "tc-shared/ConnectionHandler";
|
||||||
import {createInfoModal} from "tc-shared/ui/elements/Modal";
|
import {createInfoModal} from "tc-shared/ui/elements/Modal";
|
||||||
import * as stats from "./stats";
|
import * as stats from "./stats";
|
||||||
import * as fidentity from "./profiles/identities/TeaForumIdentity";
|
import * as fidentity from "./profiles/identities/TeaForumIdentity";
|
||||||
|
@ -32,7 +31,6 @@ import {MenuEntryType, spawn_context_menu} from "tc-shared/ui/elements/ContextMe
|
||||||
import {copy_to_clipboard} from "tc-shared/utils/helpers";
|
import {copy_to_clipboard} from "tc-shared/utils/helpers";
|
||||||
import {checkForUpdatedApp} from "tc-shared/update";
|
import {checkForUpdatedApp} from "tc-shared/update";
|
||||||
import {setupJSRender} from "tc-shared/ui/jsrender";
|
import {setupJSRender} from "tc-shared/ui/jsrender";
|
||||||
import ContextMenuEvent = JQuery.ContextMenuEvent;
|
|
||||||
import "svg-sprites/client-icons";
|
import "svg-sprites/client-icons";
|
||||||
|
|
||||||
/* required import for init */
|
/* required import for init */
|
||||||
|
@ -44,8 +42,10 @@ import "./connection/CommandHandler";
|
||||||
import "./connection/ConnectionBase";
|
import "./connection/ConnectionBase";
|
||||||
import {ConnectRequestData} from "tc-shared/ipc/ConnectHandler";
|
import {ConnectRequestData} from "tc-shared/ipc/ConnectHandler";
|
||||||
import "./video-viewer/Controller";
|
import "./video-viewer/Controller";
|
||||||
|
import "./profiles/ConnectionProfile";
|
||||||
import "./update/UpdaterWeb";
|
import "./update/UpdaterWeb";
|
||||||
|
import ContextMenuEvent = JQuery.ContextMenuEvent;
|
||||||
|
import {defaultConnectProfile, findConnectProfile} from "tc-shared/profiles/ConnectionProfile";
|
||||||
|
|
||||||
async function initialize() {
|
async function initialize() {
|
||||||
try {
|
try {
|
||||||
|
@ -109,8 +109,6 @@ async function initialize_app() {
|
||||||
});
|
});
|
||||||
sound.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER_SOUNDS) / 100);
|
sound.set_master_volume(settings.global(Settings.KEY_SOUND_MASTER_SOUNDS) / 100);
|
||||||
|
|
||||||
await profiles.load();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ppt.initialize();
|
await ppt.initialize();
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
|
@ -120,57 +118,15 @@ async function initialize_app() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
class TestProxy extends bipc.MethodProxy {
|
|
||||||
constructor(params: bipc.MethodProxyConnectParameters) {
|
|
||||||
super(bipc.get_handler(), params.channel_id && params.client_id ? params : undefined);
|
|
||||||
|
|
||||||
if(!this.is_slave()) {
|
|
||||||
this.register_method(this.add_slave);
|
|
||||||
}
|
|
||||||
if(!this.is_master()) {
|
|
||||||
this.register_method(this.say_hello);
|
|
||||||
this.register_method(this.add_master);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setup() {
|
|
||||||
super.setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected on_connected() {
|
|
||||||
log.info(LogCategory.IPC, "Test proxy connected");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected on_disconnected() {
|
|
||||||
log.info(LogCategory.IPC, "Test proxy disconnected");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async say_hello() : Promise<void> {
|
|
||||||
log.info(LogCategory.IPC, "Hello World");
|
|
||||||
}
|
|
||||||
|
|
||||||
private async add_slave(a: number, b: number) : Promise<number> {
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async add_master(a: number, b: number) : Promise<number> {
|
|
||||||
return a * b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
interface Window {
|
|
||||||
proxy_instance: TestProxy & {url: () => string};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function handle_connect_request(properties: ConnectRequestData, connection: ConnectionHandler) {
|
export function handle_connect_request(properties: ConnectRequestData, connection: ConnectionHandler) {
|
||||||
const profile_uuid = properties.profile || (profiles.default_profile() || {id: 'default'}).id;
|
const profile_uuid = properties.profile || (defaultConnectProfile() || { id: 'default' }).id;
|
||||||
const profile = profiles.find_profile(profile_uuid) || profiles.default_profile();
|
const profile = findConnectProfile(profile_uuid) || defaultConnectProfile();
|
||||||
const username = properties.username || profile.connect_username();
|
const username = properties.username || profile.connectUsername();
|
||||||
|
|
||||||
const password = properties.password ? properties.password.value : "";
|
const password = properties.password ? properties.password.value : "";
|
||||||
const password_hashed = properties.password ? properties.password.hashed : false;
|
const password_hashed = properties.password ? properties.password.hashed : false;
|
||||||
|
|
||||||
|
debugger;
|
||||||
if(profile && profile.valid()) {
|
if(profile && profile.valid()) {
|
||||||
connection.startConnection(properties.address, profile, true, {
|
connection.startConnection(properties.address, profile, true, {
|
||||||
nickname: username,
|
nickname: username,
|
||||||
|
@ -192,21 +148,6 @@ export function handle_connect_request(properties: ConnectRequestData, connectio
|
||||||
}
|
}
|
||||||
|
|
||||||
function main() {
|
function main() {
|
||||||
/*
|
|
||||||
window.proxy_instance = new TestProxy({
|
|
||||||
client_id: settings.static_global<string>("proxy_client_id", undefined),
|
|
||||||
channel_id: settings.static_global<string>("proxy_channel_id", undefined)
|
|
||||||
}) as any;
|
|
||||||
if(window.proxy_instance.is_master()) {
|
|
||||||
window.proxy_instance.setup();
|
|
||||||
window.proxy_instance.url = () => {
|
|
||||||
const data = window.proxy_instance.generate_connect_parameters();
|
|
||||||
return "proxy_channel_id=" + data.channel_id + "&proxy_client_id=" + data.client_id;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
//http://localhost:63343/Web-Client/index.php?_ijt=omcpmt8b9hnjlfguh8ajgrgolr&default_connect_url=true&default_connect_type=teamspeak&default_connect_url=localhost%3A9987&disableUnloadDialog=1&loader_ignore_age=1
|
|
||||||
|
|
||||||
/* initialize font */
|
/* initialize font */
|
||||||
{
|
{
|
||||||
const font = settings.static_global(Settings.KEY_FONT_SIZE, 14); //parseInt(getComputedStyle(document.body).fontSize)
|
const font = settings.static_global(Settings.KEY_FONT_SIZE, 14); //parseInt(getComputedStyle(document.body).fontSize)
|
||||||
|
@ -268,32 +209,6 @@ function main() {
|
||||||
server_connections.set_active_connection(server_connections.all_connections()[0]);
|
server_connections.set_active_connection(server_connections.all_connections()[0]);
|
||||||
checkForUpdatedApp();
|
checkForUpdatedApp();
|
||||||
|
|
||||||
/*
|
|
||||||
(window as any).test_upload = (message?: string) => {
|
|
||||||
message = message || "Hello World";
|
|
||||||
|
|
||||||
const connection = server_connections.active_connection();
|
|
||||||
connection.fileManager.upload_file({
|
|
||||||
size: message.length,
|
|
||||||
overwrite: true,
|
|
||||||
channel: connection.getClient().currentChannel(),
|
|
||||||
name: '/HelloWorld.txt',
|
|
||||||
path: ''
|
|
||||||
}).then(key => {
|
|
||||||
const upload = new RequestFileUpload(key);
|
|
||||||
|
|
||||||
const buffer = new Uint8Array(message.length);
|
|
||||||
{
|
|
||||||
for(let index = 0; index < message.length; index++)
|
|
||||||
buffer[index] = message.charCodeAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
upload.put_data(buffer).catch(error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
(window as any).test_download = async () => {
|
(window as any).test_download = async () => {
|
||||||
const connection = server_connections.active_connection();
|
const connection = server_connections.active_connection();
|
||||||
const download = connection.fileManager.initializeFileDownload({
|
const download = connection.fileManager.initializeFileDownload({
|
||||||
|
@ -387,10 +302,12 @@ function main() {
|
||||||
//setTimeout(() => spawnPermissionEditorModal(server_connections.active_connection()), 3000);
|
//setTimeout(() => spawnPermissionEditorModal(server_connections.active_connection()), 3000);
|
||||||
//setTimeout(() => spawnGroupCreate(server_connections.active_connection(), "server"), 3000);
|
//setTimeout(() => spawnGroupCreate(server_connections.active_connection(), "server"), 3000);
|
||||||
|
|
||||||
|
if(server_connections.active_connection().getServerConnection().getConnectionState() === ConnectionState.UNCONNECTED) {
|
||||||
if(settings.static_global(Settings.KEY_USER_IS_NEW)) {
|
if(settings.static_global(Settings.KEY_USER_IS_NEW)) {
|
||||||
const modal = openModalNewcomer();
|
const modal = openModalNewcomer();
|
||||||
modal.close_listener.push(() => settings.changeGlobal(Settings.KEY_USER_IS_NEW, false));
|
modal.close_listener.push(() => settings.changeGlobal(Settings.KEY_USER_IS_NEW, false));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//spawnVideoPopout(server_connections.active_connection(), "https://www.youtube.com/watch?v=9683D18fyvs");
|
//spawnVideoPopout(server_connections.active_connection(), "https://www.youtube.com/watch?v=9683D18fyvs");
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,33 +6,36 @@ import {AbstractServerConnection} from "../connection/ConnectionBase";
|
||||||
import {HandshakeIdentityHandler} from "../connection/HandshakeHandler";
|
import {HandshakeIdentityHandler} from "../connection/HandshakeHandler";
|
||||||
import {createErrorModal} from "../ui/elements/Modal";
|
import {createErrorModal} from "../ui/elements/Modal";
|
||||||
import {formatMessage} from "../ui/frames/chat";
|
import {formatMessage} from "../ui/frames/chat";
|
||||||
|
import * as loader from "tc-loader";
|
||||||
|
import {Stage} from "tc-loader";
|
||||||
|
import {LogCategory, logDebug, logError} from "tc-shared/log";
|
||||||
|
|
||||||
export class ConnectionProfile {
|
export class ConnectionProfile {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
profile_name: string;
|
profileName: string;
|
||||||
default_username: string;
|
defaultUsername: string;
|
||||||
default_password: string;
|
defaultPassword: string;
|
||||||
|
|
||||||
selected_identity_type: string = "unset";
|
selectedIdentityType: string = "unset";
|
||||||
identities: { [key: string]: Identity } = {};
|
identities: { [key: string]: Identity } = {};
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect_username(): string {
|
connectUsername(): string {
|
||||||
if (this.default_username && this.default_username !== "Another TeaSpeak user")
|
if (this.defaultUsername && this.defaultUsername !== "Another TeaSpeak user")
|
||||||
return this.default_username;
|
return this.defaultUsername;
|
||||||
|
|
||||||
let selected = this.selected_identity();
|
let selected = this.selectedIdentity();
|
||||||
let name = selected ? selected.fallback_name() : undefined;
|
let name = selected ? selected.fallback_name() : undefined;
|
||||||
return name || "Another TeaSpeak user";
|
return name || "Another TeaSpeak user";
|
||||||
}
|
}
|
||||||
|
|
||||||
selected_identity(current_type?: IdentitifyType): Identity {
|
selectedIdentity(current_type?: IdentitifyType): Identity {
|
||||||
if (!current_type)
|
if (!current_type)
|
||||||
current_type = this.selected_type();
|
current_type = this.selectedType();
|
||||||
|
|
||||||
if (current_type === undefined)
|
if (current_type === undefined)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -46,55 +49,58 @@ export class ConnectionProfile {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
selected_type?(): IdentitifyType {
|
selectedType(): IdentitifyType | undefined {
|
||||||
return this.selected_identity_type ? IdentitifyType[this.selected_identity_type.toUpperCase()] : undefined;
|
return this.selectedIdentityType ? IdentitifyType[this.selectedIdentityType.toUpperCase()] : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_identity(type: IdentitifyType, identity: Identity) {
|
setIdentity(type: IdentitifyType, identity: Identity) {
|
||||||
this.identities[IdentitifyType[type].toLowerCase()] = identity;
|
this.identities[IdentitifyType[type].toLowerCase()] = identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
spawn_identity_handshake_handler?(connection: AbstractServerConnection): HandshakeIdentityHandler {
|
spawnIdentityHandshakeHandler(connection: AbstractServerConnection): HandshakeIdentityHandler | undefined {
|
||||||
const identity = this.selected_identity();
|
const identity = this.selectedIdentity();
|
||||||
if (!identity)
|
if (!identity)
|
||||||
return undefined;
|
return undefined;
|
||||||
return identity.spawn_identity_handshake_handler(connection);
|
return identity.spawn_identity_handshake_handler(connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
encode?(): string {
|
encode(): string {
|
||||||
const identity_data = {};
|
const identity_data = {};
|
||||||
for (const key in this.identities)
|
for (const key in this.identities) {
|
||||||
if (this.identities[key])
|
if (this.identities[key]) {
|
||||||
identity_data[key] = this.identities[key].encode();
|
identity_data[key] = this.identities[key].encode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return JSON.stringify({
|
return JSON.stringify({
|
||||||
version: 1,
|
version: 1,
|
||||||
username: this.default_username,
|
username: this.defaultUsername,
|
||||||
password: this.default_password,
|
password: this.defaultPassword,
|
||||||
profile_name: this.profile_name,
|
profile_name: this.profileName,
|
||||||
identity_type: this.selected_identity_type,
|
identity_type: this.selectedIdentityType,
|
||||||
identity_data: identity_data,
|
identity_data: identity_data,
|
||||||
id: this.id
|
id: this.id
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
valid(): boolean {
|
valid(): boolean {
|
||||||
const identity = this.selected_identity();
|
const identity = this.selectedIdentity();
|
||||||
|
|
||||||
return !!identity && identity.valid();
|
return !!identity && identity.valid();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function decode_profile(data): Promise<ConnectionProfile | string> {
|
async function decodeProfile(payload: string): Promise<ConnectionProfile | string> {
|
||||||
data = JSON.parse(data);
|
const data = JSON.parse(payload);
|
||||||
if (data.version !== 1)
|
if (data.version !== 1) {
|
||||||
return "invalid version";
|
return "invalid version";
|
||||||
|
}
|
||||||
|
|
||||||
const result: ConnectionProfile = new ConnectionProfile(data.id);
|
const result: ConnectionProfile = new ConnectionProfile(data.id);
|
||||||
result.default_username = data.username;
|
result.defaultUsername = data.username;
|
||||||
result.default_password = data.password;
|
result.defaultPassword = data.password;
|
||||||
result.profile_name = data.profile_name;
|
result.profileName = data.profile_name;
|
||||||
result.selected_identity_type = (data.identity_type || "").toLowerCase();
|
result.selectedIdentityType = (data.identity_type || "").toLowerCase();
|
||||||
|
|
||||||
if (data.identity_data) {
|
if (data.identity_data) {
|
||||||
for (const key of Object.keys(data.identity_data)) {
|
for (const key of Object.keys(data.identity_data)) {
|
||||||
|
@ -117,20 +123,19 @@ interface ProfilesData {
|
||||||
profiles: string[];
|
profiles: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
let available_profiles: ConnectionProfile[] = [];
|
let availableProfiles_: ConnectionProfile[] = [];
|
||||||
|
|
||||||
export async function load() {
|
async function loadConnectProfiles() {
|
||||||
available_profiles = [];
|
availableProfiles_ = [];
|
||||||
|
|
||||||
const profiles_json = localStorage.getItem("profiles");
|
const profiles_json = localStorage.getItem("profiles");
|
||||||
let profiles_data: ProfilesData = (() => {
|
let profiles_data: ProfilesData = (() => {
|
||||||
try {
|
try {
|
||||||
return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any;
|
return profiles_json ? JSON.parse(profiles_json) : {version: 0} as any;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debugger;
|
|
||||||
console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json);
|
console.error(tr("Invalid profile json! Resetting profiles :( (%o)"), profiles_json);
|
||||||
createErrorModal(tr("Profile data invalid"), formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open();
|
createErrorModal(tr("Profile data invalid"), formatMessage(tr("The profile data is invalid.{:br:}This might cause data loss."))).open();
|
||||||
return {version: 0};
|
return { version: 0 };
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -142,56 +147,58 @@ export async function load() {
|
||||||
}
|
}
|
||||||
if (profiles_data.version == 1) {
|
if (profiles_data.version == 1) {
|
||||||
for (const profile_data of profiles_data.profiles) {
|
for (const profile_data of profiles_data.profiles) {
|
||||||
const profile = await decode_profile(profile_data);
|
const profile = await decodeProfile(profile_data);
|
||||||
if (typeof profile === "string") {
|
if (typeof profile === "string") {
|
||||||
console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data);
|
console.error(tr("Failed to load profile. Reason: %s, Profile data: %s"), profile, profiles_data);
|
||||||
} else {
|
} else {
|
||||||
available_profiles.push(profile as ConnectionProfile);
|
availableProfiles_.push(profile as ConnectionProfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!find_profile("default")) { //Create a default profile and teaforo profile
|
const defaultProfile = findConnectProfile("default");
|
||||||
|
if (!defaultProfile) { //Create a default profile and teaforo profile
|
||||||
{
|
{
|
||||||
const profile = create_new_profile("default", "default");
|
const profile = createConnectProfile(tr("Default Profile"), "default");
|
||||||
profile.default_password = "";
|
profile.defaultPassword = "";
|
||||||
profile.default_username = "";
|
profile.defaultUsername = "";
|
||||||
profile.profile_name = "Default Profile";
|
profile.profileName = "Default Profile";
|
||||||
|
|
||||||
/* generate default identity */
|
/* generate default identity */
|
||||||
try {
|
try {
|
||||||
const identity = await TeaSpeakIdentity.generate_new();
|
const identity = await TeaSpeakIdentity.generateNew();
|
||||||
let active = true;
|
const begin = Date.now();
|
||||||
setTimeout(() => {
|
|
||||||
active = false;
|
const newLevel = await identity.improveLevelJavascript(8, () => Date.now() - begin < 1000);
|
||||||
}, 1000);
|
/* await identity.improveLevelNative(8, 1, () => doImprove); */
|
||||||
await identity.improve_level(8, 1, () => active);
|
logDebug(LogCategory.IDENTITIES, tr("Improved the identity level to %d within %s milliseconds"), newLevel, Date.now() - begin);
|
||||||
profile.set_identity(IdentitifyType.TEAMSPEAK, identity);
|
|
||||||
profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAMSPEAK];
|
profile.setIdentity(IdentitifyType.TEAMSPEAK, identity);
|
||||||
|
profile.selectedIdentityType = IdentitifyType[IdentitifyType.TEAMSPEAK];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
logError(LogCategory.GENERAL, tr("Failed to generate the default identity: %o"), error);
|
||||||
createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!<br>Please manually generate the identity within your settings => profiles")).open();
|
createErrorModal(tr("Failed to generate default identity"), tr("Failed to generate default identity!<br>Please manually generate the identity within your settings => profiles")).open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ /* forum identity (works only when connected to the forum) */
|
{ /* forum identity (works only when connected to the forum) */
|
||||||
const profile = create_new_profile("TeaSpeak Forum", "teaforo");
|
const profile = createConnectProfile(tr("TeaSpeak Forum Profile"), "teaforo");
|
||||||
profile.default_password = "";
|
profile.defaultPassword = "";
|
||||||
profile.default_username = "";
|
profile.defaultUsername = "";
|
||||||
profile.profile_name = "TeaSpeak Forum profile";
|
|
||||||
|
|
||||||
profile.set_identity(IdentitifyType.TEAFORO, TeaForumIdentity.identity());
|
profile.setIdentity(IdentitifyType.TEAFORO, TeaForumIdentity.identity());
|
||||||
profile.selected_identity_type = IdentitifyType[IdentitifyType.TEAFORO];
|
profile.selectedIdentityType = IdentitifyType[IdentitifyType.TEAFORO];
|
||||||
}
|
}
|
||||||
|
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function create_new_profile(name: string, id?: string): ConnectionProfile {
|
export function createConnectProfile(name: string, id?: string): ConnectionProfile {
|
||||||
const profile = new ConnectionProfile(id || guid());
|
const profile = new ConnectionProfile(id || guid());
|
||||||
profile.profile_name = name;
|
profile.profileName = name;
|
||||||
profile.default_username = "";
|
profile.defaultUsername = "";
|
||||||
available_profiles.push(profile);
|
availableProfiles_.push(profile);
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,8 +206,9 @@ let _requires_save = false;
|
||||||
|
|
||||||
export function save() {
|
export function save() {
|
||||||
const profiles: string[] = [];
|
const profiles: string[] = [];
|
||||||
for (const profile of available_profiles)
|
for (const profile of availableProfiles_) {
|
||||||
profiles.push(profile.encode());
|
profiles.push(profile.encode());
|
||||||
|
}
|
||||||
|
|
||||||
const data = JSON.stringify({
|
const data = JSON.stringify({
|
||||||
version: 1,
|
version: 1,
|
||||||
|
@ -217,34 +225,36 @@ export function requires_save(): boolean {
|
||||||
return _requires_save;
|
return _requires_save;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function profiles(): ConnectionProfile[] {
|
export function availableConnectProfiles(): ConnectionProfile[] {
|
||||||
return available_profiles;
|
return availableProfiles_;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function find_profile(id: string): ConnectionProfile | undefined {
|
export function findConnectProfile(id: string): ConnectionProfile | undefined {
|
||||||
for (const profile of profiles())
|
for (const profile of availableConnectProfiles()) {
|
||||||
if (profile.id == id)
|
if (profile.id == id) {
|
||||||
return profile;
|
return profile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function find_profile_by_name(name: string): ConnectionProfile | undefined {
|
export function find_profile_by_name(name: string): ConnectionProfile | undefined {
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
for (const profile of profiles())
|
for (const profile of availableConnectProfiles())
|
||||||
if ((profile.profile_name || "").toLowerCase() == name)
|
if ((profile.profileName || "").toLowerCase() == name)
|
||||||
return profile;
|
return profile;
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function default_profile(): ConnectionProfile {
|
export function defaultConnectProfile(): ConnectionProfile {
|
||||||
return find_profile("default");
|
return findConnectProfile("default");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function set_default_profile(profile: ConnectionProfile) {
|
export function set_default_profile(profile: ConnectionProfile) {
|
||||||
const old_default = default_profile();
|
const old_default = defaultConnectProfile();
|
||||||
if (old_default && old_default != profile) {
|
if (old_default && old_default != profile) {
|
||||||
old_default.id = guid();
|
old_default.id = guid();
|
||||||
}
|
}
|
||||||
|
@ -253,10 +263,19 @@ export function set_default_profile(profile: ConnectionProfile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function delete_profile(profile: ConnectionProfile) {
|
export function delete_profile(profile: ConnectionProfile) {
|
||||||
available_profiles.remove(profile);
|
availableProfiles_.remove(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("beforeunload", event => {
|
window.addEventListener("beforeunload", event => {
|
||||||
if(requires_save())
|
if(requires_save()) {
|
||||||
save();
|
save();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
|
||||||
|
name: "Identity setup",
|
||||||
|
function: async () => {
|
||||||
|
await loadConnectProfiles();
|
||||||
|
},
|
||||||
|
priority: 30
|
||||||
|
})
|
|
@ -81,9 +81,9 @@ export class HandshakeCommandHandler<T extends AbstractHandshakeIdentityHandler>
|
||||||
|
|
||||||
|
|
||||||
handle_command(command: ServerCommand): boolean {
|
handle_command(command: ServerCommand): boolean {
|
||||||
if($.isFunction(this[command.command]))
|
if(typeof this[command.command] === "function") {
|
||||||
this[command.command](command.arguments);
|
this[command.command](command.arguments);
|
||||||
else if(command.command == "error") {
|
} else if(command.command == "error") {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
console.warn(tr("Received unknown command while handshaking (%o)"), command);
|
console.warn(tr("Received unknown command while handshaking (%o)"), command);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as log from "../../log";
|
import * as log from "../../log";
|
||||||
import {LogCategory} from "../../log";
|
import {LogCategory, logDebug, logTrace} from "../../log";
|
||||||
import * as asn1 from "../../crypto/asn1";
|
import * as asn1 from "../../crypto/asn1";
|
||||||
import * as sha from "../../crypto/sha";
|
import * as sha from "../../crypto/sha";
|
||||||
|
|
||||||
|
@ -217,6 +217,7 @@ export namespace CryptoHelper {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
import arraybuffer_to_string = CryptoHelper.arraybuffer_to_string;
|
||||||
|
|
||||||
export class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
export class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
||||||
identity: TeaSpeakIdentity;
|
identity: TeaSpeakIdentity;
|
||||||
|
@ -234,7 +235,7 @@ export class TeaSpeakHandshakeHandler extends AbstractHandshakeIdentityHandler {
|
||||||
this.connection.send_command("handshakebegin", {
|
this.connection.send_command("handshakebegin", {
|
||||||
intention: 0,
|
intention: 0,
|
||||||
authentication_method: this.identity.type(),
|
authentication_method: this.identity.type(),
|
||||||
publicKey: this.identity.public_key
|
publicKey: this.identity.publicKey
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error);
|
log.error(LogCategory.IDENTITIES, tr("Failed to initialize TeamSpeak based handshake. Error: %o"), error);
|
||||||
|
|
||||||
|
@ -433,7 +434,7 @@ class IdentityPOWWorker {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TeaSpeakIdentity implements Identity {
|
export class TeaSpeakIdentity implements Identity {
|
||||||
static async generate_new() : Promise<TeaSpeakIdentity> {
|
static async generateNew() : Promise<TeaSpeakIdentity> {
|
||||||
let key: CryptoKeyPair;
|
let key: CryptoKeyPair;
|
||||||
try {
|
try {
|
||||||
key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]);
|
key = await crypto.subtle.generateKey({name:'ECDH', namedCurve: 'P-256'}, true, ["deriveKey"]);
|
||||||
|
@ -501,7 +502,7 @@ export class TeaSpeakIdentity implements Identity {
|
||||||
private_key: string; /* base64 representation of the private key */
|
private_key: string; /* base64 representation of the private key */
|
||||||
_name: string;
|
_name: string;
|
||||||
|
|
||||||
public_key: string; /* only set when initialized */
|
publicKey: string; /* only set when initialized */
|
||||||
|
|
||||||
private _initialized: boolean;
|
private _initialized: boolean;
|
||||||
private _crypto_key: CryptoKey;
|
private _crypto_key: CryptoKey;
|
||||||
|
@ -569,21 +570,25 @@ export class TeaSpeakIdentity implements Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
async level() : Promise<number> {
|
async level() : Promise<number> {
|
||||||
if(!this._initialized || !this.public_key)
|
if(!this._initialized || !this.publicKey)
|
||||||
throw "not initialized";
|
throw "not initialized";
|
||||||
|
|
||||||
const hash = new Uint8Array(await sha.sha1(this.public_key + this.hash_number));
|
const hash = new Uint8Array(await sha.sha1(this.publicKey + this.hash_number));
|
||||||
|
|
||||||
|
return TeaSpeakIdentity.calculateLevel(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static calculateLevel(buffer: Uint8Array) : number {
|
||||||
let level = 0;
|
let level = 0;
|
||||||
while(level < hash.byteLength && hash[level] == 0)
|
while(level < buffer.byteLength && buffer[level] === 0)
|
||||||
level++;
|
level++;
|
||||||
|
|
||||||
if(level >= hash.byteLength) {
|
if(level >= buffer.byteLength) {
|
||||||
level = 256;
|
level = 256;
|
||||||
} else {
|
} else {
|
||||||
let byte = hash[level];
|
let byte = buffer[level];
|
||||||
level <<= 3;
|
level <<= 3;
|
||||||
while((byte & 0x1) == 0) {
|
while((byte & 0x1) === 0) {
|
||||||
level++;
|
level++;
|
||||||
byte >>= 1;
|
byte >>= 1;
|
||||||
}
|
}
|
||||||
|
@ -597,7 +602,7 @@ export class TeaSpeakIdentity implements Identity {
|
||||||
* @param {string} b
|
* @param {string} b
|
||||||
* @description b must be smaller (in bytes) then a
|
* @description b must be smaller (in bytes) then a
|
||||||
*/
|
*/
|
||||||
private string_add(a: string, b: string) {
|
private static string_add(a: string, b: string) {
|
||||||
const char_result: number[] = [];
|
const char_result: number[] = [];
|
||||||
const char_a = [...a].reverse().map(e => e.charCodeAt(0));
|
const char_a = [...a].reverse().map(e => e.charCodeAt(0));
|
||||||
const char_b = [...b].reverse().map(e => e.charCodeAt(0));
|
const char_b = [...b].reverse().map(e => e.charCodeAt(0));
|
||||||
|
@ -628,29 +633,34 @@ export class TeaSpeakIdentity implements Identity {
|
||||||
let active = true;
|
let active = true;
|
||||||
setTimeout(() => active = false, time);
|
setTimeout(() => active = false, time);
|
||||||
|
|
||||||
return await this.improve_level(-1, threads, () => active);
|
return await this.improveLevelNative(-1, threads, () => active);
|
||||||
}
|
}
|
||||||
|
|
||||||
async improve_level(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise<Boolean> {
|
async improveLevelNative(target: number, threads: number, active_callback: () => boolean, callback_level?: (current: number) => any, callback_status?: (hash_rate: number) => any) : Promise<Boolean> {
|
||||||
if(!this._initialized || !this.public_key)
|
if(!this._initialized || !this.publicKey) {
|
||||||
throw "not initialized";
|
throw "not initialized";
|
||||||
if(target == -1) /* get the highest level possible */
|
}
|
||||||
|
|
||||||
|
/* get the highest level possible */
|
||||||
|
if(target == -1) {
|
||||||
target = 0;
|
target = 0;
|
||||||
else if(target <= await this.level())
|
} else if(target <= await this.level()) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const workers: IdentityPOWWorker[] = [];
|
const workers: IdentityPOWWorker[] = [];
|
||||||
|
|
||||||
const iterations = 100000;
|
const iterations = 100000;
|
||||||
let current_hash;
|
let current_hash;
|
||||||
const next_hash = () => {
|
const next_hash = () => {
|
||||||
if(!current_hash)
|
if(!current_hash) {
|
||||||
return (current_hash = this.hash_number);
|
return (current_hash = this.hash_number);
|
||||||
|
}
|
||||||
|
|
||||||
if(current_hash.length < iterations.toString().length) {
|
if(current_hash.length < iterations.toString().length) {
|
||||||
current_hash = this.string_add(iterations.toString(), current_hash);
|
current_hash = TeaSpeakIdentity.string_add(iterations.toString(), current_hash);
|
||||||
} else {
|
} else {
|
||||||
current_hash = this.string_add(current_hash, iterations.toString());
|
current_hash = TeaSpeakIdentity.string_add(current_hash, iterations.toString());
|
||||||
}
|
}
|
||||||
return current_hash;
|
return current_hash;
|
||||||
};
|
};
|
||||||
|
@ -661,7 +671,7 @@ export class TeaSpeakIdentity implements Identity {
|
||||||
for (let index = 0; index < threads; index++) {
|
for (let index = 0; index < threads; index++) {
|
||||||
const worker = new IdentityPOWWorker();
|
const worker = new IdentityPOWWorker();
|
||||||
workers.push(worker);
|
workers.push(worker);
|
||||||
initialize_promise.push(worker.initialize(this.public_key));
|
initialize_promise.push(worker.initialize(this.publicKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -778,6 +788,63 @@ export class TeaSpeakIdentity implements Identity {
|
||||||
throw "this should never be reached";
|
throw "this should never be reached";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Improve the identity within the current thread */
|
||||||
|
async improveLevelJavascript(target: number, activeCallback: () => boolean) : Promise<number> {
|
||||||
|
const publicKey = str2ab8(this.publicKey);
|
||||||
|
const buffer = new Uint8Array(publicKey.byteLength + 20); /* Max 20 append digest (Appendix is a number in range of [0;2^64]) -> log10(2^64) */
|
||||||
|
|
||||||
|
buffer.set(new Uint8Array(publicKey));
|
||||||
|
buffer.set(new Uint8Array(str2ab8(this.hash_number)), publicKey.byteLength);
|
||||||
|
|
||||||
|
const kChar9 = '9'.charCodeAt(0);
|
||||||
|
const kChar0 = '0'.charCodeAt(0);
|
||||||
|
|
||||||
|
let numberIndex = publicKey.byteLength + this.hash_number.length;
|
||||||
|
let bufferView = buffer.subarray(0, numberIndex);
|
||||||
|
|
||||||
|
const incrementCounter = () => {
|
||||||
|
let currentIndex = numberIndex - 1;
|
||||||
|
while(currentIndex > publicKey.byteLength && buffer[currentIndex] == kChar9) {
|
||||||
|
buffer[currentIndex--] = kChar0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(currentIndex > publicKey.byteLength) {
|
||||||
|
buffer[currentIndex]++;
|
||||||
|
} else {
|
||||||
|
/* yeah a new diget */
|
||||||
|
if(numberIndex >= buffer.byteLength) {
|
||||||
|
throw "hash number got too big. use another identity";
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer[numberIndex] = kChar0;
|
||||||
|
numberIndex++;
|
||||||
|
buffer[currentIndex] = '1'.charCodeAt(0);
|
||||||
|
bufferView = buffer.subarray(0, numberIndex);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentLevel = await this.level();
|
||||||
|
let iteration = 0;
|
||||||
|
const timeBegin = Date.now();
|
||||||
|
|
||||||
|
while(currentLevel < target) {
|
||||||
|
if((iteration++ % 1000) === 0 && !activeCallback()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementCounter();
|
||||||
|
const newLevel = await TeaSpeakIdentity.calculateLevel(new Uint8Array(await crypto.subtle.digest("SHA-1", bufferView)));
|
||||||
|
if(newLevel > currentLevel) {
|
||||||
|
this.hash_number = arraybuffer_to_string(buffer.subarray(publicKey.byteLength, numberIndex));
|
||||||
|
logTrace(LogCategory.IDENTITIES, tr("Found a new identity level at %s. Previous level %d now %d (%d hashes/second)"),
|
||||||
|
this.hash_number, currentLevel, newLevel, iteration * 1000 / (Date.now() - timeBegin));
|
||||||
|
currentLevel = newLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentLevel;
|
||||||
|
}
|
||||||
|
|
||||||
private async initialize() {
|
private async initialize() {
|
||||||
if(!this.private_key)
|
if(!this.private_key)
|
||||||
throw "Invalid private key";
|
throw "Invalid private key";
|
||||||
|
@ -806,8 +873,8 @@ export class TeaSpeakIdentity implements Identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.public_key = await CryptoHelper.export_ecc_key(this._crypto_key, true);
|
this.publicKey = await CryptoHelper.export_ecc_key(this._crypto_key, true);
|
||||||
this._unique_id = base64_encode_ab(await sha.sha1(this.public_key));
|
this._unique_id = base64_encode_ab(await sha.sha1(this.publicKey));
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
log.error(LogCategory.IDENTITIES, error);
|
log.error(LogCategory.IDENTITIES, error);
|
||||||
throw "failed to calculate unique id";
|
throw "failed to calculate unique id";
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
save_bookmark
|
save_bookmark
|
||||||
} from "../../bookmarks";
|
} from "../../bookmarks";
|
||||||
import {connection_log, Regex} from "../../ui/modal/ModalConnect";
|
import {connection_log, Regex} from "../../ui/modal/ModalConnect";
|
||||||
import {profiles} from "../../profiles/ConnectionProfile";
|
import {availableConnectProfiles} from "../../profiles/ConnectionProfile";
|
||||||
import {spawnYesNo} from "../../ui/modal/ModalYesNo";
|
import {spawnYesNo} from "../../ui/modal/ModalYesNo";
|
||||||
import {Settings, settings} from "../../settings";
|
import {Settings, settings} from "../../settings";
|
||||||
import * as log from "../../log";
|
import * as log from "../../log";
|
||||||
|
@ -203,11 +203,11 @@ export function spawnBookmarkModal() {
|
||||||
.text("")
|
.text("")
|
||||||
.css("display", "none")
|
.css("display", "none")
|
||||||
);
|
);
|
||||||
for (const profile of profiles()) {
|
for (const profile of availableConnectProfiles()) {
|
||||||
input_connect_profile.append(
|
input_connect_profile.append(
|
||||||
$.spawn("option")
|
$.spawn("option")
|
||||||
.attr("value", profile.id)
|
.attr("value", profile.id)
|
||||||
.text(profile.profile_name)
|
.text(profile.profileName)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,7 +345,7 @@ export function spawnBookmarkModal() {
|
||||||
|
|
||||||
input_connect_profile.on('change', event => {
|
input_connect_profile.on('change', event => {
|
||||||
const id = input_connect_profile.val() as string;
|
const id = input_connect_profile.val() as string;
|
||||||
const profile = profiles().find(e => e.id === id);
|
const profile = availableConnectProfiles().find(e => e.id === id);
|
||||||
if (profile) {
|
if (profile) {
|
||||||
(selected_bookmark as Bookmark).connect_profile = id;
|
(selected_bookmark as Bookmark).connect_profile = id;
|
||||||
save_bookmark(selected_bookmark);
|
save_bookmark(selected_bookmark);
|
||||||
|
|
|
@ -3,7 +3,7 @@ import * as log from "../../log";
|
||||||
import {LogCategory} from "../../log";
|
import {LogCategory} from "../../log";
|
||||||
import * as loader from "tc-loader";
|
import * as loader from "tc-loader";
|
||||||
import {createModal} from "../../ui/elements/Modal";
|
import {createModal} from "../../ui/elements/Modal";
|
||||||
import {ConnectionProfile, default_profile, find_profile, profiles} from "../../profiles/ConnectionProfile";
|
import {ConnectionProfile, defaultConnectProfile, findConnectProfile, availableConnectProfiles} from "../../profiles/ConnectionProfile";
|
||||||
import {KeyCode} from "../../PPTListener";
|
import {KeyCode} from "../../PPTListener";
|
||||||
import * as i18nc from "../../i18n/country";
|
import * as i18nc from "../../i18n/country";
|
||||||
import {spawnSettingsModal} from "../../ui/modal/ModalSettings";
|
import {spawnSettingsModal} from "../../ui/modal/ModalSettings";
|
||||||
|
@ -208,18 +208,18 @@ export function spawnConnectModal(options: {
|
||||||
|
|
||||||
/* Connect Profiles */
|
/* Connect Profiles */
|
||||||
{
|
{
|
||||||
for (const profile of profiles()) {
|
for (const profile of availableConnectProfiles()) {
|
||||||
input_profile.append(
|
input_profile.append(
|
||||||
$.spawn("option").text(profile.profile_name).val(profile.id)
|
$.spawn("option").text(profile.profileName).val(profile.id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
input_profile.on('change', event => {
|
input_profile.on('change', event => {
|
||||||
selected_profile = find_profile(input_profile.val() as string) || default_profile();
|
selected_profile = findConnectProfile(input_profile.val() as string) || defaultConnectProfile();
|
||||||
{
|
{
|
||||||
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined);
|
settings.changeGlobal(Settings.KEY_CONNECT_USERNAME, undefined);
|
||||||
input_nickname
|
input_nickname
|
||||||
.attr('placeholder', selected_profile.connect_username() || "Another TeaSpeak user")
|
.attr('placeholder', selected_profile.connectUsername() || "Another TeaSpeak user")
|
||||||
.val("");
|
.val("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ export function spawnTeamSpeakIdentityImprove(identity: TeaSpeakIdentity, name:
|
||||||
const threads = parseInt(input_threads.val() as string);
|
const threads = parseInt(input_threads.val() as string);
|
||||||
const target_level = parseInt(input_target_level.val() as string);
|
const target_level = parseInt(input_target_level.val() as string);
|
||||||
if (target_level == 0) {
|
if (target_level == 0) {
|
||||||
identity.improve_level(-1, threads, () => active, current_level => {
|
identity.improveLevelNative(-1, threads, () => active, current_level => {
|
||||||
input_current_level.val(current_level);
|
input_current_level.val(current_level);
|
||||||
}, hash_rate => {
|
}, hash_rate => {
|
||||||
input_hash_rate.val(hash_rate);
|
input_hash_rate.val(hash_rate);
|
||||||
|
@ -63,7 +63,7 @@ export function spawnTeamSpeakIdentityImprove(identity: TeaSpeakIdentity, name:
|
||||||
button_start_stop.trigger('click');
|
button_start_stop.trigger('click');
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
identity.improve_level(target_level, threads, () => active, current_level => {
|
identity.improveLevelNative(target_level, threads, () => active, current_level => {
|
||||||
input_current_level.val(current_level);
|
input_current_level.val(current_level);
|
||||||
}, hash_rate => {
|
}, hash_rate => {
|
||||||
input_hash_rate.val(hash_rate);
|
input_hash_rate.val(hash_rate);
|
||||||
|
|
|
@ -701,7 +701,7 @@ export namespace modal_settings {
|
||||||
error: text
|
error: text
|
||||||
});
|
});
|
||||||
event_registry.on("create-profile", event => {
|
event_registry.on("create-profile", event => {
|
||||||
const profile = profiles.create_new_profile(event.name);
|
const profile = profiles.createConnectProfile(event.name);
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
event_registry.fire_async("create-profile-result", {
|
event_registry.fire_async("create-profile-result", {
|
||||||
status: "success",
|
status: "success",
|
||||||
|
@ -711,7 +711,7 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("delete-profile", event => {
|
event_registry.on("delete-profile", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("delete-profile-result", event.profile_id, tr("Unknown profile"));
|
send_error("delete-profile-result", event.profile_id, tr("Unknown profile"));
|
||||||
|
@ -723,15 +723,15 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
const build_profile_info = (profile: ConnectionProfile) => {
|
const build_profile_info = (profile: ConnectionProfile) => {
|
||||||
const forum_data = profile.selected_identity(IdentitifyType.TEAFORO) as TeaForumIdentity;
|
const forum_data = profile.selectedIdentity(IdentitifyType.TEAFORO) as TeaForumIdentity;
|
||||||
const teamspeak_data = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
const teamspeak_data = profile.selectedIdentity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
||||||
const nickname_data = profile.selected_identity(IdentitifyType.NICKNAME) as NameIdentity;
|
const nickname_data = profile.selectedIdentity(IdentitifyType.NICKNAME) as NameIdentity;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: profile.id,
|
id: profile.id,
|
||||||
name: profile.profile_name,
|
name: profile.profileName,
|
||||||
nickname: profile.default_username,
|
nickname: profile.defaultUsername,
|
||||||
identity_type: profile.selected_identity_type as any,
|
identity_type: profile.selectedIdentityType as any,
|
||||||
identity_forum: !forum_data ? undefined : {
|
identity_forum: !forum_data ? undefined : {
|
||||||
valid: forum_data.valid(),
|
valid: forum_data.valid(),
|
||||||
fallback_name: forum_data.fallback_name()
|
fallback_name: forum_data.fallback_name()
|
||||||
|
@ -749,12 +749,12 @@ export namespace modal_settings {
|
||||||
event_registry.on("query-profile-list", event => {
|
event_registry.on("query-profile-list", event => {
|
||||||
event_registry.fire_async("query-profile-list-result", {
|
event_registry.fire_async("query-profile-list-result", {
|
||||||
status: "success",
|
status: "success",
|
||||||
profiles: profiles.profiles().map(e => build_profile_info(e))
|
profiles: profiles.availableConnectProfiles().map(e => build_profile_info(e))
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("query-profile", event => {
|
event_registry.on("query-profile", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("query-profile-result", event.profile_id, tr("Unknown profile"));
|
send_error("query-profile-result", event.profile_id, tr("Unknown profile"));
|
||||||
|
@ -769,7 +769,7 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("set-default-profile", event => {
|
event_registry.on("set-default-profile", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("set-default-profile-result", event.profile_id, tr("Unknown profile"));
|
send_error("set-default-profile-result", event.profile_id, tr("Unknown profile"));
|
||||||
|
@ -785,14 +785,14 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("set-profile-name", event => {
|
event_registry.on("set-profile-name", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("set-profile-name-result", event.profile_id, tr("Unknown profile"));
|
send_error("set-profile-name-result", event.profile_id, tr("Unknown profile"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.profile_name = event.name;
|
profile.profileName = event.name;
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
event_registry.fire_async("set-profile-name-result", {
|
event_registry.fire_async("set-profile-name-result", {
|
||||||
name: event.name,
|
name: event.name,
|
||||||
|
@ -802,14 +802,14 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("set-default-name", event => {
|
event_registry.on("set-default-name", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("set-default-name-result", event.profile_id, tr("Unknown profile"));
|
send_error("set-default-name-result", event.profile_id, tr("Unknown profile"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.default_username = event.name;
|
profile.defaultUsername = event.name;
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
event_registry.fire_async("set-default-name-result", {
|
event_registry.fire_async("set-default-name-result", {
|
||||||
name: event.name,
|
name: event.name,
|
||||||
|
@ -819,16 +819,16 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("set-identity-name-name", event => {
|
event_registry.on("set-identity-name-name", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("set-identity-name-name-result", event.profile_id, tr("Unknown profile"));
|
send_error("set-identity-name-name-result", event.profile_id, tr("Unknown profile"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let identity = profile.selected_identity(IdentitifyType.NICKNAME) as NameIdentity;
|
let identity = profile.selectedIdentity(IdentitifyType.NICKNAME) as NameIdentity;
|
||||||
if (!identity)
|
if (!identity)
|
||||||
profile.set_identity(IdentitifyType.NICKNAME, identity = new NameIdentity());
|
profile.setIdentity(IdentitifyType.NICKNAME, identity = new NameIdentity());
|
||||||
identity.set_name(event.name);
|
identity.set_name(event.name);
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
|
|
||||||
|
@ -840,7 +840,7 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("query-profile-validity", event => {
|
event_registry.on("query-profile-validity", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("query-profile-validity-result", event.profile_id, tr("Unknown profile"));
|
send_error("query-profile-validity-result", event.profile_id, tr("Unknown profile"));
|
||||||
|
@ -855,14 +855,14 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("query-identity-teamspeak", event => {
|
event_registry.on("query-identity-teamspeak", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("query-identity-teamspeak-result", event.profile_id, tr("Unknown profile"));
|
send_error("query-identity-teamspeak-result", event.profile_id, tr("Unknown profile"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ts = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
const ts = profile.selectedIdentity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
||||||
if (!ts) {
|
if (!ts) {
|
||||||
event_registry.fire_async("query-identity-teamspeak-result", {
|
event_registry.fire_async("query-identity-teamspeak-result", {
|
||||||
status: "error",
|
status: "error",
|
||||||
|
@ -884,26 +884,31 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("select-identity-type", event => {
|
event_registry.on("select-identity-type", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
if(!event.identity_type) {
|
||||||
|
/* dummy event for UI init */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.selected_identity_type = event.identity_type;
|
profile.selectedIdentityType = event.identity_type;
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("generate-identity-teamspeak", event => {
|
event_registry.on("generate-identity-teamspeak", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
send_error("generate-identity-teamspeak-result", event.profile_id, tr("Unknown profile"));
|
send_error("generate-identity-teamspeak-result", event.profile_id, tr("Unknown profile"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TeaSpeakIdentity.generate_new().then(identity => {
|
TeaSpeakIdentity.generateNew().then(identity => {
|
||||||
profile.set_identity(IdentitifyType.TEAMSPEAK, identity);
|
profile.setIdentity(IdentitifyType.TEAMSPEAK, identity);
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
|
|
||||||
identity.level().then(level => {
|
identity.level().then(level => {
|
||||||
|
@ -924,14 +929,14 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("import-identity-teamspeak", event => {
|
event_registry.on("import-identity-teamspeak", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
spawnTeamSpeakIdentityImport(identity => {
|
spawnTeamSpeakIdentityImport(identity => {
|
||||||
profile.set_identity(IdentitifyType.TEAMSPEAK, identity);
|
profile.setIdentity(IdentitifyType.TEAMSPEAK, identity);
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
|
|
||||||
identity.level().catch(error => {
|
identity.level().catch(error => {
|
||||||
|
@ -948,16 +953,16 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("improve-identity-teamspeak-level", event => {
|
event_registry.on("improve-identity-teamspeak-level", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const identity = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
const identity = profile.selectedIdentity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
||||||
if (!identity) return;
|
if (!identity) return;
|
||||||
|
|
||||||
spawnTeamSpeakIdentityImprove(identity, profile.profile_name).close_listener.push(() => {
|
spawnTeamSpeakIdentityImprove(identity, profile.profileName).close_listener.push(() => {
|
||||||
profiles.mark_need_save();
|
profiles.mark_need_save();
|
||||||
|
|
||||||
identity.level().then(level => {
|
identity.level().then(level => {
|
||||||
|
@ -972,13 +977,13 @@ export namespace modal_settings {
|
||||||
});
|
});
|
||||||
|
|
||||||
event_registry.on("export-identity-teamspeak", event => {
|
event_registry.on("export-identity-teamspeak", event => {
|
||||||
const profile = profiles.find_profile(event.profile_id);
|
const profile = profiles.findConnectProfile(event.profile_id);
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
log.warn(LogCategory.CLIENT, tr("Received profile event with unknown profile id (event: %s, id: %s)"), event.type, event.profile_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const identity = profile.selected_identity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
const identity = profile.selectedIdentity(IdentitifyType.TEAMSPEAK) as TeaSpeakIdentity;
|
||||||
if (!identity) return;
|
if (!identity) return;
|
||||||
|
|
||||||
identity.export_ts(true).then(data => {
|
identity.export_ts(true).then(data => {
|
||||||
|
|
|
@ -114,6 +114,9 @@ function initializeController(connection: ConnectionHandler, events: Registry<Ec
|
||||||
events.fire("notify_voice_connection_state", {state: "unsupported-server"});
|
events.fire("notify_voice_connection_state", {state: "unsupported-server"});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case VoiceConnectionStatus.Failed:
|
||||||
|
events.fire("notify_voice_connection_state", {state: "failed", message: connection.getServerConnection().getVoiceConnection().getFailedMessage() });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ export type VoiceConnectionState =
|
||||||
| "connected"
|
| "connected"
|
||||||
| "disconnected"
|
| "disconnected"
|
||||||
| "unsupported-client"
|
| "unsupported-client"
|
||||||
| "unsupported-server";
|
| "unsupported-server"
|
||||||
|
| "failed";
|
||||||
export type TestState =
|
export type TestState =
|
||||||
{ state: "initializing" | "running" | "stopped" | "microphone-invalid" | "unsupported" }
|
{ state: "initializing" | "running" | "stopped" | "microphone-invalid" | "unsupported" }
|
||||||
| { state: "start-failed", error: string };
|
| { state: "start-failed", error: string };
|
||||||
|
@ -29,7 +30,8 @@ export interface EchoTestEvents {
|
||||||
phase: "testing" | "troubleshooting"
|
phase: "testing" | "troubleshooting"
|
||||||
},
|
},
|
||||||
notify_voice_connection_state: {
|
notify_voice_connection_state: {
|
||||||
state: VoiceConnectionState
|
state: VoiceConnectionState,
|
||||||
|
message?: string
|
||||||
},
|
},
|
||||||
notify_test_state: {
|
notify_test_state: {
|
||||||
state: TestState
|
state: TestState
|
||||||
|
|
|
@ -105,7 +105,7 @@
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|
||||||
background-color: #19191bcc;
|
background-color: #19191b;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -120,7 +120,12 @@
|
||||||
|
|
||||||
&.shown {
|
&.shown {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
opacity: 1;
|
opacity: .87;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
opacity: .92;
|
||||||
|
color: #a10000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,23 @@ const VoiceStateOverlay = () => {
|
||||||
events.fire("query_voice_connection_state");
|
events.fire("query_voice_connection_state");
|
||||||
return "loading";
|
return "loading";
|
||||||
});
|
});
|
||||||
|
const [message, setMessage] = useState(undefined);
|
||||||
|
|
||||||
events.reactUse("notify_voice_connection_state", event => setState(event.state));
|
events.reactUse("notify_voice_connection_state", event => {
|
||||||
|
setState(event.state);
|
||||||
|
setMessage(event.message);
|
||||||
|
});
|
||||||
|
|
||||||
let inner, shown = true;
|
let inner, shown = true, error = false;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
|
case "failed":
|
||||||
|
error = true;
|
||||||
|
inner = <a key={state}>
|
||||||
|
<Translatable>Voice connection establishment has been failed:</Translatable><br />
|
||||||
|
{message}
|
||||||
|
</a>;
|
||||||
|
break;
|
||||||
|
|
||||||
case "disconnected":
|
case "disconnected":
|
||||||
inner = <a key={state}><Translatable>Voice connection has been disconnected.</Translatable></a>;
|
inner = <a key={state}><Translatable>Voice connection has been disconnected.</Translatable></a>;
|
||||||
break;
|
break;
|
||||||
|
@ -57,7 +69,7 @@ const VoiceStateOverlay = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cssStyle.overlay + " " + (shown ? cssStyle.shown : "")}>
|
<div className={cssStyle.overlay + " " + (shown ? cssStyle.shown : "") + " " + (error ? cssStyle.error : "")}>
|
||||||
{inner}
|
{inner}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,14 +17,15 @@ export class AudioLibrary {
|
||||||
private registeredClients: {[key: number]: AudioClient} = {};
|
private registeredClients: {[key: number]: AudioClient} = {};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.worker = new WorkerOwner(() => {
|
this.worker = new WorkerOwner(AudioLibrary.spawnNewWorker);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static spawnNewWorker() {
|
||||||
/*
|
/*
|
||||||
* Attention don't use () => new Worker(...).
|
* Attention don't use () => new Worker(...).
|
||||||
* This confuses the worker plugin and will not emit any modules
|
* This confuses the worker plugin and will not emit any modules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return new Worker("./worker/index.ts", { type: "module" });
|
return new Worker("./worker/index.ts", { type: "module" });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import "webrtc-adapter";
|
import "webrtc-adapter";
|
||||||
|
import "webcrypto-liner";
|
||||||
|
|
||||||
import "./index.scss";
|
import "./index.scss";
|
||||||
import "./FileTransfer";
|
import "./FileTransfer";
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
private readonly serverConnectionStateListener;
|
private readonly serverConnectionStateListener;
|
||||||
private connectionType: VoiceEncodeType = VoiceEncodeType.NATIVE_ENCODE;
|
private connectionType: VoiceEncodeType = VoiceEncodeType.NATIVE_ENCODE;
|
||||||
private connectionState: VoiceConnectionStatus;
|
private connectionState: VoiceConnectionStatus;
|
||||||
|
private failedConnectionMessage: string;
|
||||||
|
|
||||||
private localAudioStarted = false;
|
private localAudioStarted = false;
|
||||||
private connectionLostModalOpen = false;
|
private connectionLostModalOpen = false;
|
||||||
|
@ -65,6 +66,8 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
|
|
||||||
private encoderCodec: number = 5;
|
private encoderCodec: number = 5;
|
||||||
|
|
||||||
|
private lastConnectAttempt: number = 0;
|
||||||
|
|
||||||
constructor(connection: ServerConnection) {
|
constructor(connection: ServerConnection) {
|
||||||
super(connection);
|
super(connection);
|
||||||
|
|
||||||
|
@ -83,6 +86,10 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
return this.connectionState;
|
return this.connectionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getFailedMessage(): string {
|
||||||
|
return this.failedConnectionMessage;
|
||||||
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.connection.events.off(this.serverConnectionStateListener);
|
this.connection.events.off(this.serverConnectionStateListener);
|
||||||
this.dropVoiceBridge();
|
this.dropVoiceBridge();
|
||||||
|
@ -98,6 +105,10 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
this.events.destroy();
|
this.events.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.dropVoiceBridge();
|
||||||
|
}
|
||||||
|
|
||||||
async acquireVoiceRecorder(recorder: RecorderProfile | undefined, enforce?: boolean) {
|
async acquireVoiceRecorder(recorder: RecorderProfile | undefined, enforce?: boolean) {
|
||||||
if(this.currentAudioSource === recorder && !enforce)
|
if(this.currentAudioSource === recorder && !enforce)
|
||||||
return;
|
return;
|
||||||
|
@ -154,12 +165,14 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.connection.getConnectionState() !== ConnectionState.CONNECTED)
|
if(this.connection.getConnectionState() !== ConnectionState.CONNECTED) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastConnectAttempt = Date.now();
|
||||||
this.connectAttemptCounter++;
|
this.connectAttemptCounter++;
|
||||||
if(this.voiceBridge) {
|
if(this.voiceBridge) {
|
||||||
this.voiceBridge.callback_disconnect = undefined;
|
this.voiceBridge.callbackDisconnect = undefined;
|
||||||
this.voiceBridge.disconnect();
|
this.voiceBridge.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +185,7 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
request: request
|
request: request
|
||||||
}, payload)))
|
}, payload)))
|
||||||
};
|
};
|
||||||
this.voiceBridge.callback_disconnect = () => {
|
this.voiceBridge.callbackDisconnect = () => {
|
||||||
this.connection.client.log.log(EventType.CONNECTION_VOICE_DROPPED, { });
|
this.connection.client.log.log(EventType.CONNECTION_VOICE_DROPPED, { });
|
||||||
if(!this.connectionLostModalOpen) {
|
if(!this.connectionLostModalOpen) {
|
||||||
this.connectionLostModalOpen = true;
|
this.connectionLostModalOpen = true;
|
||||||
|
@ -181,13 +194,14 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
modal.open();
|
modal.open();
|
||||||
}
|
}
|
||||||
logInfo(LogCategory.WEBRTC, tr("Lost voice connection to target server. Trying to reconnect."));
|
logInfo(LogCategory.WEBRTC, tr("Lost voice connection to target server. Trying to reconnect."));
|
||||||
this.startVoiceBridge();
|
this.executeVoiceBridgeReconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.connection.client.log.log(EventType.CONNECTION_VOICE_CONNECT, { attemptCount: this.connectAttemptCounter });
|
this.connection.client.log.log(EventType.CONNECTION_VOICE_CONNECT, { attemptCount: this.connectAttemptCounter });
|
||||||
this.setConnectionState(VoiceConnectionStatus.Connecting);
|
this.setConnectionState(VoiceConnectionStatus.Connecting);
|
||||||
this.voiceBridge.connect().then(result => {
|
this.voiceBridge.connect().then(result => {
|
||||||
if(result.type === "success") {
|
if(result.type === "success") {
|
||||||
|
this.lastConnectAttempt = 0;
|
||||||
this.connectAttemptCounter = 0;
|
this.connectAttemptCounter = 0;
|
||||||
|
|
||||||
this.connection.client.log.log(EventType.CONNECTION_VOICE_CONNECT_SUCCEEDED, { });
|
this.connection.client.log.log(EventType.CONNECTION_VOICE_CONNECT_SUCCEEDED, { });
|
||||||
|
@ -204,23 +218,32 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
} else if(result.type === "canceled") {
|
} else if(result.type === "canceled") {
|
||||||
/* we've to do nothing here */
|
/* we've to do nothing here */
|
||||||
} else if(result.type === "failed") {
|
} else if(result.type === "failed") {
|
||||||
logWarn(LogCategory.VOICE, tr("Failed to setup voice bridge: %s. Reconnect: %o"), result.message, result.allowReconnect);
|
let doReconnect = result.allowReconnect && this.connectAttemptCounter < 5;
|
||||||
|
logWarn(LogCategory.VOICE, tr("Failed to setup voice bridge: %s. Reconnect: %o"), result.message, doReconnect);
|
||||||
|
|
||||||
this.connection.client.log.log(EventType.CONNECTION_VOICE_CONNECT_FAILED, {
|
this.connection.client.log.log(EventType.CONNECTION_VOICE_CONNECT_FAILED, {
|
||||||
reason: result.message,
|
reason: result.message,
|
||||||
reconnect_delay: result.allowReconnect ? 1 : 0
|
reconnect_delay: doReconnect ? 1 : 0
|
||||||
});
|
});
|
||||||
|
|
||||||
if(result.allowReconnect) {
|
if(doReconnect) {
|
||||||
this.startVoiceBridge();
|
this.executeVoiceBridgeReconnect();
|
||||||
|
} else {
|
||||||
|
this.failedConnectionMessage = result.message;
|
||||||
|
this.setConnectionState(VoiceConnectionStatus.Failed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private executeVoiceBridgeReconnect() {
|
||||||
|
/* TODO: May some kind of incremental timeout? */
|
||||||
|
this.startVoiceBridge();
|
||||||
|
}
|
||||||
|
|
||||||
private dropVoiceBridge() {
|
private dropVoiceBridge() {
|
||||||
if(this.voiceBridge) {
|
if(this.voiceBridge) {
|
||||||
this.voiceBridge.callback_disconnect = undefined;
|
this.voiceBridge.callbackDisconnect = undefined;
|
||||||
this.voiceBridge.disconnect();
|
this.voiceBridge.disconnect();
|
||||||
this.voiceBridge = undefined;
|
this.voiceBridge = undefined;
|
||||||
}
|
}
|
||||||
|
@ -296,6 +319,8 @@ export class VoiceConnection extends AbstractVoiceConnection {
|
||||||
if(event.newState === ConnectionState.CONNECTED) {
|
if(event.newState === ConnectionState.CONNECTED) {
|
||||||
this.startVoiceBridge();
|
this.startVoiceBridge();
|
||||||
} else {
|
} else {
|
||||||
|
this.connectAttemptCounter = 0;
|
||||||
|
this.lastConnectAttempt = 0;
|
||||||
this.dropVoiceBridge();
|
this.dropVoiceBridge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ export abstract class VoiceBridge {
|
||||||
callback_incoming_voice: (packet: VoicePacket) => void;
|
callback_incoming_voice: (packet: VoicePacket) => void;
|
||||||
callback_incoming_whisper: (packet: VoiceWhisperPacket) => void;
|
callback_incoming_whisper: (packet: VoiceWhisperPacket) => void;
|
||||||
|
|
||||||
callback_disconnect: () => void;
|
callbackDisconnect: () => void;
|
||||||
|
|
||||||
setMuted(flag: boolean) {
|
setMuted(flag: boolean) {
|
||||||
this.muted = flag;
|
this.muted = flag;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as aplayer from "tc-backend/web/audio/player";
|
import * as aplayer from "tc-backend/web/audio/player";
|
||||||
|
import * as log from "tc-shared/log";
|
||||||
import {LogCategory, logDebug, logError, logInfo, logTrace, logWarn} from "tc-shared/log";
|
import {LogCategory, logDebug, logError, logInfo, logTrace, logWarn} from "tc-shared/log";
|
||||||
import {tr} from "tc-shared/i18n/localize";
|
import {tr} from "tc-shared/i18n/localize";
|
||||||
import * as log from "tc-shared/log";
|
|
||||||
import {VoiceBridge, VoiceBridgeConnectResult} from "./VoiceBridge";
|
import {VoiceBridge, VoiceBridgeConnectResult} from "./VoiceBridge";
|
||||||
|
|
||||||
export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
|
@ -14,6 +14,7 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
private whisperDataChannel: RTCDataChannel;
|
private whisperDataChannel: RTCDataChannel;
|
||||||
|
|
||||||
private cachedIceCandidates: RTCIceCandidateInit[];
|
private cachedIceCandidates: RTCIceCandidateInit[];
|
||||||
|
private localIceCandidateCount: number;
|
||||||
|
|
||||||
private callbackRtcAnswer: (answer: any) => void;
|
private callbackRtcAnswer: (answer: any) => void;
|
||||||
|
|
||||||
|
@ -78,8 +79,7 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
{
|
{
|
||||||
let rtcConfig: RTCConfiguration = {};
|
let rtcConfig: RTCConfiguration = {};
|
||||||
rtcConfig.iceServers = [];
|
rtcConfig.iceServers = [];
|
||||||
rtcConfig.iceServers.push({ urls: 'stun:stun.l.google.com:19302' });
|
rtcConfig.iceServers.push({ urls: ['stun:stun.l.google.com:19302'] });
|
||||||
//rtcConfig.iceServers.push({ urls: "stun:stun.teaspeak.de:3478" });
|
|
||||||
|
|
||||||
this.rtcConnection = new RTCPeerConnection(rtcConfig);
|
this.rtcConnection = new RTCPeerConnection(rtcConfig);
|
||||||
|
|
||||||
|
@ -111,17 +111,23 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
this.whisperDataChannel.binaryType = "arraybuffer";
|
this.whisperDataChannel.binaryType = "arraybuffer";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* setting a dummy connect failed handler in case the rtc peer connection changes it's state to failed */
|
||||||
|
const connectFailedPromise = new Promise((resolve, reject) => this.callbackRtcConnectFailed = reject);
|
||||||
|
const wrapWithError = <T>(promise: Promise<T>) : Promise<T> => Promise.race([ promise, connectFailedPromise ]) as any;
|
||||||
|
|
||||||
let offer: RTCSessionDescriptionInit;
|
let offer: RTCSessionDescriptionInit;
|
||||||
try {
|
try {
|
||||||
offer = await this.rtcConnection.createOffer(this.generateRtpOfferOptions());
|
offer = await wrapWithError(this.rtcConnection.createOffer(this.generateRtpOfferOptions()));
|
||||||
if(canceled.value) return;
|
if(canceled.value) return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(LogCategory.VOICE, tr("Failed to generate RTC offer: %o"), error);
|
logError(LogCategory.VOICE, tr("Failed to generate RTC offer: %o"), error);
|
||||||
throw tr("failed to generate local offer");
|
throw tr("failed to generate local offer");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.localIceCandidateCount = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.rtcConnection.setLocalDescription(offer);
|
await wrapWithError(this.rtcConnection.setLocalDescription(offer));
|
||||||
if(canceled.value) return;
|
if(canceled.value) return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logError(LogCategory.VOICE, tr("Failed to apply local description: %o"), error);
|
logError(LogCategory.VOICE, tr("Failed to apply local description: %o"), error);
|
||||||
|
@ -140,7 +146,7 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
sdp: offer.sdp
|
sdp: offer.sdp
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
answer = await new Promise((resolve, reject) => {
|
answer = await wrapWithError(new Promise((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
if(canceled.value) {
|
if(canceled.value) {
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -156,7 +162,7 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
resolve(answer);
|
resolve(answer);
|
||||||
};
|
};
|
||||||
});
|
}));
|
||||||
if(canceled.value) return;
|
if(canceled.value) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +171,7 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.rtcConnection.setRemoteDescription(new RTCSessionDescription(answer.msg));
|
await wrapWithError(this.rtcConnection.setRemoteDescription(new RTCSessionDescription(answer.msg)));
|
||||||
if(canceled.value) return;
|
if(canceled.value) return;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const kParseErrorPrefix = "Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': ";
|
const kParseErrorPrefix = "Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': ";
|
||||||
|
@ -176,9 +182,11 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
throw tr("failed to apply remotes description");
|
throw tr("failed to apply remotes description");
|
||||||
}
|
}
|
||||||
|
|
||||||
while(this.cachedIceCandidates.length > 0)
|
while(this.cachedIceCandidates.length > 0) {
|
||||||
this.registerRemoteIceCandidate(this.cachedIceCandidates.pop_front());
|
this.registerRemoteIceCandidate(this.cachedIceCandidates.pop_front());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ATTENTION: Do not use wrapWithError from now on (this.callbackRtcConnectFailed has been changed) */
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
if(this.rtcConnection.connectionState === "connected") {
|
if(this.rtcConnection.connectionState === "connected") {
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -215,8 +223,8 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
this.cleanupRtcResources();
|
this.cleanupRtcResources();
|
||||||
this.connectionState = "unconnected";
|
this.connectionState = "unconnected";
|
||||||
|
|
||||||
if(this.callback_disconnect)
|
if(this.callbackDisconnect)
|
||||||
this.callback_disconnect();
|
this.callbackDisconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
private abortConnectionAttempt() {
|
private abortConnectionAttempt() {
|
||||||
|
@ -253,8 +261,9 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
if(typeof this.voiceDataChannel === "undefined")
|
if(typeof this.voiceDataChannel === "undefined")
|
||||||
throw tr("missing main data channel");
|
throw tr("missing main data channel");
|
||||||
|
|
||||||
if(this.voiceDataChannel.readyState === "open")
|
if(this.voiceDataChannel.readyState === "open") {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
const id = setTimeout(reject, timeout);
|
const id = setTimeout(reject, timeout);
|
||||||
|
@ -288,7 +297,7 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleRtcConnectionStateChange() {
|
private handleRtcConnectionStateChange() {
|
||||||
log.debug(LogCategory.WEBRTC, tr("Connection state changed to %s"), this.rtcConnection.connectionState);
|
log.debug(LogCategory.WEBRTC, tr("Connection state changed to %s (Local connection state: %s)"), this.rtcConnection.connectionState, this.connectionState);
|
||||||
switch (this.rtcConnection.connectionState) {
|
switch (this.rtcConnection.connectionState) {
|
||||||
case "connected":
|
case "connected":
|
||||||
if(this.callbackRtcConnected)
|
if(this.callbackRtcConnected)
|
||||||
|
@ -298,14 +307,14 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
case "failed":
|
case "failed":
|
||||||
if(this.callbackRtcConnectFailed)
|
if(this.callbackRtcConnectFailed)
|
||||||
this.callbackRtcConnectFailed(tr("connect attempt failed"));
|
this.callbackRtcConnectFailed(tr("connect attempt failed"));
|
||||||
else if(this.callback_disconnect)
|
else if(this.callbackDisconnect)
|
||||||
this.callback_disconnect();
|
this.callbackDisconnect();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "disconnected":
|
case "disconnected":
|
||||||
case "closed":
|
case "closed":
|
||||||
if(this.callback_disconnect)
|
if(this.callbackDisconnect)
|
||||||
this.callback_disconnect();
|
this.callbackDisconnect();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,12 +328,22 @@ export abstract class WebRTCVoiceBridge extends VoiceBridge {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleIceCandidate(event: RTCPeerConnectionIceEvent) {
|
private handleIceCandidate(event: RTCPeerConnectionIceEvent) {
|
||||||
if(event.candidate && event.candidate.protocol !== "tcp")
|
if(event.candidate && event.candidate.protocol !== "tcp") {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(event.candidate) {
|
if(event.candidate) {
|
||||||
|
this.localIceCandidateCount++;
|
||||||
log.debug(LogCategory.WEBRTC, tr("Gathered local ice candidate for stream %d: %s"), event.candidate.sdpMLineIndex, event.candidate.candidate);
|
log.debug(LogCategory.WEBRTC, tr("Gathered local ice candidate for stream %d: %s"), event.candidate.sdpMLineIndex, event.candidate.candidate);
|
||||||
this.callback_send_control_data("ice", { msg: event.candidate.toJSON() });
|
this.callback_send_control_data("ice", { msg: event.candidate.toJSON() });
|
||||||
|
} else if(this.localIceCandidateCount === 0) {
|
||||||
|
logError(LogCategory.WEBRTC, tr("Failed to gather any local ice candidates... This is a fatal error."));
|
||||||
|
|
||||||
|
/* we don't allow a reconnect here since it's most the times not fixable by just trying again */
|
||||||
|
this.allowReconnect = false;
|
||||||
|
if(this.callbackRtcConnectFailed) {
|
||||||
|
this.callbackRtcConnectFailed(tr("failed to gather any local ICE candidates"));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.debug(LogCategory.WEBRTC, tr("Local ICE candidate gathering finish."));
|
log.debug(LogCategory.WEBRTC, tr("Local ICE candidate gathering finish."));
|
||||||
this.callback_send_control_data("ice_finish", {});
|
this.callback_send_control_data("ice_finish", {});
|
||||||
|
|
Loading…
Reference in New Issue