diff --git a/.eslintrc.js b/.eslintrc.js
index ea91bf980..a4fd5f17b 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -85,11 +85,10 @@ module.exports = {
findFirstCharacters: 'writable',
tilesets: 'writable',
layers: 'writable',
- peer: 'writable',
settings: 'writable',
meet: 'writable',
- meetLowLevel: 'writable',
meetHighLevel: 'writable',
+ jitsiMeetJS: 'writable',
zoneManager: 'writable',
notificationMessage: 'writable',
nippleManager: 'writable',
@@ -99,7 +98,6 @@ module.exports = {
charactersParts: 'writable',
characterPopIns: 'writable',
capitalize: 'writable',
- generateTURNCredentials: 'writable',
isModalOpen: 'writable',
closeModal: 'writable',
toggleModal: 'writable',
@@ -130,8 +128,6 @@ module.exports = {
relativePositionToCamera: 'writable',
updateViewport: 'writable',
- sendDataToUsers: 'writable',
- sendDataToUsersInZone: 'writable',
BootScene: 'writable',
EditorScene: 'writable',
diff --git a/.vscode/cspell.json b/.vscode/cspell.json
index a5a8e859e..8b1c94bbd 100644
--- a/.vscode/cspell.json
+++ b/.vscode/cspell.json
@@ -49,7 +49,6 @@
"ondataavailable",
"overscroll",
"passwordless",
- "peerjs",
"Phaser's",
"pickable",
"plusplus",
diff --git a/app/package-lock.json b/app/package-lock.json
index 04bef9ef6..64c69f222 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -121,103 +121,11 @@
}
}
},
- "@swc/helpers": {
- "version": "0.3.17",
- "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz",
- "integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==",
- "requires": {
- "tslib": "^2.4.0"
- }
- },
"@tokenizer/token": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
"integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
},
- "@types/body-parser": {
- "version": "1.19.2",
- "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
- "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==",
- "requires": {
- "@types/connect": "*",
- "@types/node": "*"
- }
- },
- "@types/connect": {
- "version": "3.4.35",
- "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
- "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==",
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/express": {
- "version": "4.17.17",
- "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
- "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==",
- "requires": {
- "@types/body-parser": "*",
- "@types/express-serve-static-core": "^4.17.33",
- "@types/qs": "*",
- "@types/serve-static": "*"
- }
- },
- "@types/express-serve-static-core": {
- "version": "4.17.33",
- "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz",
- "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==",
- "requires": {
- "@types/node": "*",
- "@types/qs": "*",
- "@types/range-parser": "*"
- }
- },
- "@types/mime": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz",
- "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA=="
- },
- "@types/node": {
- "version": "18.15.11",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz",
- "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q=="
- },
- "@types/qs": {
- "version": "6.9.7",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
- "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw=="
- },
- "@types/range-parser": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz",
- "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw=="
- },
- "@types/serve-static": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.1.tgz",
- "integrity": "sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==",
- "requires": {
- "@types/mime": "*",
- "@types/node": "*"
- }
- },
- "@types/ws": {
- "version": "8.5.4",
- "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz",
- "integrity": "sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==",
- "requires": {
- "@types/node": "*"
- }
- },
- "accepts": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
- "requires": {
- "mime-types": "~2.1.34",
- "negotiator": "0.6.3"
- }
- },
"agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -256,29 +164,11 @@
"uuid": "^8.3.2"
}
},
- "ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
"append-field": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
},
- "array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
- },
"array-parallel": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/array-parallel/-/array-parallel-0.1.3.tgz",
@@ -455,16 +345,6 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
- "cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
- }
- },
"color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -549,43 +429,16 @@
}
}
},
- "content-disposition": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
- "requires": {
- "safe-buffer": "5.2.1"
- }
- },
"content-type": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="
},
- "cookie": {
- "version": "0.5.0",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
- "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
- },
- "cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
- },
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
- "cors": {
- "version": "2.8.5",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
- "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
- "requires": {
- "object-assign": "^4",
- "vary": "^1"
- }
- },
"crisp-sdk-web": {
"version": "1.0.18",
"resolved": "https://registry.npmjs.org/crisp-sdk-web/-/crisp-sdk-web-1.0.18.tgz",
@@ -621,11 +474,6 @@
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
},
- "data-uri-to-buffer": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
- "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="
- },
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -696,16 +544,6 @@
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- },
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
- },
"end-of-stream": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
@@ -767,11 +605,6 @@
"is-symbol": "^1.0.2"
}
},
- "escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
- },
"escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -782,11 +615,6 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
"integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="
},
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
- },
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
@@ -802,76 +630,6 @@
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
"integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="
},
- "express": {
- "version": "4.18.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
- "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
- "requires": {
- "accepts": "~1.3.8",
- "array-flatten": "1.1.1",
- "body-parser": "1.20.1",
- "content-disposition": "0.5.4",
- "content-type": "~1.0.4",
- "cookie": "0.5.0",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.2.0",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.7",
- "qs": "6.11.0",
- "range-parser": "~1.2.1",
- "safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
- "setprototypeof": "1.2.0",
- "statuses": "2.0.1",
- "type-is": "~1.6.18",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- },
- "dependencies": {
- "body-parser": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
- "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
- "requires": {
- "bytes": "3.1.2",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "on-finished": "2.4.1",
- "qs": "6.11.0",
- "raw-body": "2.5.1",
- "type-is": "~1.6.18",
- "unpipe": "1.0.0"
- }
- },
- "raw-body": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
- "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
- "requires": {
- "bytes": "3.1.2",
- "http-errors": "2.0.0",
- "iconv-lite": "0.4.24",
- "unpipe": "1.0.0"
- }
- }
- }
- },
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -903,15 +661,6 @@
}
}
},
- "fetch-blob": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
- "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
- "requires": {
- "node-domexception": "^1.0.0",
- "web-streams-polyfill": "^3.0.3"
- }
- },
"file-type": {
"version": "16.5.4",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz",
@@ -922,20 +671,6 @@
"token-types": "^4.1.1"
}
},
- "finalhandler": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "2.4.1",
- "parseurl": "~1.3.3",
- "statuses": "2.0.1",
- "unpipe": "~1.0.0"
- }
- },
"follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
@@ -959,24 +694,6 @@
"mime-types": "^2.1.12"
}
},
- "formdata-polyfill": {
- "version": "4.0.10",
- "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
- "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
- "requires": {
- "fetch-blob": "^3.1.2"
- }
- },
- "forwarded": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
- },
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
- },
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -1003,11 +720,6 @@
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
},
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
- },
"get-intrinsic": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
@@ -1161,11 +873,6 @@
"side-channel": "^1.0.4"
}
},
- "ipaddr.js": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
- },
"is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
@@ -1215,11 +922,6 @@
"has-tostringtag": "^1.0.0"
}
},
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
- },
"is-generator-function": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
@@ -1392,11 +1094,6 @@
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
},
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
- },
"meteor-node-stubs": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-1.2.5.tgz",
@@ -2280,16 +1977,6 @@
}
}
},
- "methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
- },
- "mime": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
- },
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
@@ -2350,11 +2037,6 @@
"resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz",
"integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg=="
},
- "negotiator": {
- "version": "0.6.3",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
- },
"nipplejs": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/nipplejs/-/nipplejs-0.10.0.tgz",
@@ -2381,21 +2063,6 @@
"uuid": "8.3.2"
}
},
- "node-domexception": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
- "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="
- },
- "node-fetch": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.1.tgz",
- "integrity": "sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow==",
- "requires": {
- "data-uri-to-buffer": "^4.0.0",
- "fetch-blob": "^3.1.4",
- "formdata-polyfill": "^4.0.10"
- }
- },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -2438,11 +2105,6 @@
"wrappy": "1"
}
},
- "parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
- },
"path": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
@@ -2452,46 +2114,11 @@
"util": "^0.10.3"
}
},
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
- },
"peek-readable": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz",
"integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg=="
},
- "peer": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/peer/-/peer-1.0.0.tgz",
- "integrity": "sha512-fPVtyCKZWVfjbf7XnY7MskhTlu+pBpMvQV81sngT8aXIuT5YF9y9bwIw8y5BlI98DV0NsDpLjow/oemFNvcKkg==",
- "requires": {
- "@types/express": "^4.17.3",
- "@types/ws": "^7.2.3 || ^8.0.0",
- "cors": "^2.8.5",
- "express": "^4.17.1",
- "node-fetch": "^3.3.0",
- "ws": "^7.2.3 || ^8.0.0",
- "yargs": "^17.6.2"
- }
- },
- "peerjs": {
- "version": "1.4.7",
- "resolved": "https://registry.npmjs.org/peerjs/-/peerjs-1.4.7.tgz",
- "integrity": "sha512-dWE2HIGvJO0Hm8lYHJiO/5OWl8xYtGcAuU08To1HMIfhh76ULzkCS3NIQO/PZm4noO1RhaGTkQaQ6sbAss6/Tg==",
- "requires": {
- "@swc/helpers": "^0.3.13",
- "eventemitter3": "^4.0.7",
- "peerjs-js-binarypack": "1.0.1",
- "webrtc-adapter": "^7.7.1"
- }
- },
- "peerjs-js-binarypack": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-1.0.1.tgz",
- "integrity": "sha512-N6aeia3NhdpV7kiGxJV5xQiZZCVEEVjRz2T2C6UZQiBkHWHzUv/oWA4myQLcwBwO8LUoR1KWW5oStvwVesmfCg=="
- },
"phaser": {
"version": "3.55.2",
"resolved": "https://registry.npmjs.org/phaser/-/phaser-3.55.2.tgz",
@@ -2530,15 +2157,6 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
- "proxy-addr": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
- "requires": {
- "forwarded": "0.2.0",
- "ipaddr.js": "1.9.1"
- }
- },
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@@ -2571,11 +2189,6 @@
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
"integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g=="
},
- "range-parser": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
- },
"raw-body": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
@@ -2636,19 +2249,6 @@
"resolved": "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz",
"integrity": "sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA=="
},
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
- },
- "rtcpeerconnection-shim": {
- "version": "1.2.15",
- "resolved": "https://registry.npmjs.org/rtcpeerconnection-shim/-/rtcpeerconnection-shim-1.2.15.tgz",
- "integrity": "sha512-C6DxhXt7bssQ1nHb154lqeL0SXz5Dx4RczXZu2Aa/L1NJFnEVDxFwCBo3fqtuljhHIGceg5JKBV4XJ0gW5JKyw==",
- "requires": {
- "sdp": "^2.6.0"
- }
- },
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -2686,11 +2286,6 @@
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
"integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="
},
- "sdp": {
- "version": "2.12.0",
- "resolved": "https://registry.npmjs.org/sdp/-/sdp-2.12.0.tgz",
- "integrity": "sha512-jhXqQAQVM+8Xj5EjJGVweuEzgtGWb3tmEEpl3CLP3cStInSbVHSg0QWOGQzNq8pSID4JkpeV2mPqlMDLrm0/Vw=="
- },
"semver": {
"version": "7.3.8",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
@@ -2699,37 +2294,6 @@
"lru-cache": "^6.0.0"
}
},
- "send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
- "requires": {
- "debug": "2.6.9",
- "depd": "2.0.0",
- "destroy": "1.2.0",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "2.0.0",
- "mime": "1.6.0",
- "ms": "2.1.3",
- "on-finished": "2.4.1",
- "range-parser": "~1.2.1",
- "statuses": "2.0.1"
- }
- },
- "serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
- "requires": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.3",
- "send": "0.18.0"
- }
- },
"setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -2793,16 +2357,6 @@
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg=="
},
- "string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- }
- },
"string.prototype.trimend": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
@@ -2831,14 +2385,6 @@
"safe-buffer": "~5.2.0"
}
},
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- },
"strip-json-comments": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
@@ -2890,11 +2436,6 @@
"ieee754": "^1.2.1"
}
},
- "tslib": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
- "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ=="
- },
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@@ -2962,35 +2503,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
},
- "utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
- },
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
- "vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
- },
- "web-streams-polyfill": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz",
- "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q=="
- },
- "webrtc-adapter": {
- "version": "7.7.1",
- "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-7.7.1.tgz",
- "integrity": "sha512-TbrbBmiQBL9n0/5bvDdORc6ZfRY/Z7JnEj+EYOD1ghseZdpJ+nF2yx14k3LgQKc7JZnG7HAcL+zHnY25So9d7A==",
- "requires": {
- "rtcpeerconnection-shim": "^1.2.15",
- "sdp": "^2.12.0"
- }
- },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -3024,26 +2541,11 @@
"is-typed-array": "^1.1.9"
}
},
- "wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
- "ws": {
- "version": "8.13.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
- "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA=="
- },
"xml2js": {
"version": "0.4.19",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
@@ -3063,34 +2565,10 @@
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
},
- "y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
- },
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
- },
- "yargs": {
- "version": "17.7.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz",
- "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
- "requires": {
- "cliui": "^8.0.1",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.3",
- "y18n": "^5.0.5",
- "yargs-parser": "^21.1.1"
- }
- },
- "yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
}
}
}
diff --git a/app/package.json b/app/package.json
index 668057fed..bffe63282 100644
--- a/app/package.json
+++ b/app/package.json
@@ -30,8 +30,6 @@
"multer": "1.4.5-lts.1",
"nipplejs": "^0.10.0",
"node-cron": "^3.0.2",
- "peer": "^1.0.0",
- "peerjs": "^1.4.7",
"phaser": "^3.55.2"
}
}
diff --git a/app/settings-dev.json b/app/settings-dev.json
index 6760801d1..efbdd3138 100644
--- a/app/settings-dev.json
+++ b/app/settings-dev.json
@@ -33,27 +33,10 @@
"pinchDelta": 4
},
- "peer": {
- "answerMaxAttempt": 5,
- "answerDelayBetweenAttempt": 750,
- "avatarAPI": "https://source.unsplash.com/320x240/?[user_avatar]&sig=[user_id]",
- "callDelay": 250,
- "delayBeforeClosingCall": 1000,
- "sounds": {
- "hangUp": {
- "file": "webrtc-out.mp3",
- "volume": 1
- },
- "incomingCall": {
- "file": "webrtc-in.mp3",
- "volume": 1
- }
- }
- },
-
"meet": {
"serverURL": "meet.jit.si",
"roomDefaultName": "lemverse-test",
+ "avatarAPI": "https://source.unsplash.com/320x240/?[user_avatar]&sig=[user_id]",
"configOverwrite": {}
},
@@ -241,34 +224,8 @@
"identifier": "",
"iss": "",
"keyid": "",
- "sub": ""
- },
-
- "peer": {
- "path": "/peer",
- "client": {
- "url": "localhost",
- "port": 7010,
- "credentialDuration": 86400,
- "secret": "",
- "config": {
- "iceServers": [
- {
- "urls": "stun:stun.l.google.com:19302"
- }
- ],
- "iceTransportPolicy": "all",
- "sdpSemantics": "unified-plan"
- }
- },
- "server": {
- "start": true,
- "port": 7010,
- "key": "peerjs",
- "alive_timeout": 5000,
- "expire_timeout": 5000,
- "allow_discovery": true
- }
+ "sub": "",
+ "salt": ""
},
"s3": {},
diff --git a/core/client/helpers.js b/core/client/helpers.js
index 81b266f51..fd619648e 100644
--- a/core/client/helpers.js
+++ b/core/client/helpers.js
@@ -29,7 +29,6 @@ eventTypes = Object.freeze({
onMeetEnded: 'onMeetEnded',
onMenuOptionSelected: 'onMenuOptionSelected',
onMenuOptionUnselected: 'onMenuOptionUnselected',
- onPeerDataReceived: 'onPeerDataReceived',
onMediaStreamStateChanged: 'onMediaStreamStateChanged',
onTileAdded: 'onTileAdded',
onTileChanged: 'onTileChanged',
@@ -47,6 +46,7 @@ eventTypes = Object.freeze({
beforeSendingMessage: 'beforeSendingMessage',
afterSendingMessage: 'afterSendingMessage',
consoleClosed: 'consoleClosed',
+ onUserPropertyUpdated: 'onUserPropertyUpdated',
})
toggleUserProperty = (propertyName, value) => {
@@ -79,14 +79,16 @@ toggleUserProperty = (propertyName, value) => {
}
}
- if (typeof value === 'boolean')
- Meteor.users.update(user._id, {
- $set: { [`profile.${propertyName}`]: !!value },
- })
- else
- Meteor.users.update(user._id, {
- $set: { [`profile.${propertyName}`]: !user.profile[propertyName] },
+ const propertyValue = typeof value === 'boolean' ? !!value : !user.profile[propertyName]
+
+ Meteor.users.update(user._id, {
+ $set: { [`profile.${propertyName}`]: propertyValue },
+ })
+ window.dispatchEvent(
+ new CustomEvent(eventTypes.onUserPropertyUpdated, {
+ detail: { propertyName, propertyValue },
})
+ )
}
relativePositionToCamera = (position, camera) => {
@@ -193,27 +195,6 @@ const formatURLs = (text, shortName = false) =>
return `${linkName}`
})
-sendDataToUsers = (type, data, emitterId, userIds = []) => {
- let targets = [...new Set(userIds)]
- targets = targets.filter((target) => target !== Meteor.userId())
- if (!targets.length) throw new Error('no-targets')
-
- return networkManager.sendData(targets, { type, emitter: emitterId, data })
-}
-
-sendDataToUsersInZone = (type, data, emitterId) => {
- const user = Meteor.user()
- const usersInZone = zoneManager.usersInZone(zoneManager.currentZone(user))
- const userInZoneIds = usersInZone.map((u) => u._id)
- if (!userInZoneIds.length) throw new Error('no-targets')
-
- return networkManager.sendData(userInZoneIds, {
- type,
- emitter: emitterId,
- data,
- })
-}
-
kebabCase = (string) =>
string
.replace(/([a-z])([A-Z])/g, '$1-$2')
@@ -265,7 +246,7 @@ const replaceTextVars = (text) =>
})
generateRandomAvatarURLForUser = (user) =>
- Meteor.settings.public.peer.avatarAPI
+ Meteor.settings.public.meet.avatarAPI
.replace('[user_id]', encodeURI(user._id || 'guest'))
.replace('[user_name]', encodeURI(user.profile.name || 'guest'))
.replace('[user_avatar]', encodeURI(user.profile.avatar || 'cat'))
diff --git a/core/client/lemverse.hbs.html b/core/client/lemverse.hbs.html
index 2302da45e..cc964617c 100644
--- a/core/client/lemverse.hbs.html
+++ b/core/client/lemverse.hbs.html
@@ -20,9 +20,9 @@
{{#if and (and guest (not onboarding)) allowFormLogin}} {{> formAccount visible=loading}} {{else if onboarding}}
- {{> userOnboarding}} {{else if and (not guest not onboarding)}} {{> editToolbox}} {{/if}} {{ > stream }} {{>
- messagesPopup}} {{#each mainModule in mainModules}} {{> Template.dynamic template=mainModule}} {{/each}} {{>
- modalContainer }}
+ {{> userOnboarding}} {{else if and (not guest not onboarding)}} {{> editToolbox}} {{/if}} {{> messagesPopup}}
+ {{#each mainModule in mainModules}} {{> Template.dynamic template=mainModule}} {{/each}} {{> modalContainer }}
+ {{> meetLowLevel}}
{{#each module in modules}} {{> Template.dynamic template=module}} {{/each}}
diff --git a/core/client/lemverse.js b/core/client/lemverse.js
index 1c58c4f11..259699090 100644
--- a/core/client/lemverse.js
+++ b/core/client/lemverse.js
@@ -103,7 +103,6 @@ Template.lemverse.onCreated(function () {
window.addEventListener('beforeunload', () => {
toggleUserProperty('shareScreen', false)
- peer.destroy()
})
initAppColor()
@@ -159,8 +158,6 @@ Template.lemverse.onCreated(function () {
const user = Meteor.user()
if (!user) return
if (user.profile.guest && !guestAllowed(permissionTypes.talkToUsers)) return
-
- peer.createMyPeer()
})
})
@@ -170,9 +167,6 @@ Template.lemverse.onCreated(function () {
const user = Meteor.user()
if (!user) return
if (user.profile.guest && !guestAllowed(permissionTypes.talkToUsers)) return
-
- if (status === 'connected') peer.createMyPeer()
- else peer.peerInstance?.disconnect()
})
})
@@ -235,10 +229,6 @@ Template.lemverse.onCreated(function () {
const meetingRoomService = meetingRoom.getMeetingRoomService()
if (user.profile.shareScreen) meetingRoomService.shareScreen()
else meetingRoomService.unshareScreen()
- } else if (user.profile.shareScreen) {
- await userStreams.createScreenStream()
- userStreams.screen(true)
- userProximitySensor.callProximityStartedForAllNearUsers()
} else {
userStreams.screen(false)
}
@@ -418,7 +408,6 @@ Template.lemverse.onCreated(function () {
removed: (user) => {
userManager.onDocumentRemoved(user)
userProximitySensor.removeNearUser(user)
- lp.defer(() => peer.close(user._id, 0, 'user-disconnected'))
},
})
if (onLoaded) onLoaded()
@@ -429,7 +418,6 @@ Template.lemverse.onCreated(function () {
loadUsers(() => {
log('loading level: all users loaded')
- peer.init()
// Load entities
log(`loading level: loading entities`)
diff --git a/core/client/network-manager.js b/core/client/network-manager.js
index c4a88f26a..609d8fa6a 100644
--- a/core/client/network-manager.js
+++ b/core/client/network-manager.js
@@ -72,11 +72,6 @@ const networkManager = {
this.lastUserUpdate = character.lwOriginDate
},
- // A simple wrapper: later we will use something else than peerjs
- async sendData(userIds, data) {
- return peer.sendData(userIds, data)
- },
-
sendPlayerNewState(state) {
this.throttledSendPlayerState(state)
},
diff --git a/core/client/peer.js b/core/client/peer.js
deleted file mode 100644
index 2a78e43be..000000000
--- a/core/client/peer.js
+++ /dev/null
@@ -1,758 +0,0 @@
-import Peer from 'peerjs'
-import audioManager from './audio-manager'
-import meetingRoom from './meeting-room'
-import { canAnswerCall, meteorCallWithPromise } from './helpers'
-import { guestAllowed, permissionTypes } from '../lib/misc'
-
-const debug = (text, meta) => {
- if (!Meteor.user({ fields: { 'options.debug': 1 } })?.options?.debug) return
- log(text, meta)
-}
-
-const callAction = Object.freeze({
- open: 0,
- close: 1,
-})
-
-peer = {
- calls: {},
- waitingCallActions: {},
- callStartDates: {},
- remoteCalls: {},
- peerInstance: undefined,
- peerLoading: false,
- remoteStreamsByUsers: new ReactiveVar([]),
- sensorEnabled: true,
- enabled: true,
- lockedCalls: {},
- reconnect: {
- autoReconnectOnClose: true,
- delayBetweenAttempt: 250,
- },
- securityCheckInterval: 2000,
-
- init() {
- window.addEventListener(eventTypes.onUsersComeCloser, (e) => {
- const { users } = e.detail
- peer.onProximityStarted(users)
- })
-
- window.addEventListener(eventTypes.onUsersMovedAway, (e) => {
- const { users } = e.detail
- peer.onProximityEnded(users)
- })
- this.enable()
-
- Tracker.autorun(() => {
- const user = Meteor.user({ fields: { 'status.idle': 1 } })
- if (!user) return
-
- this.enableSensor(!user.status.idle)
- })
-
- // For security reasons we periodically check that the users in discussion are still close to the calling users
- window.setInterval(() => {
- const callEntries = Object.entries(this.remoteCalls)
- if (!callEntries.length) return
-
- callEntries.forEach((entry) => {
- // Keep the call if user is near or in follow mode
- const _id = entry[1].metadata.userId
- if (userProximitySensor.isUserNear({ _id }) || this.lockedCalls[_id]) return
- this.closeCall(_id, 0, 'security-user-far')
- })
- }, this.securityCheckInterval)
-
- // Listen device connection/disconnection to update peers
- navigator.mediaDevices.addEventListener('devicechange', async () => {
- if (!this.hasActiveStreams()) return
-
- const constraints = userStreams.getStreamConstraints(streamTypes.main)
- constraints.forceNew = true
-
- const stream = await userStreams.requestUserMedia(constraints)
- if (!stream) {
- lp.notif.error(`unable to get a valid stream`)
- return
- }
-
- peer.updatePeersStream(stream, streamTypes.main)
- })
- },
-
- enable() {
- this.enabled = true
- },
-
- disable() {
- this.enabled = false
- this.closeAll()
- },
-
- closeAll() {
- debug('peer.closeAll: start')
- _.each(this.calls, (call) =>
- this.close(call.peer, Meteor.settings.public.peer.delayBeforeClosingCall, 'close-all')
- )
- },
-
- closeCall(userId, origin) {
- debug(`closeCall: start (${origin})`, { userId })
-
- let activeCallsCount = 0
- const _close = (remote, user, type) => {
- const callsSource = remote ? this.remoteCalls : this.calls
- const call = callsSource[`${user}-${type}`]
- if (call) {
- activeCallsCount++
- call.close()
- }
-
- delete callsSource[`${user}-${type}`]
- }
-
- this.unlockCall(userId, true)
- _close(false, userId, streamTypes.main)
- _close(false, userId, streamTypes.screen)
- _close(true, userId, streamTypes.main)
- _close(true, userId, streamTypes.screen)
- this.cancelWaitingCallAction(userId)
-
- const debutText = activeCallsCount ? 'closeCall: call was active' : 'closeCall: call was inactive'
- debug(debutText, { sourceAmount: activeCallsCount })
-
- let streamsByUsers = this.remoteStreamsByUsers.get()
- streamsByUsers.map((usr) => {
- if (usr._id === userId) {
- delete usr.main.srcObject
- delete usr.screen.srcObject
- delete usr.waitingCallAnswer
- }
-
- return usr
- })
-
- // We clean up remoteStreamsByUsers table by deleting all the users who have neither webcam or screen sharing active
- streamsByUsers = streamsByUsers.filter(
- (usr) => usr.main.srcObject !== undefined || usr.screen.srcObject !== undefined || usr.waitingCallAnswer
- )
- this.remoteStreamsByUsers.set(streamsByUsers)
-
- if (!this.hasActiveStreams()) {
- userStreams.destroyStream(streamTypes.main)
- }
-
- if (this.callStartDates[userId]) {
- const duration = (Date.now() - this.callStartDates[userId]) / 1000
- Meteor.call('analyticsDiscussionEnd', {
- peerUserId: userId,
- duration,
- usersAttendingCount: this.getCallCount(),
- })
- delete this.callStartDates[userId]
- }
-
- $(`.js-video-${userId}-user`).remove()
- debug('closeCall: call closed successfully', { userId })
-
- if (!activeCallsCount) return
-
- const { file, volume } = Meteor.settings.public.peer.sounds.hangUp
- audioManager.play(file, volume)
- },
-
- close(userId, timeout = 0, origin = null) {
- debug(`close: start (${origin})`, { userId })
- if (this.isCallInState(userId, callAction.close)) {
- debug(`close: call already closing (action ignored)`, { userId })
- return
- }
-
- this.cancelWaitingCallAction(userId)
- this.waitingCallActions[userId] = {
- timer: setTimeout(() => {
- this.cancelWaitingCallAction(userId)
- this.closeCall(userId, origin)
- }, timeout),
- action: callAction.close,
- }
- },
-
- createPeerCall(peer, user, stream, streamType) {
- debug(`createPeerCall: calling remote user`, {
- user: user._id,
- streamType,
- })
- if (!stream) {
- error(`createPeerCall: stream is undefined`, { user, stream })
- return
- }
-
- if (this.calls[`${user._id}-${streamType}`]) {
- debug(`createPeerCall: creation cancelled (call already started)`)
- return
- }
-
- if (!canAnswerCall(user)) {
- debug(`createPeerCall: creation cancelled (user is too far)`)
- this.close(user._id, 0, 'far-user')
- return
- }
-
- const call = peer.call(user._id, stream, {
- metadata: { userId: Meteor.userId(), type: streamType },
- })
- if (!call) {
- error(`createPeerCall: an error occured during call creation (peerjs error)`)
- this.close(user._id, 0, 'peer-error')
- return
- }
-
- // update html element with the last stream instance
- this.calls[`${user._id}-${streamType}`] = call
- this.createOrUpdateRemoteStream(user, streamType)
-
- // ensures peers are using last stream & tracks available
- this.updatePeersStream(stream, streamType)
-
- debug(`createPeerCall: call in progress`, {
- user: user._id,
- streamType,
- })
- },
-
- async createPeerCalls(user) {
- const { shareAudio, shareScreen, shareVideo } = Meteor.user().profile
-
- if (!this.calls[`${user._id}-${streamTypes.main}`] && !this.calls[`${user._id}-${streamTypes.screen}`]) {
- const { file, volume } = Meteor.settings.public.peer.sounds.incomingCall
- audioManager.play(file, volume)
-
- if (!this.callStartDates[user._id]) {
- this.callStartDates[user._id] = Date.now()
- Meteor.call('analyticsDiscussionAttend', {
- peerUserId: user._id,
- usersAttendingCount: this.getCallCount(),
- })
- if (this.expiration && parseInt(Date.now() / 1000, 10) > this.expiration) {
- Meteor.call('analyticsCredentialsExpired')
- debug('TURN credentials expired')
- }
- }
- }
-
- const peer = await this.getPeer()
- if (!peer) {
- debug(`createPeerCalls: peer not created`)
- return
- }
-
- if (shareAudio || shareVideo)
- userStreams.createStream().then((stream) => this.createPeerCall(peer, user, stream, streamTypes.main))
- if (shareScreen)
- userStreams
- .createScreenStream()
- .then((stream) => this.createPeerCall(peer, user, stream, streamTypes.screen))
- },
-
- destroy() {
- this.closeAll()
- userStreams.destroyStream(streamTypes.main)
- this.peerInstance?.destroy()
- this.remoteStreamsByUsers.set([])
- delete this.peerInstance
- },
-
- updatePeersStream(stream, type) {
- debug('updatePeersStream: start', { stream, type })
-
- const callEntries = Object.entries(this.calls)
-
- if (type === streamTypes.main) {
- debug(`updatePeersStream: main stream ${stream.id}`, { stream })
- const audioTrack = stream.getAudioTracks()[0]
- const videoTrack = stream.getVideoTracks()[0]
-
- // note: to add a track it is necessary to renegotiate the connection with the remote user (https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addTrack)
- callEntries.forEach(([key, call]) => {
- if (key.indexOf('-screen') !== -1) return
- const senders = call.peerConnection.getSenders()
-
- const existingSenderAudioTrack = senders.find((sender) => sender.track.kind === 'audio')
- if (existingSenderAudioTrack) {
- if (audioTrack) existingSenderAudioTrack.replaceTrack(audioTrack)
- else call.peerConnection.removeTrack(existingSenderAudioTrack)
- } else if (audioTrack) call.peerConnection.addTrack(audioTrack)
-
- const existingSenderVideoTrack = senders.find((sender) => sender.track.kind === 'video')
- if (existingSenderVideoTrack) {
- if (videoTrack) existingSenderVideoTrack.replaceTrack(videoTrack)
- else call.peerConnection.removeTrack(existingSenderVideoTrack)
- } else if (videoTrack) call.peerConnection.addTrack(videoTrack)
-
- if (!existingSenderAudioTrack || !existingSenderVideoTrack)
- debug(`updatePeersStream: stream main track added for user`, { key })
- else debug(`updatePeersStream: stream main track updated for user`, { key })
- })
- } else if (type === streamTypes.screen) {
- debug(`updatePeersStream: screen share stream ${stream.id}`, {
- stream,
- })
- const screenTrack = stream.getVideoTracks()[0]
-
- callEntries.forEach(([key, call]) => {
- if (key.indexOf('-screen') === -1) return
- const senders = call.peerConnection.getSenders()
- let trackUpdated = false
-
- senders.forEach((sender) => {
- if (sender.track.id === screenTrack.id || sender.track.kind !== 'video') return
- sender.replaceTrack(screenTrack)
- trackUpdated = true
- })
-
- if (trackUpdated) debug(`updatePeersStream: stream main track updated for user ${key}`)
- })
- }
- },
-
- onProximityStarted(nearUsers) {
- if (!this.isEnabled() && this.sensorEnabled) return
-
- nearUsers.forEach((nearUser) => {
- if (nearUser.profile.guest && !guestAllowed(permissionTypes.talkToUsers)) return
- if (this.isCallInState(nearUser._id, callAction.open)) return
- this.cancelWaitingCallAction(nearUser._id)
-
- const zone = zoneManager.currentZone(nearUser)
- if (zone?.disableCommunications) {
- lp.notif.warning(
- `${nearUser.profile.name} isn't available at the moment.
Leave him a voice message by pressing "P"`
- )
- return
- }
-
- this.waitingCallActions[nearUser._id] = {
- timer: setTimeout(() => {
- this.cancelWaitingCallAction(nearUser._id)
- this.createPeerCalls(nearUser)
- }, Meteor.settings.public.peer.callDelay),
- action: callAction.open,
- }
- })
- },
-
- onProximityEnded(users) {
- users.forEach((user) => {
- if (this.lockedCalls[user._id]) return
- this.close(user._id, Meteor.settings.public.peer.delayBeforeClosingCall, 'proximity-ended')
- })
- },
-
- isCallInState(userId, state) {
- return this.waitingCallActions[userId]?.action === state
- },
-
- cancelWaitingCallAction(userId) {
- if (!this.waitingCallActions[userId]) return
- clearTimeout(this.waitingCallActions[userId].timer)
- delete this.waitingCallActions[userId]
- },
-
- async sendData(userIds, data) {
- userIds = userIds.filter(Boolean) // remove falsy values
- if (!userIds.length) throw new Error(`no users targeted`)
-
- const peer = await this.getPeer()
- userIds.forEach((userId) => {
- try {
- const connection = peer.connect(userId)
-
- connection.on('open', () => {
- connection.send(data)
-
- // Not sure if we must close the connection for now
- setTimeout(() => connection.close(), 500)
- })
-
- connection.on('error', () => {
- const user = Meteor.users.findOne(userId)
- if (user) lp.notif.warning(`${user.profile.name} was unavailable`)
- else lp.notif.warning(`${userId} is offline`)
- })
- } catch (err) {
- const user = Meteor.users.findOne(userId)
- lp.notif.error(`An error has occured during connection with ${user?.profile.name || userId}`)
- }
- })
-
- return userIds.length
- },
-
- onStreamSettingsChanged(changedUser) {
- const streamsByUsers = this.remoteStreamsByUsers.get()
- const streamsCurrentUser = streamsByUsers.find((user) => user._id === changedUser._id)
- if (!streamsCurrentUser || !streamsCurrentUser.screen.srcObject) return
-
- if (!changedUser.profile.shareScreen) {
- delete streamsCurrentUser.screen.srcObject
- this.remoteStreamsByUsers.set(streamsByUsers)
- }
- },
-
- initStreamMonitoring(remoteUserId, streamType) {
- const streamsByUsers = this.remoteStreamsByUsers.get()
- const stream = streamsByUsers.find((usr) => usr._id === remoteUserId)
- if (streamType === 'main' && !stream.videoStartedAt) {
- stream.videoStartedAt = Date.now()
- debug(`Video stream ${stream.streamId} starting...`)
- Meteor.call('analyticsStreamStarted', {
- streamId: stream.streamId,
- peerUserId: remoteUserId,
- kind: 'video',
- })
- }
- if (streamType === 'main' && !stream.audioStartedAt) {
- stream.audioStartedAt = Date.now()
- debug(`Audio stream ${stream.streamId} starting...`)
- Meteor.call('analyticsStreamStarted', {
- streamId: stream.streamId,
- peerUserId: remoteUserId,
- kind: 'audio',
- })
- }
- if (streamType === 'screen' && !stream.screenStartedAt) {
- stream.screenStartedAt = Date.now()
- debug(`Screen stream ${stream.streamId} starting...`)
- Meteor.call('analyticsStreamStarted', {
- streamId: stream.streamId,
- peerUserId: remoteUserId,
- kind: 'screen',
- })
- }
- },
-
- monitorStreams(userStream, remoteStream, streamType) {
- if (streamType === 'main') {
- const videoTrack = remoteStream.getVideoTracks()[0]
- const audioTrack = remoteStream.getAudioTracks()[0]
- if (videoTrack) {
- videoTrack.onunmute = () => {
- if (!userStream.videoEstablished) {
- const elapsed = Date.now() - userStream.videoStartedAt
- debug(`Video stream ${userStream.streamId} established in ${elapsed} ms`)
- Meteor.call('analyticsStreamEstablished', {
- streamId: userStream.streamId,
- peerUserId: userStream._id,
- kind: 'video',
- elapsed,
- })
- userStream.videoEstablished = true
- }
- }
- } else {
- debug('Video track is missing!')
- Meteor.call('analyticsMissingTrack', {
- streamId: userStream.streamId,
- peerUserId: userStream._id,
- kind: 'video',
- })
- }
- if (audioTrack) {
- audioTrack.onunmute = () => {
- if (!userStream.audioEstablished) {
- const elapsed = Date.now() - userStream.audioStartedAt
- debug(`Audio stream ${userStream.streamId} established in ${elapsed} ms`)
- Meteor.call('analyticsStreamEstablished', {
- streamId: userStream.streamId,
- peerUserId: userStream._id,
- kind: 'audio',
- elapsed,
- })
- userStream.audioEstablished = true
- }
- }
- } else {
- debug('Audio track is missing!')
- Meteor.call('analyticsMissingTrack', {
- streamId: userStream.streamId,
- peerUserId: userStream._id,
- kind: 'audio',
- })
- }
- } else if (streamType === 'screen') {
- const videoTrack = remoteStream.getVideoTracks()[0]
- if (videoTrack) {
- videoTrack.onunmute = () => {
- if (!userStream.screenEstablished) {
- const elapsed = Date.now() - userStream.screenStartedAt
- debug(`Screen sharing stream ${userStream.streamId} established in ${elapsed} ms`)
- Meteor.call('analyticsStreamEstablished', {
- streamId: userStream.streamId,
- peerUserId: userStream._id,
- kind: 'screen',
- elapsed,
- })
- userStream.screenEstablished = true
- }
- }
- } else {
- debug('Screen sharing video track is missing!')
- Meteor.call('analyticsMissingTrack', {
- streamId: userStream.streamId,
- peerUserId: userStream._id,
- kind: 'screen',
- })
- }
- }
- },
-
- createOrUpdateRemoteStream(user, streamType, remoteStream = null) {
- const streamsByUsers = this.remoteStreamsByUsers.get()
-
- if (!streamsByUsers.find((usr) => usr._id === user._id)) {
- streamsByUsers.push({
- _id: user._id,
- main: {},
- screen: {},
- waitingCallAnswer: true,
- streamId: Random.id(),
- videoEstablished: false,
- audioEstablished: false,
- screenEstablished: false,
- })
- }
-
- if (remoteStream) {
- streamsByUsers.map((usr) => {
- if (usr._id === user._id) {
- delete usr.waitingCallAnswer
- usr[streamType] = {}
- usr[streamType].srcObject = remoteStream
- this.monitorStreams(usr, remoteStream, streamType)
- }
-
- return usr
- })
- }
-
- this.remoteStreamsByUsers.set(streamsByUsers)
- },
-
- answerCall(remoteCall) {
- const remoteUserId = remoteCall.metadata?.userId
- debug(`answerCall: start`, {
- userId: remoteUserId,
- type: remoteCall.metadata.type,
- })
- if (!this.enabled) {
- debug(`answerCall: peer is disabled`)
- return false
- }
-
- if (!remoteUserId) {
- debug(`answerCall: incomplete metadata for the remote call`)
- return false
- }
- const remoteUser = Meteor.users.findOne({ _id: remoteUserId })
- if (!remoteUser) {
- debug(`answerCall: user not found "${remoteUserId}"`)
- return false
- }
-
- // Send global notification
- sendEvent('proximity-started', { user: remoteUser })
-
- // answer the call
- remoteCall.answer()
-
- const callIdentifier = `${remoteUserId}-${remoteCall.metadata.type}`
- this.remoteCalls[callIdentifier] = remoteCall
-
- // show the remote call with an empty stream
- this.createOrUpdateRemoteStream(remoteUser, remoteCall.metadata.type, null, true)
- this.initStreamMonitoring(remoteUserId, remoteCall.metadata.type)
-
- // update call's with stream received
- remoteCall.on('stream', (stream) => {
- debug(`remoteCall: received stream`, {
- userId: remoteUserId,
- type: remoteCall.metadata.type,
- stream: stream.id,
- })
- this.createOrUpdateRemoteStream(remoteUser, remoteCall.metadata.type, stream)
- })
-
- remoteCall.on('close', () => {
- debug(`remoteCall: closed`, {
- userId: remoteUserId,
- type: remoteCall.metadata.type,
- })
- this.close(remoteUserId, 0, 'peerjs-event')
- })
-
- return true
- },
-
- async getPeer() {
- debug('getPeer: start')
- if (this.isPeerValid(this.peerInstance)) {
- debug('getPeer: return peer instanceβ¦', {
- instance: this.peerInstance,
- })
- return this.peerInstance
- }
-
- if (this.peerInstance?.disconnected) {
- let reconnected = true
- try {
- debug('getPeer: peer disconnected, reconnectingβ¦')
- this.peerInstance.reconnect()
- } catch (err) {
- reconnected = false
- }
-
- // peerjs reconnect doesn't offer a promise or callback so we have to wait a certain time until the reconnection is done
- if (reconnected) {
- try {
- debug('getPeer: reconnected, waiting for instance')
- await waitFor(() => this.isPeerValid(this.peerInstance), 5, 250)
- } catch {
- this.destroy()
- lp.notif.error('Unable to reconnect to the peer server')
- }
-
- return this.peerInstance
- }
- }
-
- if (!this.peerInstance && this.peerLoading) {
- try {
- debug('getPeer: loading, waiting for instance')
- await waitFor(() => this.peerInstance !== undefined, 5, 250)
- } catch {
- this.destroy()
- lp.notif.error('Unable to get a valid peer instance')
- }
-
- return this.peerInstance
- }
-
- debug('getPeer: peer invalid, creating new peerβ¦')
- this.peerInstance = undefined
- this.peerLoading = false
-
- return this.createMyPeer().catch((error) => lp.notif.error(error.message))
- },
-
- async createMyPeer(skipConfig = false) {
- if (this.isPeerValid(this.peerInstance)) return this.peerInstance
-
- const user = Meteor.user()
- if (!user) throw new Error(`an user is required to create a peer`)
- if (user.profile.guest && !guestAllowed(permissionTypes.talkToUsers))
- throw new Error(`You need an account to talk to other users`)
-
- this.peerLoading = true
- const result = await meteorCallWithPromise('getPeerConfig')
-
- const { port, url: host, path, config, expiration } = result
-
- const peerConfig = {
- debug: user.options?.debug ? 2 : 0,
- host,
- port,
- path,
- config,
- }
-
- if (skipConfig) delete peerConfig.config
- if (this.peerInstance) this.destroy()
- this.peerInstance = new Peer(user._id, peerConfig)
- this.expiration = expiration
- this.peerLoading = false
-
- debug(`createMyPeer: created`, { peerInstanceId: this.peerInstance.id })
-
- // force reconnect
- this.peerInstance.on('disconnected', () => this.getPeer())
-
- this.peerInstance.on('connection', (connection) =>
- connection.on('data', (data) => userManager.onPeerDataReceived(data))
- )
-
- this.peerInstance.on('close', () => {
- debug('createMyPeer: peer closed')
- this.peerInstance = undefined
- })
-
- this.peerInstance.on('error', (peerErr) => {
- if (['server-error', 'network'].includes(peerErr.type) && this.peerInstance.disconnected)
- this.peerInstance.reconnect()
- else if (peerErr.type === 'unavailable-id')
- lp.notif.error(
- `It seems that ${Meteor.settings.public.lp.product} is already open in another tab (unavailable-id)`
- )
- else if (peerErr.type === 'peer-unavailable') {
- const userId = peerErr.message.split(' ').pop()
- const userUnavailable = Meteor.users.findOne(userId)
- lp.notif.warning(`User ${userUnavailable?.profile.name || userId} was unavailable`)
- } else debug(`peer error ${peerErr.type}`, peerErr)
- })
-
- this.peerInstance.on('call', (remoteCall) => {
- debug(`Incoming call`, { userId: remoteCall.metadata.userId })
- if (meetingRoom.isOpen()) {
- debug(`Call ignored (meet is open)`, {
- userId: remoteCall.metadata.userId,
- type: remoteCall.metadata.type,
- })
- return
- }
-
- this.answerCall(remoteCall)
- })
-
- return this.peerInstance
- },
-
- lockCall(userId, notify = false) {
- if (notify && !this.lockedCalls[userId])
- this.sendData([userId], {
- type: 'followed',
- emitter: Meteor.userId(),
- })
- this.lockedCalls[userId] = true
- },
-
- unlockCall(userId, notify = false) {
- if (notify && this.lockedCalls[userId])
- this.sendData([userId], {
- type: 'unfollowed',
- emitter: Meteor.userId(),
- })
- delete this.lockedCalls[userId]
- },
-
- getCallCount() {
- return Object.keys(this.callStartDates).length
- },
-
- hasActiveStreams() {
- return this.remoteStreamsByUsers.get().length
- },
-
- isPeerValid(peer) {
- return peer?.id && !peer.disconnected
- },
-
- enableSensor(value) {
- if (value === this.sensorEnabled) return
- this.sensorEnabled = value
- if (this.sensorEnabled) userProximitySensor.callProximityStartedForAllNearUsers()
- },
-
- isEnabled() {
- return this.enabled && !meetingRoom.isOpen()
- },
-}
diff --git a/core/client/scenes/scene-world.js b/core/client/scenes/scene-world.js
index 56235bf95..f69113342 100644
--- a/core/client/scenes/scene-world.js
+++ b/core/client/scenes/scene-world.js
@@ -255,7 +255,6 @@ WorldScene = new Phaser.Class({
userManager.destroy()
zoneManager.destroy()
userProximitySensor.callProximityEndedForAllNearUsers()
- peer.closeAll()
Session.set('sceneWorldReady', false)
},
diff --git a/core/client/ui/remote-stream.hbs.html b/core/client/ui/remote-stream.hbs.html
deleted file mode 100644
index 97ef5d58c..000000000
--- a/core/client/ui/remote-stream.hbs.html
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-
{{name}}
-
- {{#unless mediaState.shareAudio}}
-
- {{/unless}} {{#unless mediaState.shareVideo}}
-

- {{/unless}} {{#if (and hasMainStream (not isWebcamFullScreen))}} {{> webcam remoteUser=remoteUser}} {{/if}}
-
- {{#if hasScreenStream}}
-
{{> screenshare remoteUser=remoteUser}}
- {{/if}}
-
-
-
-
-
-
-
-
-
-
-
-
- {{#modal}}
-
-
- {{#if eq screenType "screenshare" }} {{> screenshare remoteUser=remoteUser}} {{else if eq screenType
- "webcam"}} {{> webcam remoteUser=remoteUser}} {{/if}}
-
-
- {{#if eq screenType "screenshare" }} {{> screenshare remoteUser=remoteUser}} {{else if eq screenType
- "webcam"}} {{> webcam remoteUser=remoteUser}} {{/if}}
-
-
- {{/modal}}
-
diff --git a/core/client/ui/remote-stream.js b/core/client/ui/remote-stream.js
deleted file mode 100644
index d0c0a5e28..000000000
--- a/core/client/ui/remote-stream.js
+++ /dev/null
@@ -1,134 +0,0 @@
-const maxAttempt = 10
-const delayBetweenAttempt = 2000 // in ms
-
-const checkMediaAvailable = (template, type) => {
- const { remoteUser } = template.data
- if (!remoteUser._id) {
- log(`Missing user id in template data`)
- return
- }
-
- const remoteUserStream = peer.remoteStreamsByUsers.get().find((usr) => usr._id === remoteUser._id)
- if (!remoteUserStream) {
- log(`Stop retry to get ${remoteUser.name}'s ${type}, ${remoteUser.name} is too far`)
- return
- }
-
- const source = type === streamTypes.screen ? remoteUserStream.screen?.srcObject : remoteUserStream.main?.srcObject
- if (source) {
- template.firstNode.srcObject = source
- template.firstNode.play().catch(() => {
- error(
- `unable to player remote user's media: playback interrupted (${remoteUserStream._id}) : ${template.attempt}`
- )
- setTimeout(() => checkMediaAvailable(template, type), delayBetweenAttempt)
- })
- } else if (template.attempt < maxAttempt) {
- template.attempt++
- log(`Tried to get ${remoteUserStream.name}'s ${type} and failed, attempt : ${template.attempt}`)
- setTimeout(() => checkMediaAvailable(template, type), delayBetweenAttempt)
- } else {
- error(`unable to get user's ${type}`)
- }
-}
-
-Template.webcam.onRendered(function () {
- this.attempt = 1
- checkMediaAvailable(this, 'video-audio')
-})
-
-Template.webcam.onDestroyed(function () {
- destroyVideoSource(this.find('video'))
-})
-
-Template.screenshare.onRendered(function () {
- this.attempt = 1
- checkMediaAvailable(this, 'screen')
-})
-
-Template.screenshare.onDestroyed(function () {
- destroyVideoSource(this.find('video'))
-})
-
-Template.remoteStream.onDestroyed(() => {
- if (!isModalOpen()) game.scene.getScene('WorldScene')?.enableMouse(true)
- Session.set('modal', null)
-})
-
-Template.remoteStream.onRendered(function () {
- const avatarDomElement = this.firstNode.querySelector('.avatar')
- if (!avatarDomElement) return
-
- avatarDomElement.onerror = (event) => {
- event.target.src = Meteor.settings.public.avatarFallback
- }
-})
-
-Template.remoteStream.helpers({
- mediaState() {
- const fields = { 'profile.shareAudio': 1, 'profile.shareVideo': 1 }
- const user = Meteor.users.findOne(this.remoteUser._id, { fields })
-
- return user?.profile || { shareAudio: false, shareVideo: false }
- },
- hasMainStream() {
- return this.remoteUser.main?.srcObject
- },
- hasScreenStream() {
- return this.remoteUser.screen?.srcObject
- },
- isWebcamFullScreen: () => {
- const modal = Session.get('modal')
-
- return modal?.template === 'fullScreenModal' && modal?.data?.template === 'webcam'
- },
- state() {
- const fields = { 'profile.userMediaError': 1 }
- const user = Meteor.users.findOne(this.remoteUser._id, { fields })
- if (!user) return 'user-error'
- if (user.profile.userMediaError) return 'media-error'
-
- return this.remoteUser.waitingCallAnswer ? 'calling' : 'connected'
- },
- name() {
- const fields = { 'profile.username': 1, 'profile.name': 1 }
- const user = Meteor.users.findOne(this.remoteUser._id, { fields })
- if (!user) return 'Guest'
-
- return user.profile.name || user.username || 'Guest'
- },
- avatar() {
- const fields = { 'profile.avatar': 1, 'profile.name': 1 }
- const user = Meteor.users.findOne(this.remoteUser._id, { fields })
- if (!user)
- return generateRandomAvatarURLForUser({
- _id: 'usr_a',
- profile: { name: 'Guest', avatar: 'cat' },
- })
-
- return generateRandomAvatarURLForUser(user)
- },
-})
-
-Template.remoteStream.events({
- 'click .js-webcam': function (event) {
- event.preventDefault()
-
- Session.set('modal', {
- template: 'fullScreenModal',
- classes: 'modal-fit fullscreen-modal',
- screenType: 'webcam',
- remoteUser: this.remoteUser,
- })
- },
- 'click .js-screenshare': function (event) {
- event.preventDefault()
-
- Session.set('modal', {
- template: 'fullScreenModal',
- classes: 'modal-fit fullscreen-modal',
- screenType: 'screenshare',
- remoteUser: this.remoteUser,
- })
- },
-})
diff --git a/core/client/ui/settings-medias.js b/core/client/ui/settings-medias.js
index 9eac5ac57..3550bbc47 100644
--- a/core/client/ui/settings-medias.js
+++ b/core/client/ui/settings-medias.js
@@ -20,8 +20,6 @@ const updateSettingsStream = async (template) => {
video.srcObject = stream
video.onloadedmetadata = () => video.play()
- peer.updatePeersStream(stream, streamTypes.main)
-
if (interval) clearInterval(interval)
interval = userStreams.trackSound(stream, (audioMeter) => template.audioMeter.set(audioMeter))
}
diff --git a/core/client/ui/stream.hbs.html b/core/client/ui/stream.hbs.html
deleted file mode 100644
index 15bc031a8..000000000
--- a/core/client/ui/stream.hbs.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
- {{#each remoteUser in allRemoteStreamsByUsers}} {{> remoteStream remoteUser=remoteUser }} {{/each}}
-
-
-
diff --git a/core/client/ui/stream.js b/core/client/ui/stream.js
deleted file mode 100644
index 5abc22114..000000000
--- a/core/client/ui/stream.js
+++ /dev/null
@@ -1,59 +0,0 @@
-const talking = () => !!peer.remoteStreamsByUsers.get().length
-
-const onMediaStreamStateChanged = (event) => {
- const { stream, type } = event.detail
-
- if (type === streamTypes.screen) {
- if (!this.videoScreenShareElement)
- this.videoScreenShareElement = document.querySelector('.js-stream-screen-me video')
- if (!stream) destroyVideoSource(this.videoScreenShareElement)
- else this.videoScreenShareElement.srcObject = stream
-
- const user = Meteor.user({ fields: { 'profile.shareScreen': 1 } })
- if (user) this.videoScreenShareElement.classList.toggle('active', user.profile.shareScreen)
- } else if (type === streamTypes.main) {
- if (!this.videoElement) this.videoElement = document.querySelector('.js-stream-me video')
- if (!stream) destroyVideoSource(this.videoElement)
- else this.videoElement.srcObject = stream
- }
-}
-
-Template.stream.onCreated(function () {
- this.avatarURL = new ReactiveVar()
- window.addEventListener(eventTypes.onMediaStreamStateChanged, onMediaStreamStateChanged)
-
- this.autorun(() => {
- if (!Meteor.userId()) return
-
- const user = Meteor.user({ fields: { 'profile.avatar': 1 } })
- if (user) this.avatarURL.set(generateRandomAvatarURLForUser(user))
- })
-})
-
-Template.stream.onDestroyed(() => {
- window.removeEventListener(eventTypes.onMediaStreamStateChanged, onMediaStreamStateChanged)
-})
-
-Template.stream.helpers({
- allRemoteStreamsByUsers: () => peer.remoteStreamsByUsers.get(),
- active() {
- return talking()
- },
- avatarURL() {
- return talking() && Template.instance().avatarURL.get()
- },
- screenSharing() {
- return Meteor.user({ fields: { 'profile.shareScreen': 1 } })?.profile.shareScreen
- },
- videoActive() {
- return talking() && Meteor.user({ fields: { 'profile.shareVideo': 1 } })?.profile.shareVideo
- },
-})
-
-Template.stream.events({
- 'click .js-stream-me': function (event) {
- event.preventDefault()
- event.stopPropagation()
- document.querySelector('.stream-me').focus()
- },
-})
diff --git a/core/client/ui/stream.scss b/core/client/ui/stream.scss
deleted file mode 100644
index 10e226b6f..000000000
--- a/core/client/ui/stream.scss
+++ /dev/null
@@ -1,115 +0,0 @@
-@import '../_variables';
-
-.remote-user {
- max-height: calc(100vh - 210px);
- position: fixed;
- top: 10px;
- right: 10px;
- z-index: 50;
- width: 280px;
- transform: translateX(110%);
- transition: transform 0.5s ease-out;
- overflow-y: scroll;
-
- video:not(.fullscreen) {
- object-fit: cover;
- }
-
- &.active {
- transform: translateX(0);
- }
-}
-
-.remote-user::-webkit-scrollbar {
- display: none;
-}
-
-.stream-me {
- transform: translateX(110%);
- transition: transform 0.5s ease-out;
- position: fixed;
- z-index: 50;
- bottom: 10px;
- right: 10px;
-
- @include media-max('phone-down') {
- transform: translate(300px);
- transition: transform 0.5s;
-
- &.visible {
- transform: translate(0);
- }
-
- &.videoActive {
- transform: translate(240px);
-
- &.visible {
- transform: translate(0);
- }
- }
- }
-
- &.active {
- background-color: rgba($main-color, 1);
- border-radius: 15px;
- transform: translateX(0);
- }
-
- .js-stream-me {
- position: relative;
- width: 280px;
- height: 180px;
- background-color: rgba($main-color, 0.9);
- border-radius: 15px;
- z-index: 30;
- background-size: cover;
- background-repeat: no-repeat;
- background-position: 50% 50%;
- overflow: hidden;
-
- video {
- width: 100%;
- height: 180px;
- border-radius: 10px;
- display: none;
- object-fit: cover;
- transform: scale(-1, 1);
- }
-
- &.active-video video {
- display: block;
- }
- }
-
- .js-stream-screen-me {
- display: none;
- position: absolute;
- bottom: 25px;
- right: 16px;
- width: 340px;
- height: 200px;
- background-color: #292929;
- background-image: url('loader-white.gif');
- background-size: 30%;
- background-position: 50%;
- background-repeat: no-repeat;
- border-radius: 15px;
- overflow: hidden;
- z-index: 28;
-
- &.active {
- display: block;
- }
-
- video {
- width: 100%;
- object-fit: fill;
- height: 100%;
- display: none;
-
- &.active {
- display: block;
- }
- }
- }
-}
diff --git a/core/client/ui/user-onboarding.js b/core/client/ui/user-onboarding.js
index 326a09fc0..c5326e673 100644
--- a/core/client/ui/user-onboarding.js
+++ b/core/client/ui/user-onboarding.js
@@ -43,8 +43,6 @@ const updateSettingsStream = async (template) => {
video.srcObject = stream
video.onloadedmetadata = () => video.play()
- peer.updatePeersStream(stream, streamTypes.main)
-
if (interval) clearInterval(interval)
interval = userStreams.trackSound(stream, (audioMeter) => template.audioMeter.set(audioMeter))
}
diff --git a/core/client/ui/user-panel.js b/core/client/ui/user-panel.js
index 955f23124..8a06ffadd 100644
--- a/core/client/ui/user-panel.js
+++ b/core/client/ui/user-panel.js
@@ -1,6 +1,6 @@
import { guestAllowed, permissionTypes } from '../../lib/misc'
-const talking = () => !!peer.remoteStreamsByUsers.get().length
+const talking = () => Session.get('isLowLevelActive')
Template.userPanel.onCreated(function () {
if (Meteor.settings.public.features?.userPanel?.enabled === false) return
diff --git a/core/client/user-manager.js b/core/client/user-manager.js
index d70f5deb3..44a453de3 100644
--- a/core/client/user-manager.js
+++ b/core/client/user-manager.js
@@ -180,7 +180,6 @@ userManager = {
}
} else {
if (shouldCheckDistance) userProximitySensor.checkDistance(loggedUser, user)
- if (user.profile.shareScreen !== oldUser?.profile.shareScreen) peer.onStreamSettingsChanged(user)
}
},
@@ -269,11 +268,11 @@ userManager = {
this.controlledCharacter.running = hotkeys.isPressed('shift')
this.controlledCharacter.moveDirection = this.inputVector
- if (peer.isEnabled() && !Session.get('menu')) {
+ if (jitsiMeetJS && !Session.get('menu')) {
const nearUsersCount = guestAllowed(permissionTypes.talkToUsers)
? userProximitySensor.nearUsersCount()
: userProximitySensor.nearNonGuestUsers().length
- this.controlledCharacter.enableChatCircle(nearUsersCount > 0)
+ this.controlledCharacter.enableChatCircle(!Session.get('isJitsiMeetOpen') && nearUsersCount > 0)
} else this.controlledCharacter.enableChatCircle(false)
const newVelocity = this.controlledCharacter.physicsStep()
@@ -290,7 +289,6 @@ userManager = {
this.stopInteracting()
}
- if (!peer.hasActiveStreams()) peer.enableSensor(!(this.controlledCharacter.running && moving))
this.controlledCharacter.wasMoving = moving
this.controlledCharacter?.postUpdateStep()
},
@@ -321,18 +319,15 @@ userManager = {
if (!user || (user && this.controlledCharacter.followedGameObject)) {
if (this.controlledCharacter.followedGameObject) {
lp.notif.success(`You no longer follow anyone`)
- peer.unlockCall(this.controlledCharacter.followedGameObject.getData('userId'), true)
}
this.controlledCharacter.stopFollow()
-
return
}
this.controlledCharacter.follow(this.getCharacter(user._id))
lp.notif.success(`You are following ${user.profile.name}`)
- peer.lockCall(user._id, true)
},
setUserInDoNotDisturbMode(enable) {
@@ -381,54 +376,6 @@ userManager = {
this.userMediaStates = undefined
},
- onPeerDataReceived(dataReceived) {
- const { emitter: emitterUserId, type, data } = dataReceived
-
- const userEmitter = Meteor.users.findOne(emitterUserId)
- if (!userEmitter) return
-
- const meta = {}
-
- if (type === 'audio') audioManager.playFromChunks(data)
- else if (type === 'followed') {
- peer.lockCall(emitterUserId)
- lp.notif.warning(`${userEmitter.profile.name} is following you π`)
- } else if (type === 'unfollowed') {
- peer.unlockCall(emitterUserId)
-
- const followedUserId = this.controlledCharacter.followedGameObject?.getData('userId')
- if (followedUserId === emitterUserId) this.controlledCharacter.stopFollow()
- else lp.notif.warning(`${userEmitter.profile.name} has finally stopped following you π`)
- } else if (type === 'text') {
- const emitterPlayer = this.getCharacter(emitterUserId)
- if (!emitterPlayer) return
- if (!data.content) return
-
- const { zoneLastSeenDates = [], zoneMuted = [] } = Meteor.user({
- fields: { zoneMuted: 1, zoneLastSeenDates: 1 },
- })
- const userEmitterZoneId = zoneManager.currentZone(userEmitter)?._id
- if (zoneLastSeenDates[userEmitterZoneId] && !zoneMuted[userEmitterZoneId])
- audioManager.play('text-sound.wav', 0.5)
-
- const popInIdentifier = `${emitterUserId}-pop-in`
- meta['pop-in'] = characterPopIns.createOrUpdate(popInIdentifier, data.content, {
- target: emitterPlayer,
- className: messageReceived.style,
- autoClose: messageReceived.duration,
- parseURL: true,
- classList: 'copy',
- offset: characterPopInOffset,
- })
- }
-
- window.dispatchEvent(
- new CustomEvent(eventTypes.onPeerDataReceived, {
- detail: { data: dataReceived, userEmitter, meta },
- })
- )
- },
-
getCharacter(userId) {
return this.characters[userId]
},
diff --git a/core/client/user-streams.js b/core/client/user-streams.js
index 60e79abe0..409b06e46 100644
--- a/core/client/user-streams.js
+++ b/core/client/user-streams.js
@@ -49,12 +49,6 @@ userStreams = {
this.stopTracks(screenStream)
this.streams.screen.instance = undefined
- Object.entries(peer.calls).forEach(([key, call]) => {
- if (key.indexOf('-screen') === -1) return
- debug('me -> you screen ****** I stop sharing screen, call closing', key)
- call.close()
- delete peer.calls[key]
- })
},
destroyStream(type) {
diff --git a/core/client/user-voice-recorder-ability.js b/core/client/user-voice-recorder-ability.js
index 1d24ed471..38474d269 100644
--- a/core/client/user-voice-recorder-ability.js
+++ b/core/client/user-voice-recorder-ability.js
@@ -149,23 +149,6 @@ const sendAudioChunksToTargets = (chunks, userIds) => {
uploadInstance.start()
}
-sendAudioChunksToUsersInZone = async (chunks) => {
- const user = Meteor.user()
- const usersInZone = zoneManager.usersInZone(zoneManager.currentZone(user))
- const userInZoneIds = usersInZone.map((u) => u._id)
-
- try {
- await networkManager.sendData(userInZoneIds, {
- type: 'audio',
- emitter: user._id,
- data: chunks,
- })
- lp.notif.success(`π£ Everyone has heard your powerful voice`)
- } catch {
- lp.notif.warning('β No one is there to hear you')
- }
-}
-
sendAudioChunksToNearUsers = (chunks) => {
const { nearUsers } = userProximitySensor
const userIds = [...new Set(_.keys(nearUsers))].filter((target) => target !== Meteor.userId())
diff --git a/core/modules/meet/client/meet-low-level.hbs.html b/core/modules/meet/client/meet-low-level.hbs.html
index 2f4ada5db..adc60d2d5 100644
--- a/core/modules/meet/client/meet-low-level.hbs.html
+++ b/core/modules/meet/client/meet-low-level.hbs.html
@@ -1,26 +1,25 @@
-
- {{#if sessionMeetLowLevelTracks.length}}
-
- {{#each sessionMeetLowLevelTracks}} {{#with track}} {{> meetLowLevelTrack}} {{/with}} {{/each}}
-
- {{/if}}
-
-
-
- {{#if eq getType "audio"}} {{> meetLowLevelAudioTrack}} {{else}} {{> meetLowLevelVideoTrack}} {{/if}}
-
-
-
-
-
+
+ {{#if isActive}}
+
+ {{#each tracks in remoteTracks}}
+
+ {{> remoteAudioTrack track=tracks.audio}} {{> remoteVideoTrack track=tracks.camera
+ desktopTrack=tracks.desktop displayName=tracks.displayName }}
+
+ {{/each}}
-
+ {{#if fullscreenTrack }} {{> remoteFullscreen track=fullscreenTrack}} {{/if}} {{/if}}
-
-
-
{{name}}
-
-
+
diff --git a/core/modules/meet/client/meet-low-level.js b/core/modules/meet/client/meet-low-level.js
index 378e1568b..3c60c632f 100644
--- a/core/modules/meet/client/meet-low-level.js
+++ b/core/modules/meet/client/meet-low-level.js
@@ -1,258 +1,480 @@
-/* eslint-disable prefer-rest-params */
+import { getTrackType, updateTrack, attachLocalTracks, replaceLocalTrack } from './tracks/utils'
-// https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-ljm-api/
+window.addEventListener('DOMContentLoaded', () => {
+ jitsiMeetJS = undefined
+ if (!Meteor.settings.public.meet) return
-window.addEventListener('load', () => registerModules(['meetLowLevelTracks']))
+ const head = document.querySelector('head')
-Template.meetLowLevelTracks.events({
- 'click .js-fullscreen': function () {
- Session.set(
- 'meetLowLevelFullscreenTrackId',
- Session.get('meetLowLevelFullscreenTrackId') === this.getId() ? undefined : this.getId()
- )
- },
-})
+ const scriptLowLevel = document.createElement('script')
+ scriptLowLevel.src = `https://${Meteor.settings.public.meet.serverURL}/libs/lib-jitsi-meet.min.js`
+ head.appendChild(scriptLowLevel)
-Template.meetLowLevelTracks.helpers({
- track() {
- return meet.tracks.find((t) => t.getId() === `${this}`)
- },
- sessionMeetLowLevelTracks() {
- return Session.get('meetLowLevelTracks')
- },
+ scriptLowLevel.onload = () => {
+ jitsiMeetJS = window.JitsiMeetJS
+ }
})
-Template.meetLowLevelVideoTrack.helpers({
- name() {
- const participant = meet.room.getParticipants().find((p) => p.getId() === this.getParticipantId())
- return participant?.getDisplayName() || Meteor.user().profile.name || ''
- },
+Template.meetLowLevel.onCreated(function () {
+ this.avatarURL = new ReactiveVar()
+ this.connection = new ReactiveVar(undefined)
+ this.localTracks = new ReactiveVar([])
+ this.remoteTracks = new ReactiveVar({})
+ meetLowLevel.template = this
+
+ this.autorun(() => {
+ if (!Meteor.userId()) return
+ const user = Meteor.user({ fields: { 'profile.avatar': 1 } })
+ if (user) this.avatarURL.set(generateRandomAvatarURLForUser(user))
+ })
+
+ this.autorun(() => {
+ const user = Meteor.user({ fields: { 'profile.videoRecorder': 1 } })
+
+ if (!user || !jitsiMeetJS) return
+ jitsiMeetJS
+ .createLocalTracks({
+ devices: ['video'],
+ cameraDeviceId: user?.profile?.videoRecorder,
+ })
+ .then((tracks) => replaceLocalTrack(this, tracks[0]))
+ .catch((err) => console.error('An error occured while creating local tracks', err))
+ })
+
+ this.autorun(() => {
+ const user = Meteor.user({ fields: { 'profile.audioRecorder': 1 } })
+
+ if (!user || !jitsiMeetJS) return
+ jitsiMeetJS
+ .createLocalTracks({
+ devices: ['audio'],
+ micDeviceId: user?.profile?.audioRecorder,
+ })
+ .then((tracks) => replaceLocalTrack(this, tracks[0]))
+ .catch((err) => console.error('An error occured while creating local tracks', err))
+ })
+
+ this.autorun(() => {
+ if (this.connection.get() && Session.get('isJitsiMeetOpen')) {
+ meetLowLevel.disconnect()
+ }
+ })
+
+ window.addEventListener(eventTypes.onUsersComeCloser, (e) => onUsersComeCloser(e, this))
+ window.addEventListener(eventTypes.onUsersMovedAway, (e) => onUsersMovedAway(e, this))
+ window.addEventListener(eventTypes.onUserPropertyUpdated, (e) => onUserPropertyUpdated(e, this))
})
-const trackAttach = () => {
- const track = Template.currentData()
- const el = $(`#${track.getId()} .st`)[0]
- // force muted property to be sync with attribute
- el.muted = el.getAttribute('muted')
- track.attach(el)
-}
+Template.meetLowLevel.onDestroyed(() => {
+ window.removeEventListener(eventTypes.onUsersComeCloser, (e) => onUsersComeCloser(e, this))
+ window.removeEventListener(eventTypes.onUsersMovedAway, (e) => onUsersMovedAway(e, this))
+ window.removeEventListener(eventTypes.onUserPropertyUpdated, (e) => onUserPropertyUpdated(e, this))
+})
-Template.meetLowLevelAudioTrack.onRendered(trackAttach)
-Template.meetLowLevelVideoTrack.onRendered(trackAttach)
+Template.meetLowLevel.helpers({
+ avatarURL() {
+ return Template.instance().avatarURL.get()
+ },
+ isActive() {
+ const isActive = Template.instance().connection.get() !== undefined && meetLowLevel.getCallCount() > 0
-const trackDetach = () => {
- const track = Template.currentData()
- track.detach($(`#${track.getId()} .st`)[0])
-}
+ Session.set('isLowLevelActive', isActive)
+ return isActive
+ },
+ remoteTracks() {
+ // Check a better way to remove undefined tracks
+ return Object.values(Template.instance().remoteTracks.get()).filter((track) => track.audio && track.camera)
+ },
+ isLocalVideoActive() {
+ const user = Meteor.user({ fields: { 'profile.shareVideo': 1 } })
-Template.meetLowLevelAudioTrack.onDestroyed(trackDetach)
-Template.meetLowLevelVideoTrack.onDestroyed(trackDetach)
+ if (!user || !user.profile) return
+ return user.profile.shareVideo && Template.instance().localTracks.get().length > 0
+ },
+ isSharingScreen() {
+ const localTracks = Template.instance().localTracks.get()
-meetLowLevel = {
- lowLevel: true,
+ return localTracks?.find((t) => t.getType() === 'video' && t.getVideoType() === 'desktop')
+ },
+ fullscreenTrack() {
+ const fullscreenTrackId = Session.get('fullscreenTrackId')
+
+ if (!Session.get('fullscreenTrackId')) return
+ let fullscreenTrack
+ const remoteTracks = Object.values(Template.instance().remoteTracks.get())
+
+ remoteTracks?.forEach((tracks) => {
+ const tracksMap = Object.values(tracks)
+ tracksMap.forEach((track) => {
+ if (track && typeof track !== 'string' && track.getId() === fullscreenTrackId) {
+ fullscreenTrack = track
+ }
+ })
+ })
- api: undefined,
- connection: undefined,
- room: undefined,
+ return fullscreenTrack
+ },
+})
- tracks: [],
+/*
+ ** Events listeners
+ */
+
+const onUsersComeCloser = (e, template) => {
+ const { users } = e.detail
+
+ if (jitsiMeetJS && !template.connection.get() && !meetLowLevel.connectionStarted) {
+ meetLowLevel.connectionStarted = true
+
+ Meteor.call('getUserRoomName', users[0]._id, (err, roomName) => {
+ if (!roomName) {
+ const usersIds = users.map((user) => user._id).concat(Meteor.userId())
+ Meteor.call('computeMeetLowLevelRoomName', usersIds, (err, computedRoomName) => {
+ if (!computedRoomName) {
+ lp.notif.error('Unable to load a room, please try later')
+ return
+ }
+
+ meetLowLevel.roomName = computedRoomName
+ meetLowLevel.connect()
+ })
+ } else {
+ meetLowLevel.roomName = roomName
+ Meteor.call('updateUserRoomName', roomName)
+ meetLowLevel.connect()
+ }
+ })
+ }
+
+ users.forEach((user) => {
+ if (!meetLowLevel.usersInCall[user._id]) {
+ meetLowLevel.usersInCall[user._id] = {
+ callStartDate: Date.now(),
+ }
+ Meteor.call('analyticsDiscussionAttend', {
+ peerUserId: user._id,
+ usersAttendingCount: meetLowLevel.getCallCount(),
+ })
+ }
+ })
+}
- isJoined: false,
+const onUsersMovedAway = (e, template) => {
+ const { users } = e.detail
+
+ users.forEach((user) => {
+ if (meetLowLevel.usersInCall[user._id]) {
+ const duration = (Date.now() - meetLowLevel.usersInCall[user._id].callStartDate) / 1000
+ Meteor.call('analyticsDiscussionEnd', {
+ peerUserId: user._id,
+ duration,
+ usersAttendingCount: meetLowLevel.getCallCount(template),
+ })
+ delete meetLowLevel.usersInCall[user._id]
+ }
+ })
- onTrackAdded(track) {
- log('onTrackAdded', arguments)
+ if (template.connection.get() && meetLowLevel.getCallCount() === 0) {
+ meetLowLevel.disconnect()
+ }
+}
- meet.tracks.push(track)
- Session.set(
- 'meetLowLevelTracks',
- meet.tracks.map((t) => t.getId())
- )
- },
+const onUserPropertyUpdated = async (e, template) => {
+ const { propertyName, propertyValue } = e.detail
+ const localTracks = template.localTracks.get()
+
+ if (!localTracks || localTracks.length === 0 || !propertyName) return
+
+ if (propertyName === 'shareAudio') {
+ updateTrack('audio', localTracks)
+ } else if (propertyName === 'shareVideo') {
+ updateTrack('video', localTracks)
+ } else if (propertyName === 'shareScreen') {
+ if (propertyValue) {
+ await jitsiMeetJS.createLocalTracks({ devices: ['desktop'] }).then((tracks) => {
+ const currentDesktopTrack = localTracks.find((t) => t.getVideoType() === 'desktop')
+ const screenNode = document.querySelector('#video-screen-me')
+ const track = tracks[0] // Since we just ask for desktop, we will only have one item
+
+ track.attach(screenNode)
+
+ // If it's the first time we share screen, we should add it to the conference
+ if (!currentDesktopTrack) {
+ meetLowLevel.room.addTrack(track)
+ } else {
+ // Otherwise, we should replace since because Meet will not trigger 'TRACK_REMOVED' when we dispose the desktop track
+ meetLowLevel.room.replaceTrack(currentDesktopTrack, track)
+ }
+
+ localTracks.push(track)
+ template.localTracks.set(localTracks)
+ })
+ } else {
+ const filteredLocalTracks = localTracks.filter((track) => {
+ // While we remove the desktop track, we dispose it at the same time
+ if (track.getVideoType() === 'desktop') {
+ track.dispose()
+ return false
+ }
+ return true
+ })
+
+ template.localTracks.set(filteredLocalTracks)
+ }
+ }
+}
- onTrackRemoved(track) {
- log('onTrackRemoved', arguments)
+/*
+ ** LowMeetJs
+ */
+
+const getOptions = () => {
+ const serverURL = Meteor.settings.public.meet.serverURL
+
+ if (!serverURL) return
+ const [domain, sub] = serverURL.split('/')
+
+ return {
+ // Connection
+ hosts: {
+ domain: serverURL,
+ muc: `conference.${sub}.${domain}`,
+ focus: `focus.${domain}`,
+ },
+ serviceUrl: `wss://${serverURL}/xmpp-websocket?room=${meetLowLevel.roomName}`,
+ websocketKeepAliveUrl: `https://${serverURL}/_unlock?room=${meetLowLevel.roomName}`,
+
+ // Enable Peer-to-Peer for 1-1 calls
+ p2p: {
+ enabled: false,
+ },
+
+ // Video quality / constraints
+ constraints: {
+ video: {
+ height: {
+ ideal: 720,
+ max: 720,
+ min: 180,
+ },
+ width: {
+ ideal: 1280,
+ max: 1280,
+ min: 320,
+ },
+ },
+ },
+ channelLastN: 25,
+
+ // Logging
+ logging: {
+ // Default log level
+ defaultLogLevel: 'trace',
+
+ // The following are too verbose in their logging with the default level
+ 'modules/RTC/TraceablePeerConnection.js': 'info',
+ 'modules/statistics/CallStats.js': 'info',
+ 'modules/xmpp/strophe.util.js': 'log',
+ },
+
+ // End marker, disregard
+ __end: true,
+ }
+}
- meet.tracks = _.without(meet.tracks, track)
- Session.set(
- 'meetLowLevelTracks',
- meet.tracks.map((t) => t.getId())
- )
+export const meetLowLevel = {
+ connectionStarted: false,
+ room: undefined,
+ roomName: undefined,
+ usersInCall: {},
+ template: undefined,
+
+ async connect() {
+ if (!this.template.connection.get()) {
+ const options = getOptions()
+ const user = Meteor.user({ fields: { 'profile.audioRecorder': 1, 'profile.videoRecorder': 1 } })
+
+ jitsiMeetJS.init(options)
+ jitsiMeetJS.setLogLevel(jitsiMeetJS.logLevels.ERROR)
+
+ await jitsiMeetJS
+ .createLocalTracks({
+ devices: ['audio', 'video'],
+ cameraDeviceId: user?.profile?.videoRecorder,
+ micDeviceId: user?.profile?.audioRecorder,
+ })
+ .then((tracks) => {
+ updateTrack('video', tracks)
+ updateTrack('audio', tracks)
+ attachLocalTracks(tracks)
+ this.template.localTracks.set(tracks)
+ })
+ .catch((err) => console.error('An error occured while creating local tracks', err))
+
+ Meteor.call('computeRoomToken', this.roomName, (err, token) => {
+ const connection = new jitsiMeetJS.JitsiConnection(null, token, options)
+
+ connection.addEventListener(jitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, () => {
+ this.onConnectionSuccess()
+ })
+ connection.addEventListener(jitsiMeetJS.events.connection.CONNECTION_FAILED, () => {
+ this.onConnectionFailed()
+ })
+ connection.addEventListener(jitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, () => {
+ this.onConnectionDisconnected()
+ })
+
+ connection.connect()
+ this.template.connection.set(connection)
+ })
+ }
},
- onUserLeft(participantId) {
- log('user left', arguments)
+ async disconnect() {
+ this.connectionStarted = false
+ Meteor.call('updateUserRoomName', undefined)
+ if (this.room?.room) {
+ try {
+ this.room.leave()
+ } catch (err) {
+ console.log('Error while leaving', err)
+ }
+ }
- meet.tracks = meet.tracks.filter((t) => t.getParticipantId() !== participantId)
- Session.set(
- 'meetLowLevelTracks',
- meet.tracks.map((t) => t.getId())
- )
- },
+ const connection = this.template.connection.get()
- onConnectionSuccess() {
- log('onConnectionSuccess', arguments)
-
- meet.room = meet.connection.initJitsiConference(kebabCase(meet.roomName), {})
- meet.room.on(window.JitsiMeetJS.events.conference.TRACK_ADDED, meet.onTrackAdded)
- meet.room.on(window.JitsiMeetJS.events.conference.TRACK_REMOVED, meet.onTrackRemoved)
- meet.room.on(window.JitsiMeetJS.events.conference.CONFERENCE_JOINED, meet.onConferenceJoined)
- meet.room.on(window.JitsiMeetJS.events.conference.USER_JOINED, () => {
- log('user join', arguments)
+ connection.disconnect()
+ connection.removeEventListener(jitsiMeetJS.events.connection.CONNECTION_ESTABLISHED, () => {
+ this.onConnectionSuccess()
+ })
+ connection.removeEventListener(jitsiMeetJS.events.connection.CONNECTION_FAILED, () => {
+ this.onConnectionFailed()
+ })
+ connection.removeEventListener(jitsiMeetJS.events.connection.CONNECTION_DISCONNECTED, () => {
+ this.onConnectionDisconnected()
})
- meet.room.on(window.JitsiMeetJS.events.conference.USER_LEFT, meet.onUserLeft)
-
- meet.room.setDisplayName(Meteor.user().profile.name)
- meet.room.join()
+ this.room = undefined
+ this.template.connection.set(undefined)
},
- onConnectionFailed() {
- log('onConnectionFailed', arguments)
+ getCallCount() {
+ return Object.keys(this.usersInCall).length
},
- onDisconnected() {
- log('onDisconnected', arguments)
- },
-
- onConferenceJoined() {
- log('onConferenceJoined', arguments)
- meet.isJoined = true
- meet.tracks.forEach((track) => {
- if (!track.isLocal()) return
- meet.room.addTrack(track)
- })
+ /*
+ ** LowMeetJs events listeners
+ */
+ onTrackAdded(track) {
+ // Since we attach local tracks separately, we do not need attach it again
+ if (track.isLocal()) return
+
+ const participantId = track.getParticipantId()
+ const _remoteTracks = this.template.remoteTracks.get()
+
+ if (!_remoteTracks[participantId]) _remoteTracks[participantId] = {}
+
+ if (track.getType() === 'video') {
+ // When receiving a 'desktop' track, Jitsi doesn't immediately set the correct type, leading to confusion with our own tracks.
+ // Initially, a 'desktop' track is classified as a 'camera' type, but after a few seconds, it is eventually updated to a 'desktop' track type.
+ // This inconsistency is quite frustrating, and since we haven't found a suitable solution, it's better to introduce a timeout before setting the track type to 'video'.
+ // Cf: https://community.jitsi.org/t/identifying-new-track-as-desktop/118232/2
+
+ setTimeout(() => {
+ _remoteTracks[participantId][getTrackType(track)] = track
+ this.template.remoteTracks.set(_remoteTracks)
+ }, 1000)
+ } else {
+ _remoteTracks[participantId][getTrackType(track)] = track
+ this.template.remoteTracks.set(_remoteTracks)
+ }
},
- open(roomName = Meteor.settings.public.meet.roomDefaultName) {
- meet.api = window.JitsiMeetJS
-
- meet.api.init()
-
- meet.api.setLogLevel(meet.api.logLevels.ERROR)
-
- meet.tracks = []
- Session.set(
- 'meetLowLevelTracks',
- meet.tracks.map((t) => t.getId())
- )
-
- Session.set('meetLowLevelFullscreenTrackId', undefined)
-
- meet.connection = new meet.api.JitsiConnection(null, null, {
- hosts: {
- domain: Meteor.settings.public.meet.serverURL,
- muc: 'conference.jitsi.lemverse.com',
- },
- bosh: `${Meteor.absoluteUrl()}http-bind`,
- })
+ onTrackRemoved(track) {
+ const _remoteTracks = this.template.remoteTracks.get()
+ const participantId = track.getParticipantId()
- meet.connection.addEventListener(
- window.JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
- meet.onConnectionSuccess
- )
- meet.connection.addEventListener(
- window.JitsiMeetJS.events.connection.CONNECTION_FAILED,
- meet.onConnectionFailed
- )
- meet.connection.addEventListener(
- window.JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
- meet.onDisconnected
- )
-
- meet.roomName = roomName
-
- meet.connection.connect()
- },
+ if (_remoteTracks[participantId]) {
+ _remoteTracks[participantId][getTrackType(track)] = null
- async close() {
- meet.connection.removeEventListener(
- window.JitsiMeetJS.events.connection.CONNECTION_ESTABLISHED,
- meet.onConnectionSuccess
- )
- meet.connection.removeEventListener(
- window.JitsiMeetJS.events.connection.CONNECTION_FAILED,
- meet.onConnectionFailed
- )
- meet.connection.removeEventListener(
- window.JitsiMeetJS.events.connection.CONNECTION_DISCONNECTED,
- meet.onDisconnected
- )
-
- await Promise.all(meet.tracks.filter((t) => t.isLocal()).map((t) => t.dispose()))
-
- meet.tracks = []
- Session.set(
- 'meetLowLevelTracks',
- meet.tracks.map((t) => t.getId())
- )
-
- if (meet.room) {
- await meet.room.leave()
- meet.room = undefined
+ if (_remoteTracks[participantId].length === 0) delete _remoteTracks[participantId]
+ this.template.remoteTracks.set(_remoteTracks)
}
- if (meet.connection) {
- meet.connection.disconnect()
- meet.connection = undefined
- }
- if (meet.api) meet.api = undefined
-
- meet.isJoined = false
},
- show() {
- throw new Error('"show" is not implemented')
- },
+ onConferenceJoined() {
+ console.log('conference joined!')
- fullscreen() {
- throw new Error('"fullscreen" is not implemented')
+ // If the user is the only user in the conference, disconnect from the conference.
+ if (this.getCallCount() === 0) {
+ this.disconnect()
+ }
},
- mute() {
- const track = meet.tracks.find((t) => t.isLocal() && t.getType() === 'audio')
- if (!track) return
- track.dispose()
+ onConferenceLeft() {
+ console.log('conference left!')
},
- onLocalTracksCreated(tracks) {
- tracks.forEach((track) => {
- if (!meet.isJoined) return
- meet.room.addTrack(track)
- })
- },
+ onUserJoined(userId, participant) {
+ console.log('user joined!', userId)
+ const _remoteTracks = this.template.remoteTracks.get()
- unmute() {
- window.JitsiMeetJS.createLocalTracks({
- micDeviceId: Meteor?.user()?.profile?.audioRecorder,
- devices: ['audio'],
- }).then(meet.onLocalTracksCreated)
+ if (!_remoteTracks[userId]) _remoteTracks[userId] = {}
+ _remoteTracks[userId].displayName = participant.getDisplayName()
+ this.template.remoteTracks.set(_remoteTracks)
},
- hide() {
- const track = meet.tracks.find((t) => t.isLocal() && t.getType() === 'video' && t.getVideoType() === 'camera')
- if (!track) return
- track.dispose()
+ onConnectionSuccess() {
+ console.log('Successfully connected')
+ const user = Meteor.user({ fields: { 'profile.name': 1 } })
+
+ if (!this.room) {
+ this.room = this.template.connection.get().initJitsiConference(this.roomName, {})
+ const _localTracks = this.template.localTracks.get()
+
+ // Add local tracks before joining
+ for (let i = 0; i < _localTracks.length; i++) {
+ this.room.addTrack(_localTracks[i])
+ }
+
+ // Setup event listeners
+ this.room.on(jitsiMeetJS.events.conference.TRACK_ADDED, (track) => this.onTrackAdded(track))
+ this.room.on(jitsiMeetJS.events.conference.TRACK_REMOVED, (track) => this.onTrackRemoved(track))
+ this.room.on(jitsiMeetJS.events.conference.CONFERENCE_JOINED, () => {
+ this.onConferenceJoined()
+ })
+ this.room.on(jitsiMeetJS.events.conference.CONFERENCE_LEFT, () => {
+ this.onConferenceLeft()
+ })
+ this.room.on(jitsiMeetJS.events.conference.USER_JOINED, (userId, participant) =>
+ this.onUserJoined(userId, participant)
+ )
+ this.room.on(jitsiMeetJS.events.conference.USER_LEFT, (id) => console.log('user left!', id))
+
+ // Join
+ this.room.setDisplayName(user?.profile?.name)
+ this.room.join()
+ this.room.setSenderVideoConstraint(720) // Send at most 720p
+ this.room.setReceiverVideoConstraint(360) // Receive at most 360p for each participant
+ }
},
- unhide() {
- window.JitsiMeetJS.createLocalTracks({
- cameraDeviceId: Meteor?.user()?.profile?.videoRecorder,
- devices: ['video'],
- }).then(meet.onLocalTracksCreated)
+ onConnectionFailed() {
+ console.error('connection failed!')
},
- shareScreen() {
- window.JitsiMeetJS.createLocalTracks({
- devices: ['desktop'],
- }).then(meet.onLocalTracksCreated)
- },
+ onConnectionDisconnected() {
+ console.log('connection disconnected')
- unshareScreen() {
- const track = meet.tracks.find((t) => t.isLocal() && t.getType() === 'video' && t.getVideoType() === 'desktop')
- if (!track) return
- track.dispose()
- },
+ const _localTracks = this.template.localTracks.get()
+
+ for (let i = 0; i < _localTracks.length; i++) {
+ _localTracks[i].dispose()
+ }
- userName(name) {
- meet.room.setDisplayName(name)
+ this.template.localTracks.set([])
+ this.template.remoteTracks.set({})
+ this.usersInCall = []
+ toggleUserProperty('shareScreen', false)
},
}
diff --git a/core/modules/meet/client/meet-low-level.scss b/core/modules/meet/client/meet-low-level.scss
index 8c9e0ed9e..13e9ad11d 100644
--- a/core/modules/meet/client/meet-low-level.scss
+++ b/core/modules/meet/client/meet-low-level.scss
@@ -1,40 +1,166 @@
-.meet-low-level-tracks {
+@import '../../../client/variables';
+
+.remote-users {
+ max-height: calc(100vh - 210px);
+ position: fixed;
+ top: 10px;
+ right: 10px;
+ z-index: 50;
+ width: 280px;
+ overflow-y: scroll;
+
+ video:not(.fullscreen) {
+ object-fit: cover;
+ }
+}
+
+.remote-users::-webkit-scrollbar {
+ display: none;
+}
+
+.remote-user {
+ animation: slideIn 0.5s ease-out 0s forwards;
+}
+
+.fullscreen-container {
+ z-index: 10;
position: absolute;
+ width: calc(100% - 300px) !important;
top: 0;
- max-height: 100vh;
- left: 20%;
- right: 20%;
- overflow: scroll;
- flex-wrap: wrap;
- display: flex;
- justify-content: space-evenly;
- z-index: 1;
-
- .hide {
- display: none;
- }
+ left: 0;
+ height: 100%;
+ overflow: hidden;
- &.fullscreen {
- right: 0;
- left: 80%;
+ .fullscreen {
+ height: 100%;
+ width: 100%;
+ position: relative;
+ display: flex;
+ justify-content: center;
+ align-items: center;
- .remote-stream.active {
- position: fixed;
+ .fullscreen-background {
+ position: absolute;
top: 0;
left: 0;
- width: calc(80% - 10px);
- height: calc(100vh - 10px);
-
- .webcam {
- width: 100%;
- height: 100%;
-
- video {
- width: 100%;
- height: 100%;
- object-fit: scale-down;
- }
+ width: 100%;
+ height: 100%;
+ background-color: $new-dark-secondary;
+
+ video {
+ width: 100% !important;
+ height: 100% !important;
+ object-fit: cover !important;
+ filter: blur(30px);
+ -webkit-filter: blur(30px);
}
}
+
+ .main {
+ position: relative;
+ object-fit: contain !important;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: auto;
+
+ video {
+ width: 100% !important;
+ height: auto !important;
+ }
+ }
+ }
+}
+
+.container-stream-me {
+ transform: translateX(110%);
+ transition: transform 0.5s ease-out;
+ position: fixed;
+ z-index: 50;
+ bottom: 10px;
+ right: 10px;
+
+ @include media-max('phone-down') {
+ transform: translate(300px);
+ transition: transform 0.5s;
+
+ &.visible {
+ transform: translate(0);
+ }
+
+ &.videoActive {
+ transform: translate(240px);
+
+ &.visible {
+ transform: translate(0);
+ }
+ }
+ }
+
+ &.active {
+ background-color: rgba($main-color, 1);
+ border-radius: 15px;
+ transform: translateX(0);
+ }
+
+ .stream-me {
+ position: relative;
+ width: 280px;
+ height: 180px;
+ background-color: rgba($main-color, 0.9);
+ border-radius: 15px;
+ z-index: 30;
+ overflow: hidden;
+ background-size: cover;
+
+ video {
+ width: 100%;
+ height: 180px;
+ border-radius: 10px;
+ display: none;
+ object-fit: cover;
+ transform: scale(-1, 1);
+ }
+
+ &.active-video video {
+ display: block;
+ }
+ }
+
+ .stream-screen-me {
+ display: none;
+ position: absolute;
+ bottom: 25px;
+ right: 16px;
+ width: 340px;
+ height: 200px;
+ background-color: #292929;
+ background-image: url('loader-white.gif');
+ background-size: 30%;
+ background-position: 50%;
+ background-repeat: no-repeat;
+ border-radius: 15px;
+ overflow: hidden;
+ z-index: 28;
+
+ &.active {
+ display: block;
+ }
+
+ video {
+ width: 100%;
+ object-fit: fill;
+ height: 100%;
+ }
+ }
+}
+
+@keyframes slideIn {
+ 0% {
+ transform: translateX(110%);
+ }
+ 100% {
+ transform: translateX(0);
}
}
diff --git a/core/modules/meet/client/meet.js b/core/modules/meet/client/meet.js
index ae1e33cf6..527250037 100644
--- a/core/modules/meet/client/meet.js
+++ b/core/modules/meet/client/meet.js
@@ -198,12 +198,6 @@ meetHighLevel = {
// We don't have a link between Jitsi and lemverse to identify the user at the moment. Waiting for the activation of the prosody plugin
const userEmitter = Meteor.users.findOne({ 'profile.name': nick })
if (!userEmitter) return
-
- userManager.onPeerDataReceived({
- emitter: userEmitter._id,
- data: this.convertActionToEmojis(message),
- type: 'text',
- })
})
this.api.addEventListener('participantLeft', () => {
@@ -212,8 +206,6 @@ meetHighLevel = {
this.show(true)
- peer.disable()
-
if (window.electron) {
const { setupScreenSharingRender } = window.electron.jitsiMeetElectronUtils
setupScreenSharingRender(this.api)
@@ -273,7 +265,6 @@ meetHighLevel = {
this.api?.dispose()
this.api = undefined
this.show(false)
- peer.enable()
window.dispatchEvent(new CustomEvent(eventTypes.onMeetEnded))
},
diff --git a/core/modules/meet/client/tracks/remote-audio-track.hbs.html b/core/modules/meet/client/tracks/remote-audio-track.hbs.html
new file mode 100644
index 000000000..6117318fc
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-audio-track.hbs.html
@@ -0,0 +1,11 @@
+
+
+ {{#if isMuted}}
+
+ {{/if}}
+
+
+
diff --git a/core/modules/meet/client/tracks/remote-audio-track.js b/core/modules/meet/client/tracks/remote-audio-track.js
new file mode 100644
index 000000000..929cc2e47
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-audio-track.js
@@ -0,0 +1,17 @@
+import { trackAttach, trackDetach } from './utils'
+
+Template.remoteAudioTrack.onCreated(function () {
+ this.isMuted = new ReactiveVar(false)
+})
+
+Template.remoteAudioTrack.onRendered(function () {
+ this.autorun(() => trackAttach(this))
+})
+
+Template.remoteAudioTrack.onDestroyed(function () {
+ this.autorun(() => trackDetach(this))
+})
+
+Template.remoteAudioTrack.helpers({
+ isMuted: () => Template.instance().isMuted.get(),
+})
diff --git a/core/modules/meet/client/tracks/remote-audio-track.scss b/core/modules/meet/client/tracks/remote-audio-track.scss
new file mode 100644
index 000000000..caff0b736
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-audio-track.scss
@@ -0,0 +1,22 @@
+.remote-audio {
+ position: relative;
+
+ .mute {
+ position: absolute;
+ z-index: 10;
+ top: 5px;
+ right: 35px;
+ font-size: 24px;
+
+ div:nth-child(1) {
+ position: absolute;
+ z-index: 11;
+ }
+
+ div:nth-child(2) {
+ position: absolute;
+ z-index: 12;
+ transform: scale(0.65);
+ }
+ }
+}
diff --git a/core/modules/meet/client/tracks/remote-desktop-track.hbs.html b/core/modules/meet/client/tracks/remote-desktop-track.hbs.html
new file mode 100644
index 000000000..b5d7d2660
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-desktop-track.hbs.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/core/modules/meet/client/tracks/remote-desktop-track.js b/core/modules/meet/client/tracks/remote-desktop-track.js
new file mode 100644
index 000000000..59506b2b6
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-desktop-track.js
@@ -0,0 +1,28 @@
+import { trackAttach, trackDetach } from './utils'
+
+Template.remoteDesktopTrack.onCreated(function () {
+ this.isMuted = new ReactiveVar(false)
+})
+
+Template.remoteDesktopTrack.onRendered(function () {
+ this.autorun(() => trackAttach(this))
+})
+
+Template.remoteDesktopTrack.onDestroyed(function () {
+ this.autorun(() => trackDetach(this))
+})
+
+Template.remoteDesktopTrack.events({
+ 'click .js-screenshare': function (event) {
+ event.preventDefault()
+ event.stopPropagation()
+ const { track } = Template.currentData()
+
+ if (!track) return
+ Session.set('fullscreenTrackId', Session.get('fullscreenTrackId') === track.getId() ? null : track.getId())
+ },
+})
+
+Template.remoteDesktopTrack.helpers({
+ isMuted: () => Template.instance().isMuted.get(),
+})
diff --git a/core/modules/meet/client/tracks/remote-fullscreen-track.hbs.html b/core/modules/meet/client/tracks/remote-fullscreen-track.hbs.html
new file mode 100644
index 000000000..115615e30
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-fullscreen-track.hbs.html
@@ -0,0 +1,12 @@
+
+
+
diff --git a/core/modules/meet/client/tracks/remote-fullscreen-track.js b/core/modules/meet/client/tracks/remote-fullscreen-track.js
new file mode 100644
index 000000000..64d30f6bb
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-fullscreen-track.js
@@ -0,0 +1,21 @@
+import { trackAttach, trackDetach } from './utils'
+
+Template.remoteFullscreen.onCreated(function () {
+ this.isMuted = new ReactiveVar(false)
+})
+
+Template.remoteFullscreen.onRendered(function () {
+ this.autorun(() => {
+ const { track } = Template.currentData()
+ trackAttach(this, `${track.getParticipantId()}-fullscreen`)
+ trackAttach(this, `${track.getParticipantId()}-fullscreenBackground`)
+ })
+})
+
+Template.remoteFullscreen.onDestroyed(function () {
+ this.autorun(() => {
+ const { track } = Template.currentData()
+ trackDetach(this, `${track.getParticipantId()}-fullscreen`)
+ trackDetach(this, `${track.getParticipantId()}-fullscreenBackground`)
+ })
+})
diff --git a/core/modules/meet/client/tracks/remote-video-track.hbs.html b/core/modules/meet/client/tracks/remote-video-track.hbs.html
new file mode 100644
index 000000000..3bad706ca
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-video-track.hbs.html
@@ -0,0 +1,12 @@
+
+
+
{{displayName}}
+
+ {{#if isMuted}}
+

+ {{/if}}
+
+ {{#if desktopTrack}} {{> remoteDesktopTrack track=desktopTrack}} {{/if}}
+
+
+
diff --git a/core/modules/meet/client/tracks/remote-video-track.js b/core/modules/meet/client/tracks/remote-video-track.js
new file mode 100644
index 000000000..29bdd6e74
--- /dev/null
+++ b/core/modules/meet/client/tracks/remote-video-track.js
@@ -0,0 +1,33 @@
+import { trackAttach, trackDetach } from './utils'
+
+Template.remoteVideoTrack.onCreated(function () {
+ this.isMuted = new ReactiveVar(false)
+})
+
+Template.remoteVideoTrack.onRendered(function () {
+ this.autorun(() => trackAttach(this))
+})
+
+Template.remoteVideoTrack.onDestroyed(function () {
+ this.autorun(() => trackDetach(this))
+})
+
+Template.remoteVideoTrack.events({
+ 'click .webcam': function (event) {
+ event.preventDefault()
+ event.stopPropagation()
+ const { track } = Template.currentData()
+
+ if (!track) return
+ Session.set('fullscreenTrackId', Session.get('fullscreenTrackId') === track.getId() ? null : track.getId())
+ },
+})
+
+Template.remoteVideoTrack.helpers({
+ isMuted: () => Template.instance().isMuted.get(),
+ avatar: () =>
+ generateRandomAvatarURLForUser({
+ _id: 'usr_a',
+ profile: { name: 'Guest', avatar: 'cat' },
+ }),
+})
diff --git a/core/client/ui/remote-stream.scss b/core/modules/meet/client/tracks/remote-video-track.scss
similarity index 57%
rename from core/client/ui/remote-stream.scss
rename to core/modules/meet/client/tracks/remote-video-track.scss
index 44891945a..5251accc6 100644
--- a/core/client/ui/remote-stream.scss
+++ b/core/modules/meet/client/tracks/remote-video-track.scss
@@ -1,6 +1,6 @@
-@import '../_variables';
+@import '../../../../client/variables';
-.remote-stream {
+.remote-video {
z-index: 5;
position: relative;
width: 280px;
@@ -8,8 +8,6 @@
background-color: rgba($main-color, 0.9);
border-radius: 15px;
background-size: cover;
- background-repeat: no-repeat;
- background-position: 50% 50%;
overflow-x: hidden;
margin-bottom: 10px;
@@ -57,28 +55,9 @@
}
.webcam {
+ cursor: pointer;
width: 100%;
height: 100%;
- cursor: pointer;
-
- .mute {
- position: absolute;
- z-index: 10;
- top: 5px;
- right: 35px;
- font-size: 24px;
-
- div:nth-child(1) {
- position: absolute;
- z-index: 11;
- }
-
- div:nth-child(2) {
- position: absolute;
- z-index: 12;
- transform: scale(0.65);
- }
- }
img,
video {
@@ -130,54 +109,3 @@
}
}
}
-
-.fullscreen-modal {
- position: absolute !important;
- inset: 0 !important;
- transform: unset !important;
- width: calc(100% - 300px) !important;
- height: 100% !important;
- max-height: unset !important;
- max-width: unset !important;
- border-radius: 0px !important;
-
- .fullscreen {
- height: 100%;
- width: 100%;
- position: relative;
- display: flex;
- justify-content: center;
- align-items: center;
-
- .fullscreen-background {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- filter: blur(10px);
- -webkit-filter: blur(30px);
-
- video {
- width: 100% !important;
- height: 100% !important;
- object-fit: cover !important;
- }
- }
-
- .main {
- position: relative;
- object-fit: contain !important;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: auto;
-
- video {
- width: 100% !important;
- height: auto !important;
- }
- }
- }
-}
diff --git a/core/modules/meet/client/tracks/utils.js b/core/modules/meet/client/tracks/utils.js
new file mode 100644
index 000000000..b75393a2e
--- /dev/null
+++ b/core/modules/meet/client/tracks/utils.js
@@ -0,0 +1,82 @@
+import { meetLowLevel } from '../meet-low-level'
+
+const updateTrackMuteState = (track, template) => {
+ if (template.isMuted) {
+ template.isMuted.set(track.isMuted())
+ }
+}
+
+const getTrackType = (track) => {
+ if (track.getType() === 'audio') return 'audio'
+ else if (track.getType() === 'video') return track.getVideoType()
+}
+
+const getTrackId = (track) => `${track.getParticipantId()}-${getTrackType(track)}`
+
+const updateTrack = (type, tracks) => {
+ if (!Meteor.userId()) return
+
+ const user = Meteor.user({ fields: { 'profile.shareAudio': 1, 'profile.shareVideo': 1 } })
+ const track = tracks.find((t) => t.getType() === type)
+
+ if (!track) return
+ if (type === 'audio') user?.profile?.shareAudio ? track.unmute() : track.mute()
+ else if (type === 'video') user?.profile?.shareVideo ? track.unmute() : track.mute()
+}
+
+const attachLocalTracks = (tracks) => {
+ tracks.forEach((track) => {
+ if (track.getType() === 'video') {
+ const videoNode = document.querySelector('#video-stream-me')
+ track.attach(videoNode)
+ }
+ })
+}
+
+const replaceLocalTrack = (template, newTrack) => {
+ const localTracks = template.localTracks.get()
+
+ if (localTracks) {
+ template.localTracks.set(
+ localTracks.map((track) => {
+ if (track.getType() === newTrack.getType()) {
+ // Replace old tracks by the new one
+ meetLowLevel.room.replaceTrack(track, newTrack)
+ return newTrack
+ }
+ return track
+ })
+ )
+ attachLocalTracks([newTrack])
+ }
+}
+
+const trackAttach = (template, trackId) => {
+ const { track } = Template.currentData() // Do not replace it to use 'template' props, need use global 'Template' to get the track
+
+ if (!track) return
+ const el = document.getElementById(trackId || getTrackId(track))
+
+ if (template.isMuted) {
+ template.isMuted.set(track.isMuted())
+ }
+
+ track.addEventListener(jitsiMeetJS.events.track.TRACK_MUTE_CHANGED, () => updateTrackMuteState(track, template))
+
+ if (!el) return
+ track.attach(el)
+}
+
+const trackDetach = (template, trackId) => {
+ const { track } = Template.currentData()
+
+ if (!track) return
+ const el = document.getElementById(trackId || getTrackId(track))
+
+ track.removeEventListener(jitsiMeetJS.events.track.TRACK_MUTE_CHANGED, () => updateTrackMuteState(track, template))
+
+ if (!el) return
+ track.detach(el)
+}
+
+export { trackAttach, trackDetach, getTrackType, updateTrack, attachLocalTracks, replaceLocalTrack }
diff --git a/core/modules/meet/server/meet.js b/core/modules/meet/server/meet.js
index 929d6fcd4..774c198f0 100644
--- a/core/modules/meet/server/meet.js
+++ b/core/modules/meet/server/meet.js
@@ -1,9 +1,8 @@
import * as jwt from 'jsonwebtoken'
+import crypto from 'crypto'
import { canAccessZone } from '../../../lib/misc'
-const { randomUUID } = require('crypto')
-
const computeRoomName = (zone) => {
check(zone._id, Match.Id)
@@ -11,7 +10,7 @@ const computeRoomName = (zone) => {
let { uuid } = zone
if (!uuid) {
- uuid = randomUUID()
+ uuid = crypto.randomUUID()
Zones.update(zone._id, { $set: { uuid } })
}
@@ -50,7 +49,25 @@ const computeRoomToken = (user, roomName, moderator = false) => {
)
}
+const updateUserRoomName = (roomName) => {
+ const updateObject = roomName ? { $set: { 'meet.roomName': roomName } } : { $unset: { 'meet.roomName': 1 } }
+ Meteor.users.update(Meteor.userId(), updateObject)
+}
+
Meteor.methods({
+ computeRoomToken(roomName) {
+ check(roomName, Match.Maybe(String))
+ const user = Meteor.user()
+ if (!user) return
+
+ log('computeRoomToken: start', { roomName })
+ const token = computeRoomToken(user, roomName)
+ log('computeRoomToken: end', { roomName })
+
+ return token
+ },
+
+ // Meet conference
computeMeetRoomAccess(zoneId) {
if (!this.userId) return undefined
check(zoneId, Match.Id)
@@ -75,6 +92,49 @@ Meteor.methods({
return { roomName, token }
},
+
+ // Meet low level
+ updateUserRoomName(roomName) {
+ check(roomName, Match.Maybe(String))
+ const user = Meteor.user()
+ if (user.meet.roomName === roomName) return
+
+ log('updateUserRoomName: start', { roomName })
+ updateUserRoomName(roomName)
+ log('updateUserRoomName: end', { roomName })
+ },
+ getUserRoomName(userId) {
+ check(userId, Match.OneOf(null, Match.Id))
+ const user = Meteor.users.findOne({ _id: userId || Meteor.userId() }, { fields: { 'meet.roomName': 1 } })
+ if (!user) return
+
+ log('getUserRoomName: start', { userId: userId })
+ return user.meet?.roomName
+ log('getUserRoomName: end', { userId: userId })
+ },
+ computeMeetLowLevelRoomName(usersIds) {
+ if (!this.userId) return undefined
+ check(usersIds, Array)
+
+ log('computeMeetLowLevelRoomName: start', { usersIds })
+
+ const salt = Meteor.settings.meet.salt
+ const meetRoomName = usersIds
+ .sort((a, b) => a.localeCompare(b))
+ .join('-')
+ .toLowerCase()
+
+ const hmac = crypto.createHmac('sha1', salt)
+ hmac.setEncoding('base64')
+ hmac.write(meetRoomName)
+ hmac.end()
+ hashedMeetRoomName = hmac.read().toLowerCase()
+
+ updateUserRoomName(hashedMeetRoomName)
+ log('computeMeetLowLevelRoomName: end', { meetRoomName: hashedMeetRoomName })
+
+ return hashedMeetRoomName
+ },
})
export { computeRoomName, computeRoomToken }
diff --git a/core/modules/messages/client/messages.js b/core/modules/messages/client/messages.js
index 027f6c2db..97bd69d1a 100644
--- a/core/modules/messages/client/messages.js
+++ b/core/modules/messages/client/messages.js
@@ -52,33 +52,6 @@ messagesModule = {
Session.set('messagesChannel', channel) // set console in the new channel
},
- async sendWebRTCMessage(channel, content) {
- try {
- let showPopInOverEmitter = true
- if (channel.includes('zon_')) {
- // only show the pop-in over the character when the targeted channel is the active zone
- const zone = zoneManager.currentZone(Meteor.user())
- if (!zone || zone._id !== channel) return
-
- await sendDataToUsersInZone('text', { content, channel }, Meteor.userId())
- } else {
- const userIds = userProximitySensor.filterNearUsers(channel.split(';'))
- showPopInOverEmitter = !!userIds.length
- await sendDataToUsers('text', { content, channel }, Meteor.userId(), userIds)
- }
-
- // simulate a message from himself to show a pop-in over user's head
- if (showPopInOverEmitter)
- userManager.onPeerDataReceived({
- emitter: Meteor.userId(),
- data: { content, channel },
- type: 'text',
- })
- } catch (err) {
- if (err.message !== 'no-targets') lp.notif.error(err)
- }
- },
-
async sendMessage(channel, content, file) {
if (content.length >= messageMaxLength) throw new Error(`The message is too long (> ${messageMaxLength} chars)`)
content = lp.purify(content).trim()
@@ -103,8 +76,6 @@ messagesModule = {
})
)
- if (!channel.includes('qst_') && content.length) this.sendWebRTCMessage(channel, content)
-
return messageId
},
diff --git a/core/modules/punch-ability/client/punch-ability.js b/core/modules/punch-ability/client/punch-ability.js
index fe9f5199a..cdd1fafb4 100644
--- a/core/modules/punch-ability/client/punch-ability.js
+++ b/core/modules/punch-ability/client/punch-ability.js
@@ -21,16 +21,6 @@ const punch = (users) => {
}
window.addEventListener('load', () => {
- window.addEventListener(eventTypes.onPeerDataReceived, (e) => {
- const { data, userEmitter } = e.detail
- if (data.type !== 'punch') return
-
- if (!userProximitySensor.isUserNear(userEmitter)) return
- playPunchAnimation()
-
- userManager.getCharacter(Meteor.userId())?.onDamage()
- })
-
hotkeys('x', { scope: scopes.player }, (e) => {
const user = Meteor.user({
fields: { _id: 1, 'profile.levelId': 1, roles: 1 },
diff --git a/core/modules/shout-ability/client/shout-ability.js b/core/modules/shout-ability/client/shout-ability.js
index f21324af2..f8b20d5aa 100644
--- a/core/modules/shout-ability/client/shout-ability.js
+++ b/core/modules/shout-ability/client/shout-ability.js
@@ -26,18 +26,6 @@ window.addEventListener('load', () => {
track.stop()
})
- hotkeys('r', { keyup: true, scope: scopes.player }, (event) => {
- if (event.repeat) return
-
- const user = Meteor.user({
- fields: { _id: 1, 'profile.levelId': 1, roles: 1 },
- })
-
- if (!user || !canUseLevelFeature(user, 'shout', true)) return
-
- userVoiceRecorderAbility.recordVoice(event.type === 'keydown', sendAudioChunksToUsersInZone)
- })
-
const onMenuOptionSelected = (e) => {
const { option } = e.detail
const user = Meteor.user({
diff --git a/core/modules/textual-communication-tools/client/textual-communication-tools.js b/core/modules/textual-communication-tools/client/textual-communication-tools.js
index 0fde5ab79..22efa26b7 100644
--- a/core/modules/textual-communication-tools/client/textual-communication-tools.js
+++ b/core/modules/textual-communication-tools/client/textual-communication-tools.js
@@ -66,26 +66,13 @@ const onMenuOptionSelected = (e) => {
}
}
-const onPeerDataReceived = (e) => {
- const { data, meta } = e.detail
- if (data.type !== 'text') return
- if (!meta['pop-in']) return
-
- meta['pop-in'].on('click', () => {
- if (!data.data.channel) return
- openMessagingInterface(data.data.channel)
- })
-}
-
Template.textualCommunicationTools.onCreated(() => {
messagesModule.init()
window.addEventListener(eventTypes.onMenuOptionSelected, onMenuOptionSelected)
- window.addEventListener(eventTypes.onPeerDataReceived, onPeerDataReceived)
})
Template.textualCommunicationTools.onDestroyed(() => {
window.removeEventListener(eventTypes.onMenuOptionSelected, onMenuOptionSelected)
- window.removeEventListener(eventTypes.onPeerDataReceived, onPeerDataReceived)
})
Template.textualCommunicationTools.helpers({
diff --git a/core/server/accounts.js b/core/server/accounts.js
index b3fc0ca32..0d14654f7 100644
--- a/core/server/accounts.js
+++ b/core/server/accounts.js
@@ -28,6 +28,9 @@ Accounts.validateNewUser(() => true)
Accounts.onLogin((param) => {
const user = Meteor.users.findOne(param.user._id)
+ // We should be sure that user room name is reset on login
+ Meteor.call('updateUserRoomName', undefined)
+
log('onLogin: start', {
userId: user._id,
ip: param.connection?.httpHeaders?.['x-forwarded-for'],
diff --git a/core/server/analytics.js b/core/server/analytics.js
index 39f774b01..9599e71de 100644
--- a/core/server/analytics.js
+++ b/core/server/analytics.js
@@ -94,63 +94,6 @@ Meteor.methods({
users_attending_count: properties.usersAttendingCount,
})
},
- analyticsStreamStarted(properties) {
- const { userId } = this
- if (!userId) return
-
- check(properties, {
- peerUserId: String,
- streamId: String,
- kind: String,
- })
- analytics.track(userId, 'π Stream started', {
- stream_id: properties.streamId,
- peer_user_id: properties.peerUserId,
- kind: properties.kind,
- })
- },
- analyticsStreamEstablished(properties) {
- const { userId } = this
- if (!userId) return
-
- check(properties, {
- peerUserId: String,
- streamId: String,
- kind: String,
- elapsed: Number,
- })
- analytics.track(userId, 'β
Stream established', {
- stream_id: properties.streamId,
- peer_user_id: properties.peerUserId,
- kind: properties.kind,
- elapsed: properties.elapsed,
- })
- },
- analyticsMissingTrack(properties) {
- const { userId } = this
- if (!userId) return
-
- check(properties, {
- peerUserId: String,
- streamId: String,
- kind: String,
- })
-
- analytics.track(userId, 'β Missing track', {
- stream_id: properties.streamId,
- peer_user_id: properties.peerUserId,
- kind: properties.kind,
- })
- },
- analyticsCredentialsExpired() {
- const { userId } = this
- if (!userId) return
-
- const user = Meteor.user()
- analytics.track(userId, 'βΒ Credentials expired', {
- level_id: user.profile.levelId,
- })
- },
analyticsDiscussionEnd(properties) {
const { userId } = this
if (!userId) return
diff --git a/core/server/lemverse.js b/core/server/lemverse.js
index bac38e32e..312c4771a 100644
--- a/core/server/lemverse.js
+++ b/core/server/lemverse.js
@@ -1,34 +1,10 @@
-import { PeerServer } from 'peer'
import crypto from 'crypto'
-if (Meteor.settings.peer.server.start) {
- // eslint-disable-next-line new-cap
- PeerServer({
- ...Meteor.settings.peer.server,
- path: Meteor.settings.peer.path,
- })
-}
-
Accounts.emailTemplates.from = Meteor.settings.email.from
AccountsGuest.enabled = true
AccountsGuest.forced = true
AccountsGuest.name = true
-const getTURNCredentialsExpiration = () => {
- const duration = Meteor.settings.peer?.client.credentialDuration || 86400
- return parseInt(Date.now() / 1000, 10) + duration
-}
-
-const generateTURNCredentials = (name, secret) => {
- const username = [getTURNCredentialsExpiration(), name].join(':')
- const hmac = crypto.createHmac('sha1', secret)
- hmac.setEncoding('base64')
- hmac.write(username)
- hmac.end()
-
- return { username, password: hmac.read() }
-}
-
Meteor.publish('tiles', function (levelId) {
check(levelId, Match.Maybe(String))
if (!this.userId) return undefined
@@ -46,30 +22,6 @@ Meteor.publish('characters', function () {
return Characters.find()
})
-Meteor.methods({
- getPeerConfig() {
- if (!this.userId) throw new Meteor.Error('missing-user', 'A valid user is required')
- const { url, config, port, secret } = Meteor.settings.peer.client
- const { username, password: credential } = generateTURNCredentials(this.userId, secret)
-
- const iceServers = config.iceServers.map(({ urls, auth }) => {
- if (!auth) return { urls }
- return { urls, username, credential }
- })
-
- return {
- url,
- port,
- path: Meteor.settings.peer.path,
- expiration: getTURNCredentialsExpiration(),
- config: {
- ...config,
- iceServers,
- },
- }
- },
-})
-
lp.defer(() => {
if (Levels.find().count() > 0) return