diff --git a/package-lock.json b/package-lock.json index 642bd72..196f6b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "@reduxjs/toolkit": "^1.9.7", "axios": "^1.6.0", "dayjs": "^1.11.10", + "peerjs": "^1.5.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.1.3", @@ -408,6 +409,78 @@ "node": ">=6.9.0" } }, + "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-darwin-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz", + "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz", + "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz", + "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz", + "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-win32-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz", + "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", @@ -557,342 +630,6 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, "node_modules/@esbuild/win32-x64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", @@ -1095,6 +832,14 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@msgpack/msgpack": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", + "integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@mui/base": { "version": "5.0.0-beta.34", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.34.tgz", @@ -1917,6 +1662,35 @@ } ] }, + "node_modules/cbor-extract": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz", + "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.1.1" + }, + "bin": { + "download-cbor-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", + "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", + "@cbor-extract/cbor-extract-linux-x64": "2.2.0", + "@cbor-extract/cbor-extract-win32-x64": "2.2.0" + } + }, + "node_modules/cbor-x": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.5.4.tgz", + "integrity": "sha512-PVKILDn+Rf6MRhhcyzGXi5eizn1i0i3F8Fe6UMMxXBnWkalq9+C5+VTmlIjAYM4iF2IYF2N+zToqAfYOp+3rfw==", + "optionalDependencies": { + "cbor-extract": "^2.1.1" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -2059,6 +1833,15 @@ "node": ">=0.4.0" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2426,6 +2209,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2641,20 +2429,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3197,6 +2971,20 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-releases": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", @@ -3344,6 +3132,37 @@ "node": ">=8" } }, + "node_modules/peerjs": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/peerjs/-/peerjs-1.5.2.tgz", + "integrity": "sha512-pPrtNwPyWJHRPxy2y+rHcdlrG8UwUBB1nl+3Yj6r7FLwcbBpcB2NvGNvLvcrxAVGGGX9fsdA5VT5zBKTZcm1DQ==", + "dependencies": { + "@msgpack/msgpack": "^2.8.0", + "cbor-x": "1.5.4", + "eventemitter3": "^4.0.7", + "peerjs-js-binarypack": "^2.1.0", + "webrtc-adapter": "^8.0.0" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/peer" + } + }, + "node_modules/peerjs-js-binarypack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-2.1.0.tgz", + "integrity": "sha512-YIwCC+pTzp3Bi8jPI9UFKO0t0SLo6xALnHkiNt/iUFmUUZG0fEEmEyFKvjsDKweiFitzHRyhuh6NvyJZ4nNxMg==", + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/peer" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -3807,6 +3626,11 @@ "loose-envify": "^1.1.0" } }, + "node_modules/sdp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.0.tgz", + "integrity": "sha512-d7wDPgDV3DDiqulJjKiV2865wKsJ34YI+NDREbm+FySq6WuKOikwyNQcm+doLAZ1O6ltdO0SeKle2xMpN3Brgw==" + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -4148,6 +3972,18 @@ } } }, + "node_modules/webrtc-adapter": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-8.2.3.tgz", + "integrity": "sha512-gnmRz++suzmvxtp3ehQts6s2JtAGPuDPjA1F3a9ckNpG1kYdYuHWYpazoAnL9FS5/B21tKlhkorbdCXat0+4xQ==", + "dependencies": { + "sdp": "^3.2.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index c97062e..804b946 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@reduxjs/toolkit": "^1.9.7", "axios": "^1.6.0", "dayjs": "^1.11.10", + "peerjs": "^1.5.2", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^8.1.3", diff --git a/public/app-icons/peer-tac-toe-icon.svg b/public/app-icons/peer-tac-toe-icon.svg new file mode 100644 index 0000000..6299ec4 --- /dev/null +++ b/public/app-icons/peer-tac-toe-icon.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/components/AppBar/Images.ts b/src/components/AppBar/Images.ts index 799406d..bc6ad97 100644 --- a/src/components/AppBar/Images.ts +++ b/src/components/AppBar/Images.ts @@ -1,5 +1,6 @@ const IMAGES = { firefox: new URL("/app-icons/firefox-icon.png", import.meta.url).href, + peerTacToe: new URL("/app-icons/peer-tac-toe-icon.svg", import.meta.url).href, libreOffice: new URL("/app-icons/libre-office-icon.png", import.meta.url).href, rhythmBox: new URL("/app-icons/rhythmbox-icon.png", import.meta.url).href, settings: new URL("/app-icons/settings-icon.png", import.meta.url).href, diff --git a/src/components/Apps/Apps.ts b/src/components/Apps/Apps.ts index 83b6519..827edb1 100644 --- a/src/components/Apps/Apps.ts +++ b/src/components/Apps/Apps.ts @@ -2,11 +2,13 @@ import { JSX } from "react"; import Settings from "./Settings/Settings.tsx"; import DefaultApp from "./Default/DefaultApp.tsx"; import MfTest from "src/components/Apps/MicrofrontendTest/MfTest.tsx"; +import PeerTacToe from "src/components/Apps/TicTacToe/PeerTacToe.tsx"; export const APPS: { [key: string]: () => JSX.Element } = { defaultApp: DefaultApp, settings: Settings, - mfTest: MfTest + mfTest: MfTest, + peerTacToe: PeerTacToe }; export type AppKey = keyof typeof APPS; diff --git a/src/components/Apps/TicTacToe/PeerTacToe.css b/src/components/Apps/TicTacToe/PeerTacToe.css new file mode 100644 index 0000000..d7797e7 --- /dev/null +++ b/src/components/Apps/TicTacToe/PeerTacToe.css @@ -0,0 +1,72 @@ +#tic-tac-toe { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + height: calc(100% - 20px); + padding: 10px; +} + +#ttt-game { + height: 100%; + max-width: 100%; + border-collapse: collapse; + aspect-ratio: 1/1 !important; +} + +#ttt-game td { + border: 1px white solid; + aspect-ratio: 1/1 !important; +} + +#ttt-game tbody tr:last-child td { + border-bottom: none; +} + +#ttt-game tbody tr:first-child td { + border-top: none; +} + +#ttt-game tr td:first-child { + border-left: none; +} + +#ttt-game tr td:last-child { + border-right: none; +} + +#ttt-game tbody{ + aspect-ratio: 1/1 !important; +} + +#ttt-game tr{ + aspect-ratio: 1/3 !important; +} + +.ttt-btn { + height: 100%; + width: 100%; + font-size: xxx-large; + background-color: transparent; + border: none; + cursor: pointer; + color: transparent; + aspect-ratio: 1/1 !important; +} + +.ttt-btn:disabled { + cursor: not-allowed; + border: none; + color: white; + aspect-ratio: 1/1 !important; +} + +.ttt-btn:hover{ + color: white; + aspect-ratio: 1/1 !important; +} + +.ttt-btn-svg{ + height: 5rem; + aspect-ratio: 1/1 !important; +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/PeerTacToe.tsx b/src/components/Apps/TicTacToe/PeerTacToe.tsx new file mode 100644 index 0000000..c92fe74 --- /dev/null +++ b/src/components/Apps/TicTacToe/PeerTacToe.tsx @@ -0,0 +1,30 @@ +import "./PeerTacToe.css"; +import { useState } from "react"; +import { TTTGame } from "./components/TTTGame.tsx"; +import { TTTLogin } from "./components/TTTLogin.tsx"; +import { DataConnection, Peer } from "peerjs"; + +export default function PeerTacToe() { + const [peer, setPeer] = useState(undefined); + const [conn, setConn] = useState(undefined); + const [playerNum, setPlayerNum] = useState(0); + const [username, setUsername] = useState(""); + const [oppUsername, setOppUsername] = useState(""); + const [gameOn, setGameOn] = useState(false) + + return ( +
+ {gameOn && conn + ? () + : () + } +
+ ); +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/components/Circle.tsx b/src/components/Apps/TicTacToe/components/Circle.tsx new file mode 100644 index 0000000..858ff1f --- /dev/null +++ b/src/components/Apps/TicTacToe/components/Circle.tsx @@ -0,0 +1,22 @@ +export function Circle() { + return ( + + + Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + ) +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/components/Cross.tsx b/src/components/Apps/TicTacToe/components/Cross.tsx new file mode 100644 index 0000000..5b24de4 --- /dev/null +++ b/src/components/Apps/TicTacToe/components/Cross.tsx @@ -0,0 +1,23 @@ +export function Cross() { + return ( + + + Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + ); +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/components/Empty.tsx b/src/components/Apps/TicTacToe/components/Empty.tsx new file mode 100644 index 0000000..5caa3c2 --- /dev/null +++ b/src/components/Apps/TicTacToe/components/Empty.tsx @@ -0,0 +1,14 @@ +export function Empty() { + return ( + + + Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + ); +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/components/TTTBtn.tsx b/src/components/Apps/TicTacToe/components/TTTBtn.tsx new file mode 100644 index 0000000..d9c3123 --- /dev/null +++ b/src/components/Apps/TicTacToe/components/TTTBtn.tsx @@ -0,0 +1,21 @@ +type TTTBtnProps = { + btnIndex: number; + isBtnDisabled: (index: number) => boolean; + isBtnHidden: (index: number) => boolean; + getBtnState: (index: number) => string | JSX.Element; + setField: (index: number) => void; +} + +export function TTTBtn(props: TTTBtnProps) { + + return ( + + + + ); +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/components/TTTGame.tsx b/src/components/Apps/TicTacToe/components/TTTGame.tsx new file mode 100644 index 0000000..3f00b3a --- /dev/null +++ b/src/components/Apps/TicTacToe/components/TTTGame.tsx @@ -0,0 +1,142 @@ +import { TTTRow } from "./TTTRow.tsx"; +import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; +import { DataConnection } from "peerjs"; + +type TTTGameProps = { + setConn: Dispatch>; + conn: DataConnection; + setPlayerNum: Dispatch>; + playerNum: number; + setGameOn: Dispatch>; +} + +export function TTTGame(props: TTTGameProps) { + const [gameState, setGameState] = useState([0, 0, 0, 0, 0, 0, 0, 0, 0]); + const [newGameState, setNewGameState] = useState([0, 0, 0, 0, 0, 0, 0, 0, 0]); + const inputType = useRef(0); + + useEffect(() => { + props.conn.on("data", function(data) { + inputType.current = 2; + console.log("recv", data); + setNewGameState(data as number[]); + }); + props.conn.on("close", function() { + props.setGameOn(false); + props.setConn(undefined); + }); + }, [props.conn]); + + function setField(index: number) { + setNewGameState(newGameState => { + const tmpGameState = [...newGameState]; + tmpGameState[index] = props.playerNum; + inputType.current = 1; + props.conn.send(tmpGameState); + return tmpGameState; + }); + } + + useEffect(() => { + console.log("use-effect-new", newGameState); + if (inputType.current > 0) { + evaluate(newGameState, gameState); + setGameState(newGameState); + inputType.current = 0; + } + }, [newGameState]); + + function endGame(status: number) { + console.log("exit-game", status); + props.setPlayerNum(status); + props.setGameOn(false); + } + + function checkIfWon(num1: number, num2: number, num3: number, newGameState: number[]) { + if (newGameState[num1] == newGameState[num2] && newGameState[num2] == newGameState[num3]) { + if (newGameState[num1] == 0) { + return; + } else if (newGameState[num1] == props.playerNum) { + endGame(3); + } else { + endGame(4); + } + } + } + + function evaluate(newGameState: number[], oldGameState: number[]) { + console.log("=============evaluate=========="); + console.log("old", oldGameState); + console.log("new", newGameState); + //Check if player can do move + if ((gameState.filter(state => state == 0).length % 2 == props.playerNum - 1) && inputType.current == 1) { + console.log("player not at turn"); + endGame(5); + } + + //Check if not more than on field has been changed + + //Check if field has not already been set + for (let i = 0; i < oldGameState.length - 1; i++) { + if (newGameState[i] != 0 && oldGameState[i] != 0 && newGameState[i] != oldGameState[i]) { + console.log("field already set"); + endGame(5); + } + } + + //Check if someone has won + //horizontal + for (let i = 1; i <= 3; i++) { + checkIfWon(i * 3 - 1, i * 3 - 2, i * 3 - 3, newGameState); + } + //vertical + for (let i = 6; i <= 8; i++) { + checkIfWon(i, i - 3, i - 6, newGameState); + } + //diagonal + checkIfWon(0, 4, 8, newGameState); + checkIfWon(2, 4, 6, newGameState); + console.log("=============ev done=========="); + return; + } + + + function isBtnDisabled(index: number): boolean { + return gameState.filter(state => state == 0).length % 2 == props.playerNum - 1 || gameState[index] != 0; + } + + function getBtnState(index: number): JSX.Element | string { + switch (gameState[index]) { + case 1: + return "X"; + case 2: + return "O"; + } + if (gameState.filter(state => state == 0).length % 2 != props.playerNum - 1) { + switch (props.playerNum) { + case 1: + return "X"; + case 2: + return "O"; + } + } + return "C"; + } + + function isBtnHidden(index: number): boolean { + return getBtnState(index) == "C"; + } + + return ( + + + + + + +
+ ); +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/components/TTTLogin.tsx b/src/components/Apps/TicTacToe/components/TTTLogin.tsx new file mode 100644 index 0000000..0a87a69 --- /dev/null +++ b/src/components/Apps/TicTacToe/components/TTTLogin.tsx @@ -0,0 +1,120 @@ +import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; +import { DataConnection, Peer } from "peerjs"; +import { Alert, AlertColor, Box, Button, Container, TextField, Typography } from "@mui/material"; + +type TTTLoginProps = { + peer: Peer | undefined; + setPeer: Dispatch>; + setConn: Dispatch> + setPlayerNum: Dispatch>; + playerNum: number + username: string; + setUsername: Dispatch>; + oppUsername: string; + setOppUsername: Dispatch>; + setGameOn: Dispatch>; +} + +export function TTTLogin(props: TTTLoginProps) { + const [msg, setMsg] = useState("Connect to a game"); + const [msgSeverity, setMsgSeverity] = useState("info"); + + function onConnOpen(conn: DataConnection) { + props.setConn(conn); + props.setGameOn(true); + } + + function onPeerClose() { + props.setPlayerNum(6); + props.setGameOn(false); + props.setConn(undefined); + props.setPeer(undefined); + } + + function setupPeer() { + const peer = new Peer(props.username, { debug: 3 }); + peer.on("open", function(id) { + props.setUsername(id); + props.setPeer(peer); + peer.on("connection", function(conn) { + onConnOpen(conn); + props.setOppUsername(conn.peer); + props.setPlayerNum(2); + }); + peer.on("close", function() { + onPeerClose(); + }); + peer.on("disconnected", function() { + onPeerClose(); + }); + }); + } + + function connectToGame() { + props.setPeer(peer => { + if (peer) { + const conn = peer.connect(props.oppUsername); + conn.on("open", function() { + onConnOpen(conn); + props.setPlayerNum(1); + }); + + } + return peer; + }); + + } + + useEffect(() => { + let tmpMsg = "Connect to a game"; + let tmpMsgSeverity: AlertColor = "info"; + switch (props.playerNum) { + case 3: + tmpMsg = "You won against " + props.oppUsername; + break; + case 4: + tmpMsg = "You lost against " + props.oppUsername; + break; + case 5: + tmpMsg = "Error: Game corrupted"; + tmpMsgSeverity = "error"; + break; + case 6: + tmpMsg = "Error: Disconnected"; + tmpMsgSeverity = "error"; + break; + } + setMsg(tmpMsg); + setMsgSeverity(tmpMsgSeverity); + }, [props.playerNum]); + + return ( + <> + + + + {msg} + + + props.setUsername(e.target.value)} + disabled={!!props.peer} /> + + props.setOppUsername(e.target.value)} disabled={!props.peer} /> + + + + + + ); +} \ No newline at end of file diff --git a/src/components/Apps/TicTacToe/components/TTTRow.tsx b/src/components/Apps/TicTacToe/components/TTTRow.tsx new file mode 100644 index 0000000..eae2861 --- /dev/null +++ b/src/components/Apps/TicTacToe/components/TTTRow.tsx @@ -0,0 +1,32 @@ +import { TTTBtn } from "./TTTBtn.tsx"; + +type TTTRowProps = { + rowIndex: number; + isBtnDisabled: (index: number) => boolean; + isBtnHidden: (index: number) => boolean; + getBtnState: (index: number) => string | JSX.Element; + setField: (index: number) => void; +} + +export function TTTRow(props: TTTRowProps) { + + return ( + + + + + + ); +} \ No newline at end of file diff --git a/src/components/Desktop/App/App.css b/src/components/Desktop/App/App.css index 5bc8594..8fe7913 100644 --- a/src/components/Desktop/App/App.css +++ b/src/components/Desktop/App/App.css @@ -86,7 +86,7 @@ } .appContent { - height: 100%; + height: calc(100% - 35px); width: 100%; } diff --git a/src/components/Desktop/Desktop.tsx b/src/components/Desktop/Desktop.tsx index 88c3a8d..44375bf 100644 --- a/src/components/Desktop/Desktop.tsx +++ b/src/components/Desktop/Desktop.tsx @@ -115,7 +115,6 @@ function Desktop() { width: (desktopSelectPos.width + "px"), display: (desktopSelectPos.width + desktopSelectPos.height > 0 ? "block" : "none") }} /> - { openedApps.map((appConfig) => (