diff --git a/packages/@dcl/inspector/package-lock.json b/packages/@dcl/inspector/package-lock.json
index e59fdb817..97e060c0c 100644
--- a/packages/@dcl/inspector/package-lock.json
+++ b/packages/@dcl/inspector/package-lock.json
@@ -17,11 +17,13 @@
"@dcl/ecs-math": "2.0.2",
"@dcl/rpc": "^1.1.1",
"@dcl/schemas": "^6.11.1",
+ "@reduxjs/toolkit": "^1.9.5",
"@testing-library/react": "^14.0.0",
"@types/node": "^18.11.18",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/react-modal": "^3.16.0",
+ "@types/redux-saga": "^0.10.5",
"@vscode/webview-ui-toolkit": "^1.2.2",
"@well-known-components/pushable-channel": "^1.0.3",
"classnames": "^2.3.2",
@@ -37,7 +39,10 @@
"react-dom": "^18.2.0",
"react-icons": "^4.7.1",
"react-modal": "^3.16.1",
+ "react-redux": "^8.1.0",
"react-resizable-panels": "^0.0.48",
+ "redux-saga": "^1.2.3",
+ "redux-saga-test-plan": "^4.0.6",
"typescript": "^5.0.2"
}
},
@@ -510,6 +515,87 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@redux-saga/core": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz",
+ "integrity": "sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.6.3",
+ "@redux-saga/deferred": "^1.2.1",
+ "@redux-saga/delay-p": "^1.2.1",
+ "@redux-saga/is": "^1.1.3",
+ "@redux-saga/symbols": "^1.1.3",
+ "@redux-saga/types": "^1.2.1",
+ "redux": "^4.0.4",
+ "typescript-tuple": "^2.2.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/redux-saga"
+ }
+ },
+ "node_modules/@redux-saga/deferred": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz",
+ "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==",
+ "dev": true
+ },
+ "node_modules/@redux-saga/delay-p": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz",
+ "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==",
+ "dev": true,
+ "dependencies": {
+ "@redux-saga/symbols": "^1.1.3"
+ }
+ },
+ "node_modules/@redux-saga/is": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz",
+ "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==",
+ "dev": true,
+ "dependencies": {
+ "@redux-saga/symbols": "^1.1.3",
+ "@redux-saga/types": "^1.2.1"
+ }
+ },
+ "node_modules/@redux-saga/symbols": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz",
+ "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==",
+ "dev": true
+ },
+ "node_modules/@redux-saga/types": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz",
+ "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==",
+ "dev": true
+ },
+ "node_modules/@reduxjs/toolkit": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
+ "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
+ "dev": true,
+ "dependencies": {
+ "immer": "^9.0.21",
+ "redux": "^4.2.1",
+ "redux-thunk": "^2.4.2",
+ "reselect": "^4.1.8"
+ },
+ "peerDependencies": {
+ "react": "^16.9.0 || ^17.0.0 || ^18",
+ "react-redux": "^7.2.1 || ^8.0.2"
+ },
+ "peerDependenciesMeta": {
+ "react": {
+ "optional": true
+ },
+ "react-redux": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@semantic-ui-react/event-stack": {
"version": "3.1.3",
"dev": true,
@@ -646,6 +732,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dev": true,
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.4",
"dev": true,
@@ -723,6 +819,16 @@
"@types/react": "*"
}
},
+ "node_modules/@types/redux-saga": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@types/redux-saga/-/redux-saga-0.10.5.tgz",
+ "integrity": "sha512-sqan7rs+PylBB5u2reL1hm+f0pDsZY1D1/o1vtgjrS4ErjxthK59zKCQyZhSLRcQDiOh3Pwp4fNQyn4A0CTdGA==",
+ "deprecated": "This is a stub types definition for redux-saga (https://github.com/redux-saga/redux-saga). redux-saga provides its own type definitions, so you don't need @types/redux-saga installed!",
+ "dev": true,
+ "dependencies": {
+ "redux-saga": "*"
+ }
+ },
"node_modules/@types/scheduler": {
"version": "0.16.3",
"dev": true,
@@ -738,6 +844,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
+ "dev": true
+ },
"node_modules/@types/yargs": {
"version": "17.0.24",
"dev": true,
@@ -1618,6 +1730,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fsm-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fsm-iterator/-/fsm-iterator-1.1.0.tgz",
+ "integrity": "sha512-hg47CNYdIGJ5m9WSKh617LHRdvJo4PiF0VkncFLwPVxKvBEQfSPd1qx/xLV/eSusewEu0C8eUFrsLsWlBgIcOg==",
+ "dev": true
+ },
"node_modules/function-bind": {
"version": "1.1.1",
"dev": true,
@@ -1788,6 +1906,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/immer": {
+ "version": "9.0.21",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+ "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
+ "dev": true,
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/immer"
+ }
+ },
"node_modules/impetus": {
"version": "0.8.8",
"dev": true,
@@ -2225,6 +2353,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "dev": true
+ },
+ "node_modules/lodash.ismatch": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
+ "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==",
+ "dev": true
+ },
"node_modules/long": {
"version": "4.0.0",
"dev": true,
@@ -2668,6 +2808,55 @@
"react-dom": "^16.8.0 || ^17 || ^18"
}
},
+ "node_modules/react-redux": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.0.tgz",
+ "integrity": "sha512-CtHZzAOxi7GQvTph4dVLWwZHAWUjV2kMEQtk50OrN8z3gKxpWg3Tz7JfDw32N3Rpd7fh02z73cF6yZkK467gbQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.12.1",
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "@types/use-sync-external-store": "^0.0.3",
+ "hoist-non-react-statics": "^3.3.2",
+ "react-is": "^18.0.0",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "peerDependencies": {
+ "@reduxjs/toolkit": "^1 || ^2.0.0-beta.0",
+ "@types/react": "^16.8 || ^17.0 || ^18.0",
+ "@types/react-dom": "^16.8 || ^17.0 || ^18.0",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0",
+ "react-native": ">=0.59",
+ "redux": "^4 || ^5.0.0-beta.0"
+ },
+ "peerDependenciesMeta": {
+ "@reduxjs/toolkit": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ },
+ "react-native": {
+ "optional": true
+ },
+ "redux": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-redux/node_modules/react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+ "dev": true
+ },
"node_modules/react-resizable-panels": {
"version": "0.0.48",
"dev": true,
@@ -2825,6 +3014,40 @@
"@babel/runtime": "^7.9.2"
}
},
+ "node_modules/redux-saga": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz",
+ "integrity": "sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==",
+ "dev": true,
+ "dependencies": {
+ "@redux-saga/core": "^1.2.3"
+ }
+ },
+ "node_modules/redux-saga-test-plan": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/redux-saga-test-plan/-/redux-saga-test-plan-4.0.6.tgz",
+ "integrity": "sha512-ESdbFoDWCeJ/EiFdUNSCGtA2CC9tnuvHDm6k06gVFa98EIeR2hpzFkGk9kJ1/hpMUnYFp+OOEEITIrZeDYBfFg==",
+ "dev": true,
+ "dependencies": {
+ "fsm-iterator": "^1.1.0",
+ "lodash.isequal": "^4.5.0",
+ "lodash.ismatch": "^4.4.0"
+ },
+ "peerDependencies": {
+ "@redux-saga/is": "^1.0.1",
+ "@redux-saga/symbols": "^1.0.1",
+ "redux-saga": "^1.0.1"
+ }
+ },
+ "node_modules/redux-thunk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
+ "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
+ "dev": true,
+ "peerDependencies": {
+ "redux": "^4"
+ }
+ },
"node_modules/regenerator-runtime": {
"version": "0.13.11",
"dev": true,
@@ -2859,6 +3082,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/reselect": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
+ "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==",
+ "dev": true
+ },
"node_modules/right-now": {
"version": "1.0.0",
"dev": true,
@@ -3166,6 +3395,30 @@
"node": ">=12.20"
}
},
+ "node_modules/typescript-compare": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
+ "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==",
+ "dev": true,
+ "dependencies": {
+ "typescript-logic": "^0.0.0"
+ }
+ },
+ "node_modules/typescript-logic": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz",
+ "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==",
+ "dev": true
+ },
+ "node_modules/typescript-tuple": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz",
+ "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==",
+ "dev": true,
+ "dependencies": {
+ "typescript-compare": "^0.0.2"
+ }
+ },
"node_modules/universalify": {
"version": "0.2.0",
"dev": true,
@@ -3191,6 +3444,15 @@
"requires-port": "^1.0.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "dev": true,
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/victory-vendor": {
"version": "36.6.8",
"dev": true,
@@ -3676,6 +3938,71 @@
"version": "4.0.2",
"dev": true
},
+ "@redux-saga/core": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.2.3.tgz",
+ "integrity": "sha512-U1JO6ncFBAklFTwoQ3mjAeQZ6QGutsJzwNBjgVLSWDpZTRhobUzuVDS1qH3SKGJD8fvqoaYOjp6XJ3gCmeZWgA==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.6.3",
+ "@redux-saga/deferred": "^1.2.1",
+ "@redux-saga/delay-p": "^1.2.1",
+ "@redux-saga/is": "^1.1.3",
+ "@redux-saga/symbols": "^1.1.3",
+ "@redux-saga/types": "^1.2.1",
+ "redux": "^4.0.4",
+ "typescript-tuple": "^2.2.1"
+ }
+ },
+ "@redux-saga/deferred": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/deferred/-/deferred-1.2.1.tgz",
+ "integrity": "sha512-cmin3IuuzMdfQjA0lG4B+jX+9HdTgHZZ+6u3jRAOwGUxy77GSlTi4Qp2d6PM1PUoTmQUR5aijlA39scWWPF31g==",
+ "dev": true
+ },
+ "@redux-saga/delay-p": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/delay-p/-/delay-p-1.2.1.tgz",
+ "integrity": "sha512-MdiDxZdvb1m+Y0s4/hgdcAXntpUytr9g0hpcOO1XFVyyzkrDu3SKPgBFOtHn7lhu7n24ZKIAT1qtKyQjHqRd+w==",
+ "dev": true,
+ "requires": {
+ "@redux-saga/symbols": "^1.1.3"
+ }
+ },
+ "@redux-saga/is": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@redux-saga/is/-/is-1.1.3.tgz",
+ "integrity": "sha512-naXrkETG1jLRfVfhOx/ZdLj0EyAzHYbgJWkXbB3qFliPcHKiWbv/ULQryOAEKyjrhiclmr6AMdgsXFyx7/yE6Q==",
+ "dev": true,
+ "requires": {
+ "@redux-saga/symbols": "^1.1.3",
+ "@redux-saga/types": "^1.2.1"
+ }
+ },
+ "@redux-saga/symbols": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@redux-saga/symbols/-/symbols-1.1.3.tgz",
+ "integrity": "sha512-hCx6ZvU4QAEUojETnX8EVg4ubNLBFl1Lps4j2tX7o45x/2qg37m3c6v+kSp8xjDJY+2tJw4QB3j8o8dsl1FDXg==",
+ "dev": true
+ },
+ "@redux-saga/types": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@redux-saga/types/-/types-1.2.1.tgz",
+ "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==",
+ "dev": true
+ },
+ "@reduxjs/toolkit": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.5.tgz",
+ "integrity": "sha512-Rt97jHmfTeaxL4swLRNPD/zV4OxTes4la07Xc4hetpUW/vc75t5m1ANyxG6ymnEQ2FsLQsoMlYB2vV1sO3m8tQ==",
+ "dev": true,
+ "requires": {
+ "immer": "^9.0.21",
+ "redux": "^4.2.1",
+ "redux-thunk": "^2.4.2",
+ "reselect": "^4.1.8"
+ }
+ },
"@semantic-ui-react/event-stack": {
"version": "3.1.3",
"dev": true,
@@ -3778,6 +4105,16 @@
"version": "3.0.0",
"dev": true
},
+ "@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dev": true,
+ "requires": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"@types/istanbul-lib-coverage": {
"version": "2.0.4",
"dev": true
@@ -3844,6 +4181,15 @@
"@types/react": "*"
}
},
+ "@types/redux-saga": {
+ "version": "0.10.5",
+ "resolved": "https://registry.npmjs.org/@types/redux-saga/-/redux-saga-0.10.5.tgz",
+ "integrity": "sha512-sqan7rs+PylBB5u2reL1hm+f0pDsZY1D1/o1vtgjrS4ErjxthK59zKCQyZhSLRcQDiOh3Pwp4fNQyn4A0CTdGA==",
+ "dev": true,
+ "requires": {
+ "redux-saga": "*"
+ }
+ },
"@types/scheduler": {
"version": "0.16.3",
"dev": true
@@ -3856,6 +4202,12 @@
"version": "4.0.2",
"dev": true
},
+ "@types/use-sync-external-store": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
+ "integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
+ "dev": true
+ },
"@types/yargs": {
"version": "17.0.24",
"dev": true,
@@ -4426,6 +4778,12 @@
"version": "1.0.1",
"dev": true
},
+ "fsm-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fsm-iterator/-/fsm-iterator-1.1.0.tgz",
+ "integrity": "sha512-hg47CNYdIGJ5m9WSKh617LHRdvJo4PiF0VkncFLwPVxKvBEQfSPd1qx/xLV/eSusewEu0C8eUFrsLsWlBgIcOg==",
+ "dev": true
+ },
"function-bind": {
"version": "1.1.1",
"dev": true
@@ -4533,6 +4891,12 @@
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
+ "immer": {
+ "version": "9.0.21",
+ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
+ "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
+ "dev": true
+ },
"impetus": {
"version": "0.8.8",
"dev": true
@@ -4805,6 +5169,18 @@
"version": "4.17.21",
"dev": true
},
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "dev": true
+ },
+ "lodash.ismatch": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz",
+ "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==",
+ "dev": true
+ },
"long": {
"version": "4.0.0",
"dev": true
@@ -5093,6 +5469,28 @@
"warning": "^4.0.2"
}
},
+ "react-redux": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.0.tgz",
+ "integrity": "sha512-CtHZzAOxi7GQvTph4dVLWwZHAWUjV2kMEQtk50OrN8z3gKxpWg3Tz7JfDw32N3Rpd7fh02z73cF6yZkK467gbQ==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.12.1",
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "@types/use-sync-external-store": "^0.0.3",
+ "hoist-non-react-statics": "^3.3.2",
+ "react-is": "^18.0.0",
+ "use-sync-external-store": "^1.0.0"
+ },
+ "dependencies": {
+ "react-is": {
+ "version": "18.2.0",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
+ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
+ "dev": true
+ }
+ }
+ },
"react-resizable-panels": {
"version": "0.0.48",
"dev": true,
@@ -5198,6 +5596,33 @@
"@babel/runtime": "^7.9.2"
}
},
+ "redux-saga": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/redux-saga/-/redux-saga-1.2.3.tgz",
+ "integrity": "sha512-HDe0wTR5nhd8Xr5xjGzoyTbdAw6rjy1GDplFt3JKtKN8/MnkQSRqK/n6aQQhpw5NI4ekDVOaW+w4sdxPBaCoTQ==",
+ "dev": true,
+ "requires": {
+ "@redux-saga/core": "^1.2.3"
+ }
+ },
+ "redux-saga-test-plan": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/redux-saga-test-plan/-/redux-saga-test-plan-4.0.6.tgz",
+ "integrity": "sha512-ESdbFoDWCeJ/EiFdUNSCGtA2CC9tnuvHDm6k06gVFa98EIeR2hpzFkGk9kJ1/hpMUnYFp+OOEEITIrZeDYBfFg==",
+ "dev": true,
+ "requires": {
+ "fsm-iterator": "^1.1.0",
+ "lodash.isequal": "^4.5.0",
+ "lodash.ismatch": "^4.4.0"
+ }
+ },
+ "redux-thunk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz",
+ "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==",
+ "dev": true,
+ "requires": {}
+ },
"regenerator-runtime": {
"version": "0.13.11",
"dev": true
@@ -5219,6 +5644,12 @@
"version": "1.0.0",
"dev": true
},
+ "reselect": {
+ "version": "4.1.8",
+ "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz",
+ "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==",
+ "dev": true
+ },
"right-now": {
"version": "1.0.0",
"dev": true
@@ -5440,6 +5871,30 @@
"version": "5.0.3",
"dev": true
},
+ "typescript-compare": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
+ "integrity": "sha512-8ja4j7pMHkfLJQO2/8tut7ub+J3Lw2S3061eJLFQcvs3tsmJKp8KG5NtpLn7KcY2w08edF74BSVN7qJS0U6oHA==",
+ "dev": true,
+ "requires": {
+ "typescript-logic": "^0.0.0"
+ }
+ },
+ "typescript-logic": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/typescript-logic/-/typescript-logic-0.0.0.tgz",
+ "integrity": "sha512-zXFars5LUkI3zP492ls0VskH3TtdeHCqu0i7/duGt60i5IGPIpAHE/DWo5FqJ6EjQ15YKXrt+AETjv60Dat34Q==",
+ "dev": true
+ },
+ "typescript-tuple": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/typescript-tuple/-/typescript-tuple-2.2.1.tgz",
+ "integrity": "sha512-Zcr0lbt8z5ZdEzERHAMAniTiIKerFCMgd7yjq1fPnDJ43et/k9twIFQMUYff9k5oXcsQ0WpvFcgzK2ZKASoW6Q==",
+ "dev": true,
+ "requires": {
+ "typescript-compare": "^0.0.2"
+ }
+ },
"universalify": {
"version": "0.2.0",
"dev": true
@@ -5459,6 +5914,13 @@
"requires-port": "^1.0.0"
}
},
+ "use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "dev": true,
+ "requires": {}
+ },
"victory-vendor": {
"version": "36.6.8",
"dev": true,
diff --git a/packages/@dcl/inspector/package.json b/packages/@dcl/inspector/package.json
index ab101e62d..9e65b49b6 100644
--- a/packages/@dcl/inspector/package.json
+++ b/packages/@dcl/inspector/package.json
@@ -11,11 +11,13 @@
"@dcl/ecs-math": "2.0.2",
"@dcl/rpc": "^1.1.1",
"@dcl/schemas": "^6.11.1",
+ "@reduxjs/toolkit": "^1.9.5",
"@testing-library/react": "^14.0.0",
"@types/node": "^18.11.18",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/react-modal": "^3.16.0",
+ "@types/redux-saga": "^0.10.5",
"@vscode/webview-ui-toolkit": "^1.2.2",
"@well-known-components/pushable-channel": "^1.0.3",
"classnames": "^2.3.2",
@@ -31,7 +33,10 @@
"react-dom": "^18.2.0",
"react-icons": "^4.7.1",
"react-modal": "^3.16.1",
+ "react-redux": "^8.1.0",
"react-resizable-panels": "^0.0.48",
+ "redux-saga": "^1.2.3",
+ "redux-saga-test-plan": "^4.0.6",
"typescript": "^5.0.2"
},
"files": [
diff --git a/packages/@dcl/inspector/src/components/App/App.tsx b/packages/@dcl/inspector/src/components/App/App.tsx
index 308f75522..9d0260f8b 100644
--- a/packages/@dcl/inspector/src/components/App/App.tsx
+++ b/packages/@dcl/inspector/src/components/App/App.tsx
@@ -11,6 +11,8 @@ import './App.css'
import Assets from '../Assets'
import { useSelectedEntity } from '../../hooks/sdk/useSelectedEntity'
import { useWindowSize } from '../../hooks/useWindowSize'
+import { useAppSelector } from '../../redux/hooks'
+import { getError } from '../../redux/data-layer'
const App = () => {
const selectedEntity = useSelectedEntity()
@@ -19,9 +21,9 @@ const App = () => {
// Footer's height is 48 pixels, so we need to calculate the percentage of the screen that it takes to pass as the minSize prop for the Panel
const footerMin = (48 / height!) * 100
-
+ const disconnected = useAppSelector(getError)
return (
-
+
diff --git a/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx b/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx
index e0a3d4fe1..2b37e73a2 100644
--- a/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx
+++ b/packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx
@@ -4,7 +4,6 @@ import { RxCross2 } from 'react-icons/rx'
import { IoIosImage } from 'react-icons/io'
import FileInput from '../FileInput'
-import { withSdk } from '../../hoc/withSdk'
import { Container } from '../Container'
import { TextField } from '../EntityInspector/TextField'
import { Block } from '../Block'
@@ -16,6 +15,8 @@ import { GLTFValidation } from '@babylonjs/loaders'
import './ImportAsset.css'
import classNames from 'classnames'
import { withAssetDir } from '../../lib/data-layer/host/fs-utils'
+import { getDataLayer } from '../../redux/data-layer'
+import { useAppSelector } from '../../redux/hooks'
const ONE_MB_IN_BYTES = 1_048_576
const ONE_GB_IN_BYTES = ONE_MB_IN_BYTES * 1024
@@ -29,9 +30,9 @@ type ValidationError = string | null
async function validateGltf(data: ArrayBuffer): Promise {
let result
try {
- result = await GLTFValidation.ValidateAsync(
- data, '', '', (uri) => { throw new Error('external references are not supported yet')}
- )
+ result = await GLTFValidation.ValidateAsync(data, '', '', (uri) => {
+ throw new Error('external references are not supported yet')
+ })
} catch (error) {
return `Invalid GLTF: ${error}`
}
@@ -42,13 +43,13 @@ async function validateGltf(data: ArrayBuffer): Promise {
Babylon's type declarations incorrectly state that result.issues.messages
is an Array. In fact, it's an array of objects with useful properties.
*/
- type BabylonValidationIssue = {severity: number, code: string}
+ type BabylonValidationIssue = { severity: number; code: string }
const severity = (issue as unknown as BabylonValidationIssue).severity
/*
Severity codes are Error (0), Warning (1), Information (2), Hint (3).
https://github.com/KhronosGroup/glTF-Validator/blob/main/lib/src/errors.dart
*/
- if (severity == 0) {
+ if (severity === 0) {
const message = (issue as unknown as BabylonValidationIssue).code
return `Invalid GLTF: ${message}`
}
@@ -59,8 +60,10 @@ async function validateGltf(data: ArrayBuffer): Promise {
}
}
-const ImportAsset = withSdk(({ sdk, onSave }) => {
+const ImportAsset: React.FC = ({ onSave }) => {
// TODO: multiple files
+ const dataLayer = useAppSelector(getDataLayer)
+
const [file, setFile] = useState()
const [validationError, setValidationError] = useState(null)
const [assetName, setAssetName] = useState('')
@@ -92,7 +95,7 @@ const ImportAsset = withSdk(({ sdk, onSave }) => {
}
const gltfValidationError = await validateGltf(binary)
- if (gltfValidationError != null) {
+ if (gltfValidationError !== null) {
setValidationError(gltfValidationError)
return
}
@@ -100,9 +103,9 @@ const ImportAsset = withSdk(({ sdk, onSave }) => {
const content: Map = new Map()
content.set(assetName + '.' + assetExtension, new Uint8Array(binary))
- const basePath = withAssetDir((await sdk!.dataLayer.getProjectData({})).path)
+ const basePath = withAssetDir((await dataLayer!.getProjectData({})).path)
- await sdk!.dataLayer.importAsset({
+ await dataLayer?.importAsset({
content,
basePath,
assetPackageName: ''
@@ -124,10 +127,8 @@ const ImportAsset = withSdk(({ sdk, onSave }) => {
const invalidName = !!assets.find((asset) => {
const [packageName, otherAssetName] = removeBasePath(basePath, asset.path).split('/')
- if (packageName === 'builder')
- return false
- else
- return otherAssetName?.toLocaleLowerCase() === (assetName?.toLocaleLowerCase() + '.' + assetExtension)
+ if (packageName === 'builder') return false
+ else return otherAssetName?.toLocaleLowerCase() === assetName?.toLocaleLowerCase() + '.' + assetExtension
})
return (
@@ -155,22 +156,18 @@ const ImportAsset = withSdk(({ sdk, onSave }) => {
-
+
- {validationError}
+ {validationError}
)}
)
-})
+}
export default ImportAsset
diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx
index 7adbd93ec..a2e9d6cb5 100644
--- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx
+++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectAssetExplorer.tsx
@@ -1,3 +1,5 @@
+import React from 'react'
+
import { useAssetTree } from '../../hooks/catalog/useAssetTree'
import { useFileSystem } from '../../hooks/catalog/useFileSystem'
import ProjectView from './ProjectView'
@@ -6,10 +8,12 @@ import { AssetNodeFolder } from './types'
import './ProjectAssetExplorer.css'
-export function ProjectAssetExplorer() {
+function ProjectAssetExplorer() {
const [files] = useFileSystem()
const { tree } = useAssetTree(files)
const folders = tree.children.filter((item) => item.type === 'folder') as AssetNodeFolder[]
return
}
+
+export default React.memo(ProjectAssetExplorer)
diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx
index 4d85e066c..00ab258e9 100644
--- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx
+++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/ProjectView.tsx
@@ -13,6 +13,8 @@ import { AssetNode, AssetNodeFolder } from './types'
import { getFullNodePath } from './utils'
import Search from '../Search'
import { withAssetDir } from '../../lib/data-layer/host/fs-utils'
+import { getDataLayer } from '../../redux/data-layer'
+import { useAppSelector } from '../../redux/hooks'
function noop() {}
@@ -36,6 +38,8 @@ const FilesTree = Tree()
function ProjectView({ folders }: Props) {
const sdk = useSdk()
+ const dataLayer = useAppSelector(getDataLayer)
+
const [open, setOpen] = useState(new Set())
const [modal, setModal] = useState(undefined)
const [lastSelected, setLastSelected] = useState()
@@ -146,12 +150,11 @@ function ProjectView({ folders }: Props) {
const removeAsset = useCallback(
async (path: string, _: Entity[] = []) => {
- if (!sdk) return
- const { dataLayer } = sdk
+ if (!dataLayer) return
await dataLayer.removeAsset({ path })
fileSystemEvent.emit('change')
},
- [sdk]
+ [dataLayer]
)
const handleConfirm = useCallback(async () => {
@@ -251,7 +254,7 @@ function ProjectView({ folders }: Props) {
- }
- else
- return (
- <>
-
-
- >
- )
+ return (
+
+
+
+ )
+ } else
+ return (
+ <>
+
+
+ >
+ )
}
export default React.memo(ProjectView)
diff --git a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts
index 9386d5627..9912a840b 100644
--- a/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts
+++ b/packages/@dcl/inspector/src/components/ProjectAssetExplorer/index.ts
@@ -1,2 +1,2 @@
-import { ProjectAssetExplorer } from './ProjectAssetExplorer'
+import ProjectAssetExplorer from './ProjectAssetExplorer'
export { ProjectAssetExplorer }
diff --git a/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx b/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx
index 302b233b5..d8c9bccbc 100644
--- a/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx
+++ b/packages/@dcl/inspector/src/components/Renderer/Renderer.tsx
@@ -4,6 +4,9 @@ import { Vector3 } from '@babylonjs/core'
import { Loader } from 'decentraland-ui/dist/components/Loader/Loader'
import { Dimmer } from 'decentraland-ui/dist/components/Dimmer/Dimmer'
+import { withAssetDir } from '../../lib/data-layer/host/fs-utils'
+import { useAppSelector } from '../../redux/hooks'
+import { getDataLayer } from '../../redux/data-layer'
import { BuilderAsset, DROP_TYPES, IDrop, ProjectAssetDrop, isDropType } from '../../lib/sdk/drag-drop'
import { useRenderer } from '../../hooks/sdk/useRenderer'
import { useSdk } from '../../hooks/sdk/useSdk'
@@ -16,11 +19,10 @@ import { AssetNodeItem } from '../ProjectAssetExplorer/types'
import { IAsset } from '../AssetsCatalog/types'
import { getModel, isAsset } from '../EntityInspector/GltfInspector/utils'
import { useIsMounted } from '../../hooks/useIsMounted'
-import { Warnings } from './Warnings'
+import { Warnings } from '../Warnings'
import { CameraSpeed } from './CameraSpeed'
import './Renderer.css'
-import { withAssetDir } from '../../lib/data-layer/host/fs-utils'
const fixedNumber = (val: number) => Math.round(val * 1e2) / 1e2
@@ -28,6 +30,8 @@ const Renderer: React.FC = () => {
const canvasRef = React.useRef(null)
useRenderer(() => canvasRef)
const sdk = useSdk()
+ const dataLayer = useAppSelector(getDataLayer)
+
const [isLoading, setIsLoading] = useState(false)
const isMounted = useIsMounted()
const [files, init] = useFileSystem()
@@ -84,7 +88,7 @@ const Renderer: React.FC = () => {
})
)
- await sdk!.dataLayer.importAsset({
+ await dataLayer?.importAsset({
content: new Map(Object.entries(fileContent)),
basePath: withAssetDir(destFolder),
assetPackageName
diff --git a/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx b/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx
index f7f4ba5e4..eb6b716c9 100644
--- a/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx
+++ b/packages/@dcl/inspector/src/components/Toolbar/Toolbar.tsx
@@ -8,10 +8,13 @@ import { withSdk } from '../../hoc/withSdk'
import { Gizmos } from './Gizmos'
import { Preferences } from './Preferences'
import { ToolbarButton } from './ToolbarButton'
-
import './Toolbar.css'
+import { getDataLayer } from '../../redux/data-layer'
+import { useAppSelector } from '../../redux/hooks'
+import { DataLayerRpcClient } from '../../lib/data-layer/types'
const Toolbar = withSdk(({ sdk }) => {
+ const dataLayer = useAppSelector(getDataLayer)
const [save, isDirty] = useSave()
const handleInspector = useCallback(() => {
const { debugLayer } = sdk.scene
@@ -23,14 +26,15 @@ const Toolbar = withSdk(({ sdk }) => {
}, [])
const handleUndoRedo = useCallback(
- (fn: typeof sdk.dataLayer.undo) => async () => {
+ (fn?: DataLayerRpcClient['undo']) => async () => {
+ if (!fn) return
const { type } = await fn({})
if (type === 'file') {
fileSystemEvent.emit('change')
}
saveEvent.emit('change', true)
},
- []
+ [dataLayer]
)
return (
@@ -38,10 +42,10 @@ const Toolbar = withSdk(({ sdk }) => {
{isDirty ? : }
-
+
-
+
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx b/packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx
similarity index 66%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx
rename to packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx
index 66ce35009..089de443e 100644
--- a/packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx
+++ b/packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/RotationGizmoLocalAlignmentDisabled.tsx
@@ -1,11 +1,11 @@
import React from 'react'
import { Warning } from '../Warning'
-import { useGizmoAlignment } from '../../../../hooks/editor/useGizmoAlignment'
-import { useSelectedEntity } from '../../../../hooks/sdk/useSelectedEntity'
-import { ROOT } from '../../../../lib/sdk/tree'
-import { withSdk } from '../../../../hoc/withSdk'
-import { useComponentValue } from '../../../../hooks/sdk/useComponentValue'
-import { GizmoType } from '../../../../lib/utils/gizmo'
+import { useGizmoAlignment } from '../../../hooks/editor/useGizmoAlignment'
+import { useSelectedEntity } from '../../../hooks/sdk/useSelectedEntity'
+import { ROOT } from '../../../lib/sdk/tree'
+import { withSdk } from '../../../hoc/withSdk'
+import { useComponentValue } from '../../../hooks/sdk/useComponentValue'
+import { GizmoType } from '../../../lib/utils/gizmo'
const RotationGizmoLocalAlignmentDisabled: React.FC = withSdk(({ sdk }) => {
const selectedEntity = useSelectedEntity()
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts b/packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts
similarity index 100%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts
rename to packages/@dcl/inspector/src/components/Warnings/RotationGizmoLocalAlignmentDisabled/index.ts
diff --git a/packages/@dcl/inspector/src/components/Warnings/SocketConnection/SocketConnection.tsx b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/SocketConnection.tsx
new file mode 100644
index 000000000..6f4bd900d
--- /dev/null
+++ b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/SocketConnection.tsx
@@ -0,0 +1,18 @@
+import React from 'react'
+
+import { Warning } from '../Warning'
+import { useAppSelector } from '../../../redux/hooks'
+import { ErrorType, getError } from '../../../redux/data-layer'
+
+const mapError = {
+ [ErrorType.Disconnected]: 'Socket disconnected. Please refresh the page',
+ [ErrorType.Reconnecting]: 'Disconnected. Trying to reconnect...'
+}
+
+const SocketConnection: React.FC = () => {
+ const error = useAppSelector(getError)
+ if (!error) return null
+ return
+}
+
+export default React.memo(SocketConnection)
diff --git a/packages/@dcl/inspector/src/components/Warnings/SocketConnection/index.ts b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/index.ts
new file mode 100644
index 000000000..6877a77c1
--- /dev/null
+++ b/packages/@dcl/inspector/src/components/Warnings/SocketConnection/index.ts
@@ -0,0 +1,2 @@
+import SocketConnection from './SocketConnection'
+export { SocketConnection }
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.css b/packages/@dcl/inspector/src/components/Warnings/Warning/Warning.css
similarity index 100%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.css
rename to packages/@dcl/inspector/src/components/Warnings/Warning/Warning.css
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.tsx b/packages/@dcl/inspector/src/components/Warnings/Warning/Warning.tsx
similarity index 100%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/Warning.tsx
rename to packages/@dcl/inspector/src/components/Warnings/Warning/Warning.tsx
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/index.ts b/packages/@dcl/inspector/src/components/Warnings/Warning/index.ts
similarity index 100%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warning/index.ts
rename to packages/@dcl/inspector/src/components/Warnings/Warning/index.ts
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.css b/packages/@dcl/inspector/src/components/Warnings/Warnings.css
similarity index 100%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.css
rename to packages/@dcl/inspector/src/components/Warnings/Warnings.css
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.tsx b/packages/@dcl/inspector/src/components/Warnings/Warnings.tsx
similarity index 79%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.tsx
rename to packages/@dcl/inspector/src/components/Warnings/Warnings.tsx
index 86b57a481..0f6d157e0 100644
--- a/packages/@dcl/inspector/src/components/Renderer/Warnings/Warnings.tsx
+++ b/packages/@dcl/inspector/src/components/Warnings/Warnings.tsx
@@ -1,11 +1,15 @@
import React from 'react'
+
import { RotationGizmoLocalAlignmentDisabled } from './RotationGizmoLocalAlignmentDisabled'
+import { SocketConnection } from './SocketConnection'
+
import './Warnings.css'
const Warnings: React.FC = () => {
return (
+
)
}
diff --git a/packages/@dcl/inspector/src/components/Renderer/Warnings/index.ts b/packages/@dcl/inspector/src/components/Warnings/index.ts
similarity index 100%
rename from packages/@dcl/inspector/src/components/Renderer/Warnings/index.ts
rename to packages/@dcl/inspector/src/components/Warnings/index.ts
diff --git a/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts b/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts
index 914ba9624..0d78addfd 100644
--- a/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts
+++ b/packages/@dcl/inspector/src/hooks/catalog/useFileSystem.ts
@@ -1,8 +1,9 @@
import mitt from 'mitt'
-import { useState } from 'react'
+import { useEffect, useState } from 'react'
-import { useSdk } from '../sdk/useSdk'
import { AssetCatalogResponse } from '../../tooling-entrypoint'
+import { useAppSelector } from '../../redux/hooks'
+import { getDataLayer } from '../../redux/data-layer'
type FileSystemEvent = { change: unknown }
export const fileSystemEvent = mitt()
@@ -14,15 +15,23 @@ export const removeBasePath = (basePath: string, path: string) => {
/* istanbul ignore next */
export const useFileSystem = (): [AssetCatalogResponse, boolean] => {
const [files, setFiles] = useState({ basePath: '', assets: [] })
+ const dataLayer = useAppSelector(getDataLayer)
+
const [init, setInit] = useState(false)
- useSdk(({ dataLayer }) => {
- async function fetchFiles() {
- const assets = await dataLayer.getAssetCatalog({})
- setFiles(assets)
- }
+
+ const fetchFiles = async () => {
+ if (!dataLayer) return
+ const assets = await dataLayer.getAssetCatalog({})
+ setFiles(assets)
+ }
+
+ useEffect(() => {
fileSystemEvent.on('change', fetchFiles)
+ }, [])
+
+ useEffect(() => {
void fetchFiles().then(() => setInit(true))
- })
+ }, [dataLayer])
return [files, init]
}
diff --git a/packages/@dcl/inspector/src/hooks/editor/useSave.ts b/packages/@dcl/inspector/src/hooks/editor/useSave.ts
index 0b72b0e0b..93660dd67 100644
--- a/packages/@dcl/inspector/src/hooks/editor/useSave.ts
+++ b/packages/@dcl/inspector/src/hooks/editor/useSave.ts
@@ -1,19 +1,22 @@
import mitt from 'mitt'
import { useCallback, useState } from 'react'
+import { useAppSelector } from '../../redux/hooks'
+import { getDataLayer } from '../../redux/data-layer'
import { useSdk } from '../sdk/useSdk'
type SaveEvent = { change: boolean }
export const saveEvent = mitt()
export const useSave = (): [() => Promise, boolean] => {
+ const dataLayer = useAppSelector(getDataLayer)
const sdk = useSdk()
const [isDirty, setIsDirty] = useState(false)
const saveFn = useCallback(async () => {
- await sdk?.dataLayer.save({})
+ await dataLayer?.save({})
setIsDirty(false)
- }, [sdk])
+ }, [dataLayer])
saveEvent.on('change', (value: boolean) => {
if (value && !sdk?.preferences.data.autosaveEnabled) setIsDirty(true)
diff --git a/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts b/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts
index f332d7208..0c47c24f7 100644
--- a/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts
+++ b/packages/@dcl/inspector/src/hooks/sdk/useSdkContext.ts
@@ -1,22 +1,33 @@
import { useCallback, useEffect, useState } from 'react'
import { createSdkContext, SdkContextValue } from '../../lib/sdk/context'
import { useCatalog } from '../catalog/useCatalog'
+import { useAppDispatch, useAppSelector } from '../../redux/hooks'
+import { getDataLayer, connect as connectDataLayer } from '../../redux/data-layer'
+import { addEngines } from '../../redux/sdk'
/**
*
* @returns This hook is only meant to be used by the SdkProvider, use useSdk instead
*/
export const useSdkContext = () => {
+ const dataLayer = useAppSelector(getDataLayer)
const [canvas, setCanvas] = useState(null)
const [sdk, setSdk] = useState(null)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState(null)
const [catalog] = useCatalog()
+ const dispatch = useAppDispatch()
+
+ useEffect(() => {
+ dispatch(connectDataLayer())
+ }, [dispatch])
+
useEffect(() => {
- if (!catalog || !canvas || sdk || isLoading) return
+ if (!catalog || !canvas || sdk || isLoading || !dataLayer) return
setIsLoading(true)
- createSdkContext(canvas, catalog)
+ createSdkContext(dataLayer, canvas, catalog)
.then((ctx) => {
+ dispatch(addEngines({ inspector: ctx.engine, babylon: ctx.sceneContext.engine }))
setSdk(ctx)
})
.catch((e) => {
@@ -24,7 +35,7 @@ export const useSdkContext = () => {
setError(e)
})
.finally(() => setIsLoading(false))
- }, [catalog, canvas, sdk, isLoading])
+ }, [catalog, canvas, sdk, isLoading, dispatch, dataLayer])
const renderer = useCallback(
(ref: React.RefObject) => {
diff --git a/packages/@dcl/inspector/src/index.tsx b/packages/@dcl/inspector/src/index.tsx
index ec318cc44..df5343476 100644
--- a/packages/@dcl/inspector/src/index.tsx
+++ b/packages/@dcl/inspector/src/index.tsx
@@ -1,7 +1,9 @@
+import { Provider } from 'react-redux'
import { DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'
import ReactDOM from 'react-dom/client'
+import { store } from './redux/store'
import { App } from './components/App'
import { SdkProvider } from './components/SdkProvider'
@@ -17,8 +19,10 @@ globalThis.Buffer = MaybeBuffer
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
-
-
-
+
+
+
+
+
)
diff --git a/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts b/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts
index 0d1f815c5..bba17cf42 100644
--- a/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts
+++ b/packages/@dcl/inspector/src/lib/babylon/decentraland/SceneContext.ts
@@ -16,10 +16,10 @@ import { putBillboardComponent } from './sdkComponents/billboard'
import { putGltfContainerComponent } from './sdkComponents/gltf-container'
import { putMeshRendererComponent } from './sdkComponents/mesh-renderer'
import { putTransformComponent } from './sdkComponents/transform'
-import { consumeAllMessagesInto } from '../../logic/consume-stream'
import { putSceneComponent } from './editorComponents/scene'
import { createOperations } from '../../sdk/operations'
import { createGizmoManager } from './gizmo-manager'
+import { store } from '../../../redux/store'
export type LoadableScene = {
readonly entity: Readonly>
@@ -68,12 +68,7 @@ export class SceneContext {
// this future is resolved when the scene is disposed
readonly stopped = future()
- constructor(
- public babylon: BABYLON.Engine,
- public scene: BABYLON.Scene,
- public loadableScene: LoadableScene,
- public dataLayer: DataLayerRpcClient
- ) {
+ constructor(public babylon: BABYLON.Engine, public scene: BABYLON.Scene, public loadableScene: LoadableScene) {
this.rootNode = new EcsEntity(0 as Entity, this.#weakThis, scene)
Object.assign(globalThis, { babylon: this.engine })
}
@@ -152,7 +147,9 @@ export class SceneContext {
async getFile(src: string): Promise {
if (!src) return null
try {
- const response = await this.dataLayer.getAssetData({ path: src })
+ const { dataLayer } = store.getState().dataLayer
+ if (!dataLayer) return null
+ const response = await dataLayer.getAssetData({ path: src })
return response.data
} catch (err) {
console.error('Error fetching file ' + src, err)
@@ -176,26 +173,6 @@ export class SceneContext {
this.rootNode.parent = null
this.rootNode.dispose()
}
-
- async connectCrdtTransport(crdtStream: DataLayerRpcClient['crdtStream']) {
- const outgoingMessages = new AsyncQueue((_, _action) => {
- // console.log('SCENE QUEUE', action)
- })
- const transport = this.addTransport(outgoingMessages)
- const engine = this.engine
-
- async function onMessage(message: Uint8Array) {
- if (message.byteLength) {
- Array.from(serializeCrdtMessages('DataLayer>Babylon', message, engine)).forEach(($) => console.log($))
- }
- transport.onmessage!(message)
- await engine.update(1)
- }
-
- consumeAllMessagesInto(crdtStream(outgoingMessages), onMessage, outgoingMessages.close).catch((e) => {
- console.error('consumeAllMessagesInto failed: ', e)
- })
- }
}
// an entity only exists if it has any component attached to it
diff --git a/packages/@dcl/inspector/src/lib/data-layer/client/index.ts b/packages/@dcl/inspector/src/lib/data-layer/client/index.ts
deleted file mode 100644
index 83cff66e3..000000000
--- a/packages/@dcl/inspector/src/lib/data-layer/client/index.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { DataLayerRpcClient } from '../types'
-import { feededFileSystem } from './feeded-local-fs'
-import { createLocalDataLayerRpcClient } from './local-data-layer'
-import { createWebSocketDataLayerRpcClient } from './ws-data-layer'
-
-const dataLayerWsByQueryParams = new URLSearchParams(window.location.search).get('ws')
-const dataLayerWsByGlobalThis = ((globalThis as any).InspectorConfig?.dataLayerRpcWsUrl as string) || null
-
-const dataLayerWs: string | null = dataLayerWsByQueryParams || dataLayerWsByGlobalThis || null
-
-export async function createDataLayerClientRpc(): Promise {
- if (!dataLayerWs) {
- const fs = await feededFileSystem()
- return createLocalDataLayerRpcClient(fs)
- } else {
- return createWebSocketDataLayerRpcClient(dataLayerWs)
- }
-}
diff --git a/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts b/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts
index 7c367d829..f1a9e5fa6 100644
--- a/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts
+++ b/packages/@dcl/inspector/src/lib/data-layer/client/local-data-layer.ts
@@ -1,12 +1,14 @@
import { createDataLayerHost } from '../host'
-import { DataLayerRpcClient, FileSystemInterface } from '../types'
+import { DataLayerRpcClient } from '../types'
+import { feededFileSystem } from './feeded-local-fs'
/**
* This RpcClient creates internally the DataLayer HOST, implementing its own file system interface and engine.
* @param fs
* @returns
*/
-export async function createLocalDataLayerRpcClient(fs: FileSystemInterface): Promise {
+export async function createLocalDataLayerRpcClient(): Promise {
+ const fs = await feededFileSystem()
const localDataLayerHost = await createDataLayerHost(fs)
return localDataLayerHost.rpcMethods as DataLayerRpcClient
}
diff --git a/packages/@dcl/inspector/src/lib/data-layer/client/ws-data-layer.ts b/packages/@dcl/inspector/src/lib/data-layer/client/ws-data-layer.ts
deleted file mode 100644
index 830316db1..000000000
--- a/packages/@dcl/inspector/src/lib/data-layer/client/ws-data-layer.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { createRpcClient } from '@dcl/rpc'
-import { WebSocketTransport } from '@dcl/rpc/dist/transports/WebSocket'
-import { DataLayerRpcClient } from '../types'
-import * as codegen from '@dcl/rpc/dist/codegen'
-import { DataServiceDefinition } from '../proto/gen/data-layer.gen'
-import { IEngine } from '@dcl/ecs'
-
-export function createWebSocketDataLayerRpcClient(dataLayerWebSocketUrl: string): Promise {
- // TODO: should be some persistent logic to reconnect in case of failure or disconnection? YES
- return new Promise((resolve, _reject) => {
- const ws = new WebSocket(dataLayerWebSocketUrl)
- ws.onopen = async () => {
- const clientTransport = WebSocketTransport(ws)
- const client = await createRpcClient(clientTransport)
- const clientPort = await client.createPort('scene-ctx')
- const serviceClient: DataLayerRpcClient = codegen.loadService<{ engine: IEngine }, DataServiceDefinition>(
- clientPort,
- DataServiceDefinition
- )
- resolve(serviceClient)
- }
- })
-}
diff --git a/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts b/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts
index 579491ae9..3590b71c5 100644
--- a/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts
+++ b/packages/@dcl/inspector/src/lib/data-layer/host/stream.ts
@@ -40,8 +40,9 @@ export function stream(
}
// and lastly wire the new messages from the renderer engine
- consumeAllMessagesInto(stream, processMessage, closeCallback).catch((err) => {
- console.error('consumeAllMessagesInto failed: ', err)
+ consumeAllMessagesInto(stream, processMessage).catch((err) => {
+ console.error('x failed: ', err)
+ closeCallback()
})
// Send initial message (engineSerialized)
diff --git a/packages/@dcl/inspector/src/lib/logic/consume-stream.ts b/packages/@dcl/inspector/src/lib/logic/consume-stream.ts
index 656798353..909734275 100644
--- a/packages/@dcl/inspector/src/lib/logic/consume-stream.ts
+++ b/packages/@dcl/inspector/src/lib/logic/consume-stream.ts
@@ -1,20 +1,9 @@
import { CrdtStreamMessage } from '../data-layer/proto/gen/data-layer.gen'
-export async function consumeAllMessagesInto(
- iter: AsyncIterable,
- cb: (data: Uint8Array) => void,
- onClose: () => void
-) {
- try {
- for await (const it of iter) {
- if (it.data.byteLength) {
- cb(it.data)
- }
+export async function consumeAllMessagesInto(iter: AsyncIterable, cb: (data: Uint8Array) => void) {
+ for await (const it of iter) {
+ if (it.data.byteLength) {
+ cb(it.data)
}
- } catch (err) {
- debugger
- } finally {
- onClose()
}
- debugger
}
diff --git a/packages/@dcl/inspector/src/lib/sdk/connect-stream.ts b/packages/@dcl/inspector/src/lib/sdk/connect-stream.ts
new file mode 100644
index 000000000..51b045777
--- /dev/null
+++ b/packages/@dcl/inspector/src/lib/sdk/connect-stream.ts
@@ -0,0 +1,44 @@
+import { IEngine, Transport } from '@dcl/ecs'
+import { AsyncQueue } from '@well-known-components/pushable-channel'
+
+import { CrdtStreamMessage } from '../data-layer/remote-data-layer'
+import { DataLayerRpcClient } from '../data-layer/types'
+import { consumeAllMessagesInto } from '../logic/consume-stream'
+import { serializeCrdtMessages } from './crdt-logger'
+
+export function connectCrdtToEngine(
+ engine: IEngine,
+ dataLayerStream: DataLayerRpcClient['crdtStream'],
+ engineKey: string
+) {
+ //
+ const outgoingMessagesStream = new AsyncQueue((_, _action) => {})
+
+ const transport: Transport = {
+ filter() {
+ return !outgoingMessagesStream.closed
+ },
+ async send(message) {
+ if (outgoingMessagesStream.closed) return
+ outgoingMessagesStream.enqueue({ data: message })
+ if (message.byteLength) {
+ Array.from(serializeCrdtMessages(`${engineKey}>Datalayer`, message, engine)).forEach(($) => console.log($))
+ }
+ }
+ }
+ Object.assign(transport, { name: `${engineKey}TransportClient` })
+ engine.addTransport(transport)
+
+ function onMessage(message: Uint8Array) {
+ if (message.byteLength) {
+ Array.from(serializeCrdtMessages(`DataLayer>${engineKey}`, message, engine)).forEach(($) => console.log($))
+ }
+ transport.onmessage!(message)
+ void engine.update(1)
+ }
+
+ consumeAllMessagesInto(dataLayerStream(outgoingMessagesStream), onMessage).catch((e) => {
+ console.error(`${engineKey} consumeAllMessagesInto failed: `, e)
+ outgoingMessagesStream.close()
+ })
+}
diff --git a/packages/@dcl/inspector/src/lib/sdk/context.ts b/packages/@dcl/inspector/src/lib/sdk/context.ts
index ac7195ca3..e899f37e0 100644
--- a/packages/@dcl/inspector/src/lib/sdk/context.ts
+++ b/packages/@dcl/inspector/src/lib/sdk/context.ts
@@ -5,7 +5,6 @@ import { Emitter } from 'mitt'
import { ITheme } from '../../components/AssetsCatalog'
import { SceneContext } from '../babylon/decentraland/SceneContext'
import { initRenderer } from '../babylon/setup/init'
-import { createDataLayerClientRpc } from '../data-layer/client'
import { EditorComponents, SdkComponents } from './components'
import { getHardcodedLoadableScene } from './test-local-scene'
import { createInspectorEngine } from './inspector-engine'
@@ -27,17 +26,17 @@ export type SdkContextValue = {
sceneContext: SceneContext
events: Emitter
dispose(): void
- dataLayer: DataLayerRpcClient
operations: ReturnType
gizmos: Gizmos
editorCamera: CameraManager
preferences: InspectorPreferencesManager
}
-export async function createSdkContext(canvas: HTMLCanvasElement, catalog: ITheme[]): Promise {
- // initialize DataLayer
- const dataLayer = await createDataLayerClientRpc()
-
+export async function createSdkContext(
+ dataLayer: DataLayerRpcClient,
+ canvas: HTMLCanvasElement,
+ catalog: ITheme[]
+): Promise {
// fetch user preferences from the data layer
const preferences = await dataLayer.getInspectorPreferences({})
@@ -51,16 +50,12 @@ export async function createSdkContext(canvas: HTMLCanvasElement, catalog: IThem
getHardcodedLoadableScene(
'urn:decentraland:entity:bafkreid44xhavttoz4nznidmyj3rjnrgdza7v6l7kd46xdmleor5lmsxfm1',
catalog
- ),
- dataLayer
+ )
)
ctx.rootNode.position.set(0, 0, 0)
- // Connect babylon engine with dataLayer transport
- void ctx.connectCrdtTransport(dataLayer.crdtStream)
-
// create inspector engine context and components
- const { engine, components, events, dispose } = createInspectorEngine(dataLayer)
+ const { engine, components, events, dispose } = createInspectorEngine()
// register some globals for debugging
Object.assign(globalThis, { dataLayer, inspectorEngine: engine })
@@ -72,7 +67,6 @@ export async function createSdkContext(canvas: HTMLCanvasElement, catalog: IThem
scene,
sceneContext: ctx,
dispose,
- dataLayer,
operations: createOperations(engine),
gizmos: ctx.gizmos,
editorCamera: renderer.editorCamera,
diff --git a/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts b/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts
index 679df1253..4f5f6ae03 100644
--- a/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts
+++ b/packages/@dcl/inspector/src/lib/sdk/inspector-engine.ts
@@ -1,17 +1,9 @@
-import { Transport } from '@dcl/ecs'
-import { AsyncQueue } from '@well-known-components/pushable-channel'
import mitt from 'mitt'
-import { CrdtStreamMessage } from '../data-layer/proto/gen/data-layer.gen'
-import { DataLayerRpcClient } from '../data-layer/types'
-import { consumeAllMessagesInto } from '../logic/consume-stream'
-import { serializeCrdtMessages } from './crdt-logger'
import { SdkContextEvents, SdkContextValue } from './context'
import { createEngineContext } from '../data-layer/host/utils/engine'
-export function createInspectorEngine(
- dataLayer: DataLayerRpcClient
-): Omit<
+export function createInspectorEngine(): Omit<
SdkContextValue,
'scene' | 'sceneContext' | 'dataLayer' | 'operations' | 'gizmos' | 'editorCamera' | 'preferences'
> {
@@ -21,40 +13,8 @@ export function createInspectorEngine(
events.emit('change', { entity, operation, component, value })
})
- //
- const outgoingMessagesStream = new AsyncQueue((_, _action) => {})
- const transport: Transport = {
- filter() {
- return !outgoingMessagesStream.closed
- },
- async send(message) {
- if (outgoingMessagesStream.closed) return
- outgoingMessagesStream.enqueue({ data: message })
- if (message.byteLength) {
- Array.from(serializeCrdtMessages('Inspector>Datalayer', message, engine)).forEach(($) => console.log($))
- }
- }
- }
- Object.assign(transport, { name: 'InspectorTransportClient' })
- engine.addTransport(transport)
-
- function onMessage(message: Uint8Array) {
- if (message.byteLength) {
- Array.from(serializeCrdtMessages('DataLayer>Inspector', message, engine)).forEach(($) => console.log($))
- }
- transport.onmessage!(message)
- void engine.update(1)
- }
-
- consumeAllMessagesInto(dataLayer.crdtStream(outgoingMessagesStream), onMessage, outgoingMessagesStream.close).catch(
- (e) => {
- console.error('consumeAllMessagesInto failed: ', e)
- }
- )
- //
-
function dispose() {
- outgoingMessagesStream.close()
+ // outgoingMessagesStream.close()
events.emit('dispose')
}
return {
diff --git a/packages/@dcl/inspector/src/redux/data-layer/index.ts b/packages/@dcl/inspector/src/redux/data-layer/index.ts
new file mode 100644
index 000000000..e1b2cb71f
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/data-layer/index.ts
@@ -0,0 +1,53 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+import { RootState } from '../../redux/store'
+import { DataLayerRpcClient } from '../../lib/data-layer/types'
+
+export enum ErrorType {
+ Disconnected = 'disconnected',
+ Reconnecting = 'reconnecting'
+}
+
+export interface DataLayerState {
+ dataLayer: DataLayerRpcClient | undefined
+ reconnectAttempts: number
+ error: ErrorType | undefined
+}
+
+export const initialState: DataLayerState = {
+ dataLayer: undefined,
+ reconnectAttempts: 0,
+ error: undefined
+}
+
+export const dataLayer = createSlice({
+ name: 'data-layer',
+ initialState,
+ reducers: {
+ connect: (state) => {
+ state.reconnectAttempts++
+ console.log('[WS] Connecting')
+ },
+ reconnect: (state) => {
+ console.log('[WS] Reconnecting')
+ state.error = ErrorType.Reconnecting
+ state.dataLayer = undefined
+ },
+ connected: (state, { payload }: PayloadAction<{ dataLayer: DataLayerState['dataLayer'] }>) => {
+ console.log('[WS] Connected')
+ state.dataLayer = payload.dataLayer
+ state.reconnectAttempts = 0
+ state.error = undefined
+ },
+ error: (state, { payload }: PayloadAction<{ error: ErrorType }>) => {
+ console.log('[WS] Error', payload.error)
+ state.error = payload.error
+ }
+ }
+})
+
+export const { connect, connected, reconnect, error } = dataLayer.actions
+export const getError = (state: RootState) => state.dataLayer.error
+export const getDataLayer = (state: RootState) => state.dataLayer.dataLayer
+export const getDataLayerReconnectAttempts = (state: RootState) => state.dataLayer.reconnectAttempts
+
+export default dataLayer.reducer
diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.spec.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.spec.ts
new file mode 100644
index 000000000..df7324593
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.spec.ts
@@ -0,0 +1,119 @@
+/* eslint-disable @typescript-eslint/ban-types */
+import { expectSaga, testSaga } from 'redux-saga-test-plan'
+import * as codegen from '@dcl/rpc/dist/codegen'
+
+import { connectSaga, createSocketChannel, createWebSocketConnection, getWsUrl } from './connect'
+import { WebSocketTransport } from '@dcl/rpc/dist/transports/WebSocket'
+import { RpcClient, RpcClientPort, Transport, createRpcClient } from '@dcl/rpc'
+import { DataLayerRpcClient } from '../../../lib/data-layer/types'
+import reducer, { connected, reconnect } from '..'
+import { createLocalDataLayerRpcClient } from '../../../lib/data-layer/client/local-data-layer'
+import { call } from 'redux-saga/effects'
+
+describe('WebSocket Connection Saga', () => {
+ it('Should create LOCAL data-layer if no ws url is provided', async () => {
+ const dataLayer = { boedo: 'casla' } as any as DataLayerRpcClient
+
+ return expectSaga(connectSaga)
+ .withReducer(reducer)
+ .provide([
+ [call(getWsUrl), undefined],
+ [call(createLocalDataLayerRpcClient), dataLayer]
+ ])
+ .put(connected({ dataLayer }))
+ .hasFinalState({
+ dataLayer,
+ error: undefined,
+ reconnectAttempts: 0
+ })
+ .run()
+ })
+
+ it('Should create remote data-layer with Ws', async () => {
+ const url = 'ws://boedo.com'
+ const ws = new MockWebSocket()
+ const channel = createSocketChannel(ws as any as WebSocket)
+ const clientTransport = {} as Transport
+ const client: RpcClient = { createPort: (_port: string) => {} } as RpcClient
+ const clientPort: RpcClientPort = {} as RpcClientPort
+ const dataLayer = { boedo: 'casla' } as any as DataLayerRpcClient
+ jest.spyOn(codegen, 'loadService').mockReturnValue(dataLayer as any)
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
+
+ testSaga(connectSaga)
+ .next()
+ .call(getWsUrl)
+ .next(url)
+ .call(createWebSocketConnection, url)
+ .next(ws)
+ .call(createSocketChannel, ws)
+ .next(channel)
+ .take(channel as any)
+
+ // OPEN event. Connect data layer
+ .next({ type: 'WS_OPENED' })
+ .call(WebSocketTransport, ws)
+ .next(clientTransport)
+ .call(createRpcClient, clientTransport)
+ .next(client)
+ .call(client.createPort, 'scene-ctx')
+ .next(clientPort)
+ .put(connected({ dataLayer }))
+ .next()
+
+ // Error event. console.error (TODO: handle this)
+ .next({ type: 'WS_ERROR', error: 'some - error' })
+
+ // Break the connection. Should reconnect
+ .finish()
+ .put(reconnect())
+ .next()
+ .isDone()
+
+ // Error logic
+ expect(consoleSpy).toBeCalledWith('some - error')
+ })
+})
+
+// Mock WebSocket
+
+class MockWebSocket {
+ listeners: { [key: string]: Function[] } = {}
+
+ addEventListener(event: string, callback: Function) {
+ if (!this.listeners[event]) {
+ this.listeners[event] = []
+ }
+ this.listeners[event].push(callback)
+ }
+
+ removeEventListener(event: string, callback: Function) {
+ if (this.listeners[event]) {
+ this.listeners[event] = this.listeners[event].filter((cb) => cb !== callback)
+ }
+ }
+
+ simulateOpen() {
+ if (this.listeners['open']) {
+ this.listeners['open'].forEach((callback) => callback())
+ }
+ }
+
+ simulateMessage(data: any) {
+ if (this.listeners['message']) {
+ this.listeners['message'].forEach((callback) => callback({ data }))
+ }
+ }
+
+ simulateClose() {
+ if (this.listeners['close']) {
+ this.listeners['close'].forEach((callback) => callback())
+ }
+ }
+
+ simulateError() {
+ if (this.listeners['error']) {
+ this.listeners['error'].forEach((callback) => callback())
+ }
+ }
+}
diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.ts
new file mode 100644
index 000000000..d0c4cb719
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/connect.ts
@@ -0,0 +1,80 @@
+import { IEngine } from '@dcl/ecs'
+import { END, EventChannel, eventChannel } from 'redux-saga'
+import * as codegen from '@dcl/rpc/dist/codegen'
+import { call, put, take } from 'redux-saga/effects'
+import { createRpcClient, RpcClient, RpcClientPort, Transport } from '@dcl/rpc'
+import { WebSocketTransport } from '@dcl/rpc/dist/transports/WebSocket'
+
+import { connected, DataLayerState, reconnect } from '../'
+import { createLocalDataLayerRpcClient } from '../../../lib/data-layer/client/local-data-layer'
+import { DataServiceDefinition } from '../../..//lib/data-layer/proto/gen/data-layer.gen'
+import { DataLayerRpcClient } from '../../../lib/data-layer/types'
+
+export function getWsUrl() {
+ const dataLayerWsByQueryParams = new URLSearchParams(window.location.search).get('ws')
+ const dataLayerWsByGlobalThis = ((globalThis as any).InspectorConfig?.dataLayerRpcWsUrl as string) || null
+
+ return dataLayerWsByQueryParams || dataLayerWsByGlobalThis || null
+}
+
+export function createWebSocketConnection(url: string): WebSocket {
+ return new WebSocket(url)
+}
+
+export function createSocketChannel(socket: WebSocket): EventChannel {
+ return eventChannel((emit) => {
+ socket.addEventListener('close', () => {
+ emit(END)
+ })
+ socket.addEventListener('error', (error) => {
+ emit({ type: 'WS_ERROR', error })
+ })
+ socket.addEventListener('open', () => {
+ emit({ type: 'WS_OPENED' })
+ })
+ return () => {}
+ })
+}
+
+export type WsActions =
+ | {
+ type: 'WS_OPENED'
+ }
+ | {
+ type: 'WS_ERROR'
+ error: unknown
+ }
+
+export function* connectSaga() {
+ const wsUrl: string | undefined = yield call(getWsUrl)
+
+ if (!wsUrl) {
+ const dataLayer: DataLayerState['dataLayer'] = yield call(createLocalDataLayerRpcClient)
+ yield put(connected({ dataLayer }))
+ return
+ }
+ const ws: WebSocket = yield call(createWebSocketConnection, wsUrl)
+ const socketChannel: EventChannel = yield call(createSocketChannel, ws)
+ try {
+ while (true) {
+ const wsEvent: WsActions = yield take(socketChannel)
+
+ if (wsEvent.type === 'WS_OPENED') {
+ const clientTransport: Transport = yield call(WebSocketTransport, ws)
+ const client: RpcClient = yield call(createRpcClient, clientTransport)
+ const clientPort: RpcClientPort = yield call(client.createPort, 'scene-ctx')
+ const dataLayer: DataLayerRpcClient = codegen.loadService<{ engine: IEngine }, DataServiceDefinition>(
+ clientPort,
+ DataServiceDefinition
+ )
+ yield put(connected({ dataLayer }))
+ } else if (wsEvent.type === 'WS_ERROR') {
+ console.error(wsEvent.error)
+ }
+ }
+ } catch (error) {
+ console.log('[WS] Error', error)
+ } finally {
+ yield put(reconnect())
+ }
+}
diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/index.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/index.ts
new file mode 100644
index 000000000..96672d204
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/index.ts
@@ -0,0 +1,11 @@
+import { takeEvery } from 'redux-saga/effects'
+import { connect, reconnect } from '..'
+import { connectSaga } from './connect'
+import { reconnectSaga } from './reconnect'
+
+export function* dataLayerSaga() {
+ yield takeEvery(connect.type, connectSaga)
+ yield takeEvery(reconnect.type, reconnectSaga)
+}
+
+export default dataLayerSaga
diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.spec.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.spec.ts
new file mode 100644
index 000000000..852990059
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.spec.ts
@@ -0,0 +1,31 @@
+/* eslint-disable @typescript-eslint/ban-types */
+import { expectSaga } from 'redux-saga-test-plan'
+import reducer, { ErrorType, connect, error, initialState } from '..'
+
+import { MAX_RETRY_TIMES, reconnectSaga } from './reconnect'
+import { combineReducers } from '@reduxjs/toolkit'
+
+describe('WebSocket Reconnection Saga', () => {
+ it('Should try to reconnect', async () => {
+ const provideDelay = ({ fn }, next) => (fn.name === 'delayP' ? null : next())
+
+ return expectSaga(reconnectSaga)
+ .withReducer(combineReducers({ dataLayer: reducer }))
+ .withState({ dataLayer: initialState })
+ .provide([{ call: provideDelay }])
+ .put(connect())
+ .hasFinalState({ dataLayer: { ...initialState, reconnectAttempts: 1 } })
+ .run()
+ })
+
+ it('Should set error if max attemps reached', async () => {
+ return expectSaga(reconnectSaga)
+ .withReducer(combineReducers({ dataLayer: reducer }))
+ .withState({ dataLayer: { ...initialState, reconnectAttempts: MAX_RETRY_TIMES } })
+ .put(error({ error: ErrorType.Disconnected }))
+ .hasFinalState({
+ dataLayer: { ...initialState, reconnectAttempts: MAX_RETRY_TIMES, error: ErrorType.Disconnected }
+ })
+ .run()
+ })
+})
diff --git a/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.ts b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.ts
new file mode 100644
index 000000000..4e2968b52
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/data-layer/sagas/reconnect.ts
@@ -0,0 +1,18 @@
+import { put, select, delay } from 'redux-saga/effects'
+
+import { ErrorType, connect, error, getDataLayerReconnectAttempts } from '../'
+
+const RECONNECT_TIMEOUT = 1000
+export const MAX_RETRY_TIMES = 6
+
+export function* reconnectSaga() {
+ const reconnectAttempts: number = yield select(getDataLayerReconnectAttempts)
+ console.log(`[WS] Reconnecting for ${reconnectAttempts} time`)
+
+ if (reconnectAttempts >= MAX_RETRY_TIMES) {
+ yield put(error({ error: ErrorType.Disconnected }))
+ return
+ }
+ yield delay(RECONNECT_TIMEOUT * reconnectAttempts)
+ yield put(connect())
+}
diff --git a/packages/@dcl/inspector/src/redux/hooks.ts b/packages/@dcl/inspector/src/redux/hooks.ts
new file mode 100644
index 000000000..4eabbe863
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/hooks.ts
@@ -0,0 +1,5 @@
+import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
+import type { RootState, AppDispatch } from './store'
+
+export const useAppDispatch: () => AppDispatch = useDispatch
+export const useAppSelector: TypedUseSelectorHook = useSelector
diff --git a/packages/@dcl/inspector/src/redux/root-saga.ts b/packages/@dcl/inspector/src/redux/root-saga.ts
new file mode 100644
index 000000000..fe400e71c
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/root-saga.ts
@@ -0,0 +1,7 @@
+import { all } from 'redux-saga/effects'
+import { dataLayerSaga } from './data-layer/sagas'
+import { sdkSagas } from './sdk/sagas'
+
+export default function* rootSaga() {
+ yield all([...dataLayerSaga(), ...sdkSagas()])
+}
diff --git a/packages/@dcl/inspector/src/redux/sdk/index.ts b/packages/@dcl/inspector/src/redux/sdk/index.ts
new file mode 100644
index 000000000..a1bcaffc4
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/sdk/index.ts
@@ -0,0 +1,32 @@
+import { IEngine } from '@dcl/ecs'
+import { PayloadAction, createSlice } from '@reduxjs/toolkit'
+import { RootState } from '../store'
+
+export interface EngineState {
+ inspectorEngine: IEngine | undefined
+ rendererEngine: IEngine | undefined
+}
+
+export const initialState: EngineState = {
+ inspectorEngine: undefined,
+ rendererEngine: undefined
+}
+
+export const sdkEngines = createSlice({
+ name: 'sdk-engines',
+ initialState,
+ reducers: {
+ addEngines: (state, { payload }: PayloadAction<{ inspector: IEngine; babylon: IEngine }>) => {
+ state.inspectorEngine = payload.inspector
+ state.rendererEngine = payload.babylon
+ }
+ }
+})
+
+export const { addEngines } = sdkEngines.actions
+export const getEngines = (state: RootState) => ({
+ inspector: state.sdk.inspectorEngine,
+ renderer: state.sdk.rendererEngine
+})
+
+export default sdkEngines.reducer
diff --git a/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.spec.ts b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.spec.ts
new file mode 100644
index 000000000..6db4218d4
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.spec.ts
@@ -0,0 +1,43 @@
+/* eslint-disable @typescript-eslint/ban-types */
+import { expectSaga } from 'redux-saga-test-plan'
+import { combineReducers } from '@reduxjs/toolkit'
+import { Engine } from '@dcl/ecs'
+
+import dataLayerReducer, { initialState as dataLayerState, getDataLayer } from '../../data-layer'
+import sdkReducer, { getEngines, initialState as sdkState } from '../'
+import { connectStream } from './connect-stream'
+import * as connectStreamEngine from '../../../lib/sdk/connect-stream'
+
+describe('SDK Engines crdt stream', () => {
+ it('Should not connect crdt stream if there is no ws', async () => {
+ const spy = jest.spyOn(connectStreamEngine, 'connectCrdtToEngine')
+ await expectSaga(connectStream)
+ .withReducer(combineReducers({ dataLayer: dataLayerReducer, sdk: sdkReducer }))
+ .withState({ dataLayer: dataLayerState, sdk: sdkState })
+ .select(getEngines)
+ .select(getDataLayer)
+ .run()
+ expect(spy).not.toBeCalled()
+ })
+
+ it('Should not connect crdt stream if there is no ws', async () => {
+ const spy = jest.spyOn(connectStreamEngine, 'connectCrdtToEngine').mockImplementation(() => {})
+ const state = {
+ dataLayer: {
+ ...dataLayerState,
+ dataLayer: jest.fn()
+ },
+ sdk: {
+ inspectorEngine: Engine(),
+ rendererEngine: Engine()
+ }
+ }
+ await expectSaga(connectStream)
+ .withReducer(combineReducers({ dataLayer: dataLayerReducer, sdk: sdkReducer }))
+ .withState(state)
+ .select(getEngines)
+ .select(getDataLayer)
+ .run()
+ expect(spy).toBeCalledTimes(2)
+ })
+})
diff --git a/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.ts b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.ts
new file mode 100644
index 000000000..974527f4d
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/sdk/sagas/connect-stream.ts
@@ -0,0 +1,15 @@
+import { call, select } from 'redux-saga/effects'
+
+import { getEngines } from '..'
+import { connectCrdtToEngine } from '../../../lib/sdk/connect-stream'
+import { getDataLayer } from '../../data-layer'
+
+export function* connectStream() {
+ const engines: ReturnType = yield select(getEngines)
+ const dataLayer: ReturnType = yield select(getDataLayer)
+ if (!dataLayer || !engines.inspector || !engines.renderer) return
+
+ console.log('Data layer reconnected')
+ yield call(connectCrdtToEngine, engines.inspector, dataLayer.crdtStream, 'Inspector')
+ yield call(connectCrdtToEngine, engines.renderer, dataLayer.crdtStream, 'Renderer')
+}
diff --git a/packages/@dcl/inspector/src/redux/sdk/sagas/index.ts b/packages/@dcl/inspector/src/redux/sdk/sagas/index.ts
new file mode 100644
index 000000000..6903737dc
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/sdk/sagas/index.ts
@@ -0,0 +1,11 @@
+import { takeEvery } from 'redux-saga/effects'
+import { addEngines } from '../'
+import { connected } from '../../data-layer'
+import { connectStream } from './connect-stream'
+
+export function* sdkSagas() {
+ yield takeEvery(connected.type, connectStream)
+ yield takeEvery(addEngines.type, connectStream)
+}
+
+export default sdkSagas
diff --git a/packages/@dcl/inspector/src/redux/store.ts b/packages/@dcl/inspector/src/redux/store.ts
new file mode 100644
index 000000000..bb4077fce
--- /dev/null
+++ b/packages/@dcl/inspector/src/redux/store.ts
@@ -0,0 +1,23 @@
+import { configureStore } from '@reduxjs/toolkit'
+import createSagaMiddleware from 'redux-saga'
+
+import dataLayerReducer from './data-layer'
+import sdkReducer from './sdk'
+import sagas from './root-saga'
+
+const sagaMiddleware = createSagaMiddleware()
+
+export const store = configureStore({
+ reducer: {
+ dataLayer: dataLayerReducer,
+ sdk: sdkReducer
+ },
+ middleware: (getDefaultMiddleware) => {
+ return getDefaultMiddleware({ thunk: false, serializableCheck: false }).concat(sagaMiddleware)
+ }
+})
+
+sagaMiddleware.run(sagas)
+
+export type AppDispatch = typeof store.dispatch
+export type RootState = ReturnType
diff --git a/packages/@dcl/inspector/test/data-layer/utils.ts b/packages/@dcl/inspector/test/data-layer/utils.ts
index 3732f6dfd..83c370dad 100644
--- a/packages/@dcl/inspector/test/data-layer/utils.ts
+++ b/packages/@dcl/inspector/test/data-layer/utils.ts
@@ -1,13 +1,14 @@
import * as BABYLON from '@babylonjs/core'
import { IEngine } from '@dcl/ecs'
+
import { SceneContext, LoadableScene } from '../../src/lib/babylon/decentraland/SceneContext'
import { SdkContextValue } from '../../src/lib/sdk/context'
import { createInspectorEngine } from '../../src/lib/sdk/inspector-engine'
import { createLocalDataLayerRpcClient } from '../../src/lib/data-layer/client/local-data-layer'
-import { feededFileSystem } from '../../src/lib/data-layer/client/feeded-local-fs'
import { DataLayerRpcClient } from '../../src/lib/data-layer/types'
import { createOperations } from '../../src/lib/sdk/operations'
import { getDefaultInspectorPreferences } from '../../src/lib/logic/preferences/types'
+import { connectCrdtToEngine } from '../../src/lib/sdk/connect-stream'
export function initTestEngine(loadableScene: Readonly) {
let sceneCtx: SceneContext
@@ -18,8 +19,7 @@ export function initTestEngine(loadableScene: Readonly) {
>
beforeAll(async () => {
- const fs = await feededFileSystem({})
- dataLayer = await createLocalDataLayerRpcClient(fs)
+ dataLayer = await createLocalDataLayerRpcClient()
// Enable autosave to improve test coverage
await dataLayer.setInspectorPreferences({
...getDefaultInspectorPreferences(),
@@ -36,9 +36,9 @@ export function initTestEngine(loadableScene: Readonly) {
const scene = new BABYLON.Scene(engine)
sceneCtx = new SceneContext(engine, scene, loadableScene, dataLayer)
-
inspector = createInspectorEngine(dataLayer)
- void sceneCtx.connectCrdtTransport(dataLayer.crdtStream)
+ connectCrdtToEngine(sceneCtx.engine, dataLayer.crdtStream, 'renderer')
+ connectCrdtToEngine(inspector.engine, dataLayer.crdtStream, 'inspector')
})
afterAll(() => {
diff --git a/packages/@dcl/sdk-commands/package-lock.json b/packages/@dcl/sdk-commands/package-lock.json
index 016761fe8..59b80a515 100644
--- a/packages/@dcl/sdk-commands/package-lock.json
+++ b/packages/@dcl/sdk-commands/package-lock.json
@@ -63,11 +63,13 @@
"@dcl/ecs-math": "2.0.2",
"@dcl/rpc": "^1.1.1",
"@dcl/schemas": "^6.11.1",
+ "@reduxjs/toolkit": "^1.9.5",
"@testing-library/react": "^14.0.0",
"@types/node": "^18.11.18",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/react-modal": "^3.16.0",
+ "@types/redux-saga": "^0.10.5",
"@vscode/webview-ui-toolkit": "^1.2.2",
"@well-known-components/pushable-channel": "^1.0.3",
"classnames": "^2.3.2",
@@ -83,7 +85,10 @@
"react-dom": "^18.2.0",
"react-icons": "^4.7.1",
"react-modal": "^3.16.1",
+ "react-redux": "^8.1.0",
"react-resizable-panels": "^0.0.48",
+ "redux-saga": "^1.2.3",
+ "redux-saga-test-plan": "^4.0.6",
"typescript": "^5.0.2"
}
},
@@ -2425,11 +2430,13 @@
"@dcl/ecs-math": "2.0.2",
"@dcl/rpc": "^1.1.1",
"@dcl/schemas": "^6.11.1",
+ "@reduxjs/toolkit": "^1.9.5",
"@testing-library/react": "^14.0.0",
"@types/node": "^18.11.18",
"@types/react": "^18.0.27",
"@types/react-dom": "^18.0.10",
"@types/react-modal": "^3.16.0",
+ "@types/redux-saga": "^0.10.5",
"@vscode/webview-ui-toolkit": "^1.2.2",
"@well-known-components/pushable-channel": "^1.0.3",
"classnames": "^2.3.2",
@@ -2445,7 +2452,10 @@
"react-dom": "^18.2.0",
"react-icons": "^4.7.1",
"react-modal": "^3.16.1",
+ "react-redux": "^8.1.0",
"react-resizable-panels": "^0.0.48",
+ "redux-saga": "^1.2.3",
+ "redux-saga-test-plan": "^4.0.6",
"typescript": "^5.0.2"
}
},
diff --git a/test/snapshots/development-bundles/static-scene.test.ts.crdt b/test/snapshots/development-bundles/static-scene.test.ts.crdt
index a93f9fa3c..bf7e08cc8 100644
--- a/test/snapshots/development-bundles/static-scene.test.ts.crdt
+++ b/test/snapshots/development-bundles/static-scene.test.ts.crdt
@@ -10,9 +10,9 @@ EVAL test/snapshots/development-bundles/static-scene.test.js
REQUIRE: ~system/Testing
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 31k
- MALLOC_COUNT = 9983
- ALIVE_OBJS_DELTA ~= 1.85k
+ OPCODES ~= 35k
+ MALLOC_COUNT = 10117
+ ALIVE_OBJS_DELTA ~= 1.89k
CALL onStart()
main.crdt: PUT_COMPONENT e=0x200 c=1 t=0 data={"position":{"x":5.880000114440918,"y":2.7916901111602783,"z":7.380000114440918},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0}
main.crdt: PUT_COMPONENT e=0x202 c=1 t=0 data={"position":{"x":4,"y":0.800000011920929,"z":8},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0}
@@ -56,4 +56,4 @@ CALL onUpdate(0.1)
OPCODES ~= 2k
MALLOC_COUNT = -3
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 984.26k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 987.25k bytes
\ No newline at end of file
diff --git a/test/snapshots/development-bundles/testing-fw.test.ts.crdt b/test/snapshots/development-bundles/testing-fw.test.ts.crdt
index 002b1e84d..127396c3f 100644
--- a/test/snapshots/development-bundles/testing-fw.test.ts.crdt
+++ b/test/snapshots/development-bundles/testing-fw.test.ts.crdt
@@ -1,4 +1,4 @@
-SCENE_COMPILED_JS_SIZE_PROD=375.4k bytes
+SCENE_COMPILED_JS_SIZE_PROD=375.5k bytes
THE BUNDLE HAS SOURCEMAPS
(start empty vm 0.21.0-3680274614.commit-1808aa1)
OPCODES ~= 0k
@@ -10,9 +10,9 @@ EVAL test/snapshots/development-bundles/testing-fw.test.js
REQUIRE: ~system/Testing
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 32k
- MALLOC_COUNT = 10096
- ALIVE_OBJS_DELTA ~= 1.89k
+ OPCODES ~= 36k
+ MALLOC_COUNT = 10230
+ ALIVE_OBJS_DELTA ~= 1.93k
CALL onStart()
LOG: ["Adding one to position.y=0"]
Renderer: PUT_COMPONENT e=0x0 c=1 t=1 data={"position":{"x":1,"y":0,"z":0},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":9,"y":9,"z":9},"parent":0}
@@ -64,4 +64,4 @@ CALL onUpdate(0.1)
OPCODES ~= 4k
MALLOC_COUNT = -40
ALIVE_OBJS_DELTA ~= -0.01k
- MEMORY_USAGE_COUNT ~= 984.29k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 987.27k bytes
\ No newline at end of file
diff --git a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt
index 736e113ef..b804f21f7 100644
--- a/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt
+++ b/test/snapshots/development-bundles/two-way-crdt.test.ts.crdt
@@ -1,4 +1,4 @@
-SCENE_COMPILED_JS_SIZE_PROD=375.4k bytes
+SCENE_COMPILED_JS_SIZE_PROD=375.5k bytes
THE BUNDLE HAS SOURCEMAPS
(start empty vm 0.21.0-3680274614.commit-1808aa1)
OPCODES ~= 0k
@@ -10,9 +10,9 @@ EVAL test/snapshots/development-bundles/two-way-crdt.test.js
REQUIRE: ~system/Testing
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 32k
- MALLOC_COUNT = 10096
- ALIVE_OBJS_DELTA ~= 1.89k
+ OPCODES ~= 36k
+ MALLOC_COUNT = 10230
+ ALIVE_OBJS_DELTA ~= 1.93k
CALL onStart()
LOG: ["Adding one to position.y=0"]
Renderer: PUT_COMPONENT e=0x0 c=1 t=1 data={"position":{"x":1,"y":0,"z":0},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":9,"y":9,"z":9},"parent":0}
@@ -64,4 +64,4 @@ CALL onUpdate(0.1)
OPCODES ~= 4k
MALLOC_COUNT = -40
ALIVE_OBJS_DELTA ~= -0.01k
- MEMORY_USAGE_COUNT ~= 984.29k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 987.28k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/append-value-crdt.ts.crdt b/test/snapshots/production-bundles/append-value-crdt.ts.crdt
index e8f667769..4c77d844d 100644
--- a/test/snapshots/production-bundles/append-value-crdt.ts.crdt
+++ b/test/snapshots/production-bundles/append-value-crdt.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/append-value-crdt.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 33k
- MALLOC_COUNT = 8902
- ALIVE_OBJS_DELTA ~= 1.90k
+ OPCODES ~= 37k
+ MALLOC_COUNT = 9036
+ ALIVE_OBJS_DELTA ~= 1.94k
CALL onStart()
Renderer: APPEND_VALUE e=0x200 c=1063 t=0 data={"button":0,"hit":{"position":{"x":1,"y":2,"z":3},"globalOrigin":{"x":1,"y":2,"z":3},"direction":{"x":1,"y":2,"z":3},"normalHit":{"x":1,"y":2,"z":3},"length":10,"meshName":"mesh","entityId":512},"state":1,"timestamp":1,"analog":5,"tickNumber":0}
OPCODES ~= 8k
@@ -55,4 +55,4 @@ CALL onUpdate(0.1)
OPCODES ~= 13k
MALLOC_COUNT = 31
ALIVE_OBJS_DELTA ~= 0.01k
- MEMORY_USAGE_COUNT ~= 727.43k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 730.42k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/billboard.ts.crdt b/test/snapshots/production-bundles/billboard.ts.crdt
index 535cf3227..17b882b90 100644
--- a/test/snapshots/production-bundles/billboard.ts.crdt
+++ b/test/snapshots/production-bundles/billboard.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/billboard.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 43k
- MALLOC_COUNT = 11203
- ALIVE_OBJS_DELTA ~= 2.35k
+ OPCODES ~= 47k
+ MALLOC_COUNT = 11335
+ ALIVE_OBJS_DELTA ~= 2.39k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 4
@@ -77,4 +77,4 @@ CALL onUpdate(0.1)
OPCODES ~= 8k
MALLOC_COUNT = 0
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 878.46k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 881.34k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/cube-deleted.ts.crdt b/test/snapshots/production-bundles/cube-deleted.ts.crdt
index 90b882904..96268d864 100644
--- a/test/snapshots/production-bundles/cube-deleted.ts.crdt
+++ b/test/snapshots/production-bundles/cube-deleted.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/cube-deleted.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 32k
- MALLOC_COUNT = 8435
- ALIVE_OBJS_DELTA ~= 1.78k
+ OPCODES ~= 36k
+ MALLOC_COUNT = 8569
+ ALIVE_OBJS_DELTA ~= 1.81k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 6
@@ -42,4 +42,4 @@ CALL onUpdate(0.1)
OPCODES ~= 3k
MALLOC_COUNT = 1
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 700.84k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 703.82k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/cube.ts.crdt b/test/snapshots/production-bundles/cube.ts.crdt
index f5e1482c8..e390d2ee5 100644
--- a/test/snapshots/production-bundles/cube.ts.crdt
+++ b/test/snapshots/production-bundles/cube.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/cube.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 32k
- MALLOC_COUNT = 8274
- ALIVE_OBJS_DELTA ~= 1.74k
+ OPCODES ~= 36k
+ MALLOC_COUNT = 8408
+ ALIVE_OBJS_DELTA ~= 1.78k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 8
@@ -32,4 +32,4 @@ CALL onUpdate(0.1)
OPCODES ~= 1k
MALLOC_COUNT = 0
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 685.34k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 688.32k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/cubes.ts.crdt b/test/snapshots/production-bundles/cubes.ts.crdt
index dd22cd968..5ce7f7f5b 100644
--- a/test/snapshots/production-bundles/cubes.ts.crdt
+++ b/test/snapshots/production-bundles/cubes.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/cubes.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 82k
- MALLOC_COUNT = 14653
- ALIVE_OBJS_DELTA ~= 3.67k
+ OPCODES ~= 86k
+ MALLOC_COUNT = 14787
+ ALIVE_OBJS_DELTA ~= 3.70k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 6
@@ -1652,4 +1652,4 @@ CALL onUpdate(0.1)
OPCODES ~= 636k
MALLOC_COUNT = 0
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 1016.60k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 1019.54k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/pointer-events.ts.crdt b/test/snapshots/production-bundles/pointer-events.ts.crdt
index 292b53cad..c7a0536d0 100644
--- a/test/snapshots/production-bundles/pointer-events.ts.crdt
+++ b/test/snapshots/production-bundles/pointer-events.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/pointer-events.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 33k
- MALLOC_COUNT = 8776
- ALIVE_OBJS_DELTA ~= 1.87k
+ OPCODES ~= 37k
+ MALLOC_COUNT = 8910
+ ALIVE_OBJS_DELTA ~= 1.91k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 8
@@ -28,7 +28,7 @@ CALL onUpdate(0)
Scene: PUT_COMPONENT e=0x200 c=1019 t=1 data={"mesh":{"$case":"box","box":{}}}
Scene: PUT_COMPONENT e=0x201 c=1019 t=1 data={"mesh":{"$case":"box","box":{}}}
Scene: PUT_COMPONENT e=0x202 c=1019 t=1 data={"mesh":{"$case":"box","box":{}}}
- OPCODES ~= 20k
+ OPCODES ~= 21k
MALLOC_COUNT = 35
ALIVE_OBJS_DELTA ~= 0.01k
CALL onUpdate(0.1)
@@ -43,4 +43,4 @@ CALL onUpdate(0.1)
OPCODES ~= 1k
MALLOC_COUNT = 0
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 709.87k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 712.87k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/schema-components.ts.crdt b/test/snapshots/production-bundles/schema-components.ts.crdt
index 0021bb9d4..05a649762 100644
--- a/test/snapshots/production-bundles/schema-components.ts.crdt
+++ b/test/snapshots/production-bundles/schema-components.ts.crdt
@@ -1,4 +1,4 @@
-SCENE_COMPILED_JS_SIZE_PROD=162.5k bytes
+SCENE_COMPILED_JS_SIZE_PROD=162.6k bytes
(start empty vm 0.21.0-3680274614.commit-1808aa1)
OPCODES ~= 0k
MALLOC_COUNT = 1005
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/schema-components.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 36k
- MALLOC_COUNT = 8393
- ALIVE_OBJS_DELTA ~= 1.77k
+ OPCODES ~= 40k
+ MALLOC_COUNT = 8524
+ ALIVE_OBJS_DELTA ~= 1.81k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 8
@@ -31,4 +31,4 @@ CALL onUpdate(0.1)
OPCODES ~= 1k
MALLOC_COUNT = 0
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 687.70k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 690.54k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/ui.ts.crdt b/test/snapshots/production-bundles/ui.ts.crdt
index e4f315b45..6360589a9 100644
--- a/test/snapshots/production-bundles/ui.ts.crdt
+++ b/test/snapshots/production-bundles/ui.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/ui.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 42k
- MALLOC_COUNT = 16026
- ALIVE_OBJS_DELTA ~= 3.04k
+ OPCODES ~= 46k
+ MALLOC_COUNT = 16160
+ ALIVE_OBJS_DELTA ~= 3.07k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 6
@@ -65,4 +65,4 @@ CALL onUpdate(0.1)
OPCODES ~= 61k
MALLOC_COUNT = 0
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 1508.39k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 1511.33k bytes
\ No newline at end of file
diff --git a/test/snapshots/production-bundles/with-main-function.ts.crdt b/test/snapshots/production-bundles/with-main-function.ts.crdt
index 829a6c4c4..fa142c536 100644
--- a/test/snapshots/production-bundles/with-main-function.ts.crdt
+++ b/test/snapshots/production-bundles/with-main-function.ts.crdt
@@ -8,9 +8,9 @@ EVAL test/snapshots/production-bundles/with-main-function.js
REQUIRE: long
REQUIRE: ~system/EngineApi
REQUIRE: ~system/EngineApi
- OPCODES ~= 33k
- MALLOC_COUNT = 8527
- ALIVE_OBJS_DELTA ~= 1.80k
+ OPCODES ~= 37k
+ MALLOC_COUNT = 8661
+ ALIVE_OBJS_DELTA ~= 1.84k
CALL onStart()
OPCODES ~= 0k
MALLOC_COUNT = 6
@@ -18,7 +18,7 @@ CALL onStart()
CALL onUpdate(0)
Scene: PUT_COMPONENT e=0x200 c=1 t=1 data={"position":{"x":0,"y":0,"z":0},"rotation":{"x":0,"y":0,"z":0,"w":1},"scale":{"x":1,"y":1,"z":1},"parent":0}
Scene: PUT_COMPONENT e=0x200 c=1018 t=1 data={"mesh":{"$case":"box","box":{"uvs":[]}}}
- OPCODES ~= 3k
+ OPCODES ~= 4k
MALLOC_COUNT = 69
ALIVE_OBJS_DELTA ~= 0.03k
CALL onUpdate(0.1)
@@ -36,4 +36,4 @@ CALL onUpdate(0.1)
OPCODES ~= 2k
MALLOC_COUNT = 5
ALIVE_OBJS_DELTA ~= 0.00k
- MEMORY_USAGE_COUNT ~= 703.18k bytes
\ No newline at end of file
+ MEMORY_USAGE_COUNT ~= 706.16k bytes
\ No newline at end of file