Merge pull request 'Handle error cases when switching clan' (#4879) from feat/handle-clan-switch-errors into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4879
This commit is contained in:
245
pkgs/clan-app/ui/package-lock.json
generated
245
pkgs/clan-app/ui/package-lock.json
generated
@@ -15,9 +15,10 @@
|
||||
"@modular-forms/solid": "^0.25.1",
|
||||
"@solid-primitives/storage": "^4.3.2",
|
||||
"@solidjs/router": "^0.15.3",
|
||||
"@tanstack/eslint-plugin-query": "^5.51.12",
|
||||
"@tanstack/solid-query": "^5.76.0",
|
||||
"@tanstack/solid-query-devtools": "^5.83.0",
|
||||
"@tanstack/eslint-plugin-query": "^5.83.1",
|
||||
"@tanstack/solid-query": "^5.85.5",
|
||||
"@tanstack/solid-query-devtools": "^5.85.5",
|
||||
"@tanstack/solid-query-persist-client": "^5.85.5",
|
||||
"solid-js": "^1.9.7",
|
||||
"solid-toast": "^0.5.0",
|
||||
"three": "^0.176.0",
|
||||
@@ -2487,12 +2488,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query": {
|
||||
"version": "5.81.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.81.2.tgz",
|
||||
"integrity": "sha512-h4k6P6fm5VhKP5NkK+0TTVpGGyKQdx6tk7NYYG7J7PkSu7ClpLgBihw7yzK8N3n5zPaF3IMyErxfoNiXWH/3/A==",
|
||||
"version": "5.83.1",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/eslint-plugin-query/-/eslint-plugin-query-5.83.1.tgz",
|
||||
"integrity": "sha512-tdkpPFfzkTksN9BIlT/qjixSAtKrsW6PUVRwdKWaOcag7DrD1vpki3UzzdfMQGDRGeg1Ue1Dg+rcl5FJGembNg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/utils": "^8.18.1"
|
||||
"@typescript-eslint/utils": "^8.37.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2502,10 +2503,169 @@
|
||||
"eslint": "^8.57.0 || ^9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/project-service": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.40.0.tgz",
|
||||
"integrity": "sha512-/A89vz7Wf5DEXsGVvcGdYKbVM9F7DyFXj52lNYUDS1L9yJfqjW/fIp5PgMuEJL/KeqVTe2QSbXAGUZljDUpArw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.40.0",
|
||||
"@typescript-eslint/types": "^8.40.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.40.0.tgz",
|
||||
"integrity": "sha512-y9ObStCcdCiZKzwqsE8CcpyuVMwRouJbbSrNuThDpv16dFAj429IkM6LNb1dZ2m7hK5fHyzNcErZf7CEeKXR4w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/visitor-keys": "8.40.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/tsconfig-utils": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.40.0.tgz",
|
||||
"integrity": "sha512-jtMytmUaG9d/9kqSl/W3E3xaWESo4hFDxAIHGVW/WKKtQhesnRIJSAJO6XckluuJ6KDB5woD1EiqknriCtAmcw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/types": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.40.0.tgz",
|
||||
"integrity": "sha512-ETdbFlgbAmXHyFPwqUIYrfc12ArvpBhEVgGAxVYSwli26dn8Ko+lIo4Su9vI9ykTZdJn+vJprs/0eZU0YMAEQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.40.0.tgz",
|
||||
"integrity": "sha512-k1z9+GJReVVOkc1WfVKs1vBrR5MIKKbdAjDTPvIK3L8De6KbFfPFt6BKpdkdk7rZS2GtC/m6yI5MYX+UsuvVYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.40.0",
|
||||
"@typescript-eslint/tsconfig-utils": "8.40.0",
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/visitor-keys": "8.40.0",
|
||||
"debug": "^4.3.4",
|
||||
"fast-glob": "^3.3.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "^9.0.4",
|
||||
"semver": "^7.6.0",
|
||||
"ts-api-utils": "^2.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/utils": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.40.0.tgz",
|
||||
"integrity": "sha512-Cgzi2MXSZyAUOY+BFwGs17s7ad/7L+gKt6Y8rAVVWS+7o6wrjeFN4nVfTpbE25MNcxyJ+iYUXflbs2xR9h4UBg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
"@typescript-eslint/scope-manager": "8.40.0",
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"@typescript-eslint/typescript-estree": "8.40.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.57.0 || ^9.0.0",
|
||||
"typescript": ">=4.8.4 <6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "8.40.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.40.0.tgz",
|
||||
"integrity": "sha512-8CZ47QwalyRjsypfwnbI3hKy5gJDPmrkLjkgMxhi0+DZZ2QNx2naS6/hWoVYUHU7LU2zleF68V9miaVZvhFfTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.40.0",
|
||||
"eslint-visitor-keys": "^4.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/eslint-visitor-keys": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/eslint-plugin-query/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-core": {
|
||||
"version": "5.83.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.83.0.tgz",
|
||||
"integrity": "sha512-0M8dA+amXUkyz5cVUm/B+zSk3xkQAcuXuz5/Q/LveT4ots2rBpPTZOzd7yJa2Utsf8D2Upl5KyjhHRY+9lB/XA==",
|
||||
"version": "5.85.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.85.5.tgz",
|
||||
"integrity": "sha512-KO0WTob4JEApv69iYp1eGvfMSUkgw//IpMnq+//cORBzXf0smyRwPLrUvEe5qtAEGjwZTXrjxg+oJNP/C00t6w==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2513,22 +2673,35 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/query-devtools": {
|
||||
"version": "5.81.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.81.2.tgz",
|
||||
"integrity": "sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg==",
|
||||
"version": "5.84.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.84.0.tgz",
|
||||
"integrity": "sha512-fbF3n+z1rqhvd9EoGp5knHkv3p5B2Zml1yNRjh7sNXklngYI5RVIWUrUjZ1RIcEoscarUb0+bOvIs5x9dwzOXQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/solid-query": {
|
||||
"version": "5.83.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/solid-query/-/solid-query-5.83.0.tgz",
|
||||
"integrity": "sha512-RF8Tv9+6+Kmzj+EafbTzvzzPq+J5SzHtc1Tz3D2MZ/EvlZTH+GL5q4HNnWK3emg7CB6WzyGnTuERmmWJaZs8/w==",
|
||||
"node_modules/@tanstack/query-persist-client-core": {
|
||||
"version": "5.85.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/query-persist-client-core/-/query-persist-client-core-5.85.5.tgz",
|
||||
"integrity": "sha512-2JQiyiTVaaUu8pwPqOp6tjNa64ZN+0T9eZ3lfksV4le1VuG99fTcAYmZFIydvzwWlSM7GEF/1kpl5bwW2Y1qfQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.83.0"
|
||||
"@tanstack/query-core": "5.85.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/solid-query": {
|
||||
"version": "5.85.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/solid-query/-/solid-query-5.85.5.tgz",
|
||||
"integrity": "sha512-0o0Ibk9wqydm4JatbIjmvDu1+MofeZ1bU9BKwAbpt7HYjrLVCeddpW6zGmp41nN7t/mHJyR+ctW9oiNumCkEfg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-core": "5.85.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
@@ -2539,19 +2712,36 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/solid-query-devtools": {
|
||||
"version": "5.83.0",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/solid-query-devtools/-/solid-query-devtools-5.83.0.tgz",
|
||||
"integrity": "sha512-Z0wQlAWXz/U2bJ/paMRBTDhMoPnB9Te6GmA21sXnI+nDnAAPZRcPxFBiCgYJS3eFsvbkdRGJwoUSQrdIgy0shg==",
|
||||
"version": "5.85.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/solid-query-devtools/-/solid-query-devtools-5.85.5.tgz",
|
||||
"integrity": "sha512-9rC22wILlV9Lcsi4xKPmzRkNio1NOxNT36diIS+HjpOmhsEP/aI8XkNKQa/KPhhaSN2naYaTCJamh7eBAQ0Ymg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-devtools": "5.81.2"
|
||||
"@tanstack/query-devtools": "5.84.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/solid-query": "^5.83.0",
|
||||
"@tanstack/solid-query": "^5.85.5",
|
||||
"solid-js": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/solid-query-persist-client": {
|
||||
"version": "5.85.5",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/solid-query-persist-client/-/solid-query-persist-client-5.85.5.tgz",
|
||||
"integrity": "sha512-2aG7UnLZlfE3R4XKqYuIeXVKjJOghjsjq4EU2Ifp915FTBZcZo61sEw1zRqRlrDjEFYAs4kJUZwqViDSJYyX2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/query-persist-client-core": "5.85.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tanstack/solid-query": "^5.85.5",
|
||||
"solid-js": "^1.6.0"
|
||||
}
|
||||
},
|
||||
@@ -2894,6 +3084,7 @@
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.36.0.tgz",
|
||||
"integrity": "sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/tsconfig-utils": "^8.36.0",
|
||||
@@ -2915,6 +3106,7 @@
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.36.0.tgz",
|
||||
"integrity": "sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.36.0",
|
||||
@@ -2932,6 +3124,7 @@
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.36.0.tgz",
|
||||
"integrity": "sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2972,6 +3165,7 @@
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.36.0.tgz",
|
||||
"integrity": "sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
@@ -2985,6 +3179,7 @@
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.36.0.tgz",
|
||||
"integrity": "sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/project-service": "8.36.0",
|
||||
@@ -3013,6 +3208,7 @@
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
@@ -3025,6 +3221,7 @@
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.36.0.tgz",
|
||||
"integrity": "sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.7.0",
|
||||
@@ -3048,6 +3245,7 @@
|
||||
"version": "8.36.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.36.0.tgz",
|
||||
"integrity": "sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "8.36.0",
|
||||
@@ -3065,6 +3263,7 @@
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
|
||||
"integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
|
||||
@@ -72,9 +72,10 @@
|
||||
"@modular-forms/solid": "^0.25.1",
|
||||
"@solid-primitives/storage": "^4.3.2",
|
||||
"@solidjs/router": "^0.15.3",
|
||||
"@tanstack/eslint-plugin-query": "^5.51.12",
|
||||
"@tanstack/solid-query": "^5.76.0",
|
||||
"@tanstack/solid-query-devtools": "^5.83.0",
|
||||
"@tanstack/eslint-plugin-query": "^5.83.1",
|
||||
"@tanstack/solid-query": "^5.85.5",
|
||||
"@tanstack/solid-query-devtools": "^5.85.5",
|
||||
"@tanstack/solid-query-persist-client": "^5.85.5",
|
||||
"solid-js": "^1.9.7",
|
||||
"solid-toast": "^0.5.0",
|
||||
"three": "^0.176.0",
|
||||
|
||||
@@ -11,7 +11,7 @@ import { useClanListQuery } from "@/src/hooks/queries";
|
||||
import { Alert } from "@/src/components/Alert/Alert";
|
||||
|
||||
export interface ListClansModalProps {
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
error?: {
|
||||
title: string;
|
||||
description: string;
|
||||
@@ -68,7 +68,7 @@ export const ListClansModal = (props: ListClansModalProps) => {
|
||||
size="s"
|
||||
startIcon="Plus"
|
||||
onClick={() => {
|
||||
props.onClose();
|
||||
props.onClose?.();
|
||||
navigateToOnboarding(navigate, true);
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -30,7 +30,7 @@ export const useModalContext = () => {
|
||||
export interface ModalProps {
|
||||
id?: string;
|
||||
title: string;
|
||||
onClose: () => void;
|
||||
onClose?: () => void;
|
||||
children: JSX.Element;
|
||||
mount?: Node;
|
||||
class?: string;
|
||||
@@ -57,13 +57,11 @@ export const Modal = (props: ModalProps) => {
|
||||
>
|
||||
{props.title}
|
||||
</Typography>
|
||||
<KDialog.CloseButton
|
||||
onClick={() => {
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
<Icon icon="Close" size="0.75rem" />
|
||||
</KDialog.CloseButton>
|
||||
<Show when={props.onClose}>
|
||||
<KDialog.CloseButton onClick={props.onClose}>
|
||||
<Icon icon="Close" size="0.75rem" />
|
||||
</KDialog.CloseButton>
|
||||
</Show>
|
||||
</div>
|
||||
<Show when={props.metaHeader}>
|
||||
{(metaHeader) => (
|
||||
|
||||
@@ -31,7 +31,11 @@ export const SidebarHeader = () => {
|
||||
ctx?.activeClanQuery?.data?.name.charAt(0).toUpperCase();
|
||||
const clanName = () => ctx?.activeClanQuery?.data?.name;
|
||||
|
||||
const clans = () => ctx.otherClanQueries.filter((clan) => !clan.isError);
|
||||
const clanList = () =>
|
||||
ctx.allClansQueries
|
||||
.filter((it) => it.isSuccess)
|
||||
.map((it) => it.data!)
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
return (
|
||||
<div class="sidebar-header">
|
||||
@@ -100,13 +104,13 @@ export const SidebarHeader = () => {
|
||||
</Button>
|
||||
</DropdownMenu.GroupLabel>
|
||||
<div class="dropdown-group-items">
|
||||
<For each={clans()}>
|
||||
<For each={clanList()}>
|
||||
{(clan) => (
|
||||
<Suspense fallback={"Loading..."}>
|
||||
<DropdownMenu.Item
|
||||
class="dropdown-item"
|
||||
onSelect={() => {
|
||||
setActiveClanURI(clan.data!.uri);
|
||||
setActiveClanURI(clan.uri);
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
@@ -114,7 +118,7 @@ export const SidebarHeader = () => {
|
||||
size="xs"
|
||||
weight="medium"
|
||||
>
|
||||
{clan.data?.name}
|
||||
{clan.name}
|
||||
</Typography>
|
||||
</DropdownMenu.Item>
|
||||
</Suspense>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import { useQueries, useQuery, UseQueryResult } from "@tanstack/solid-query";
|
||||
import {
|
||||
QueryClient,
|
||||
useQueries,
|
||||
useQuery,
|
||||
UseQueryResult,
|
||||
} from "@tanstack/solid-query";
|
||||
import { SuccessData } from "../hooks/api";
|
||||
import { encodeBase64 } from "@/src/hooks/clan";
|
||||
import { useApiClient } from "./ApiClient";
|
||||
import { experimental_createQueryPersister } from "@tanstack/solid-query-persist-client";
|
||||
import { ClanDetailsStore } from "@/src/stores/clanDetails";
|
||||
|
||||
export type ClanDetails = SuccessData<"get_clan_details">;
|
||||
export type ClanDetailsWithURI = ClanDetails & { uri: string };
|
||||
@@ -24,6 +31,14 @@ export interface MachineDetail {
|
||||
export type MachinesQueryResult = UseQueryResult<ListMachines>;
|
||||
export type ClanListQueryResult = UseQueryResult<ClanDetailsWithURI>[];
|
||||
|
||||
export const DefaultQueryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
retry: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const useMachinesQuery = (clanURI: string) => {
|
||||
const client = useApiClient();
|
||||
|
||||
@@ -155,10 +170,15 @@ export const useMachineDetailsQuery = (
|
||||
}));
|
||||
};
|
||||
|
||||
export const ClanDetailsPersister = experimental_createQueryPersister({
|
||||
storage: ClanDetailsStore,
|
||||
});
|
||||
|
||||
export const useClanDetailsQuery = (clanURI: string) => {
|
||||
const client = useApiClient();
|
||||
return useQuery<ClanDetailsWithURI>(() => ({
|
||||
queryKey: ["clans", encodeBase64(clanURI), "details"],
|
||||
persister: ClanDetailsPersister.persisterFn,
|
||||
queryFn: async () => {
|
||||
const call = client.fetch("get_clan_details", {
|
||||
flake: {
|
||||
@@ -169,7 +189,7 @@ export const useClanDetailsQuery = (clanURI: string) => {
|
||||
|
||||
if (result.status === "error") {
|
||||
// todo should we create some specific error types?
|
||||
console.error("Error fetching clan details:", result.errors);
|
||||
console.error("Error fetching clan details", clanURI, result.errors);
|
||||
throw new Error(result.errors[0].message);
|
||||
}
|
||||
|
||||
@@ -181,32 +201,54 @@ export const useClanDetailsQuery = (clanURI: string) => {
|
||||
}));
|
||||
};
|
||||
|
||||
export const useClanListQuery = (clanURIs: string[]): ClanListQueryResult => {
|
||||
export const useClanListQuery = (
|
||||
clanURIs: string[],
|
||||
activeClanURI?: string,
|
||||
): ClanListQueryResult => {
|
||||
const client = useApiClient();
|
||||
|
||||
return useQueries(() => ({
|
||||
queries: clanURIs.map((clanURI) => ({
|
||||
queryKey: ["clans", encodeBase64(clanURI), "details"],
|
||||
enabled: !!clanURI,
|
||||
queryFn: async () => {
|
||||
const call = client.fetch("get_clan_details", {
|
||||
flake: {
|
||||
identifier: clanURI,
|
||||
},
|
||||
});
|
||||
const result = await call.result;
|
||||
queries: clanURIs.map((clanURI) => {
|
||||
const queryKey = ["clans", encodeBase64(clanURI), "details"];
|
||||
|
||||
if (result.status === "error") {
|
||||
// todo should we create some specific error types?
|
||||
console.error("Error fetching clan details:", result.errors);
|
||||
throw new Error(result.errors[0].message);
|
||||
}
|
||||
return {
|
||||
// eslint-disable-next-line @tanstack/query/exhaustive-deps
|
||||
queryKey,
|
||||
persister: ClanDetailsPersister.persisterFn,
|
||||
queryFn: async () => {
|
||||
// we only perform a request for the active clan
|
||||
// for all others we load the cached query state
|
||||
// this is due to how expensive it currently is to evaluate a flake for clan details
|
||||
// it also helps when a clan folder has been moved/renamed
|
||||
if (clanURI != activeClanURI) {
|
||||
const cached = DefaultQueryClient.getQueryCache().find({
|
||||
queryKey,
|
||||
});
|
||||
|
||||
return {
|
||||
uri: clanURI,
|
||||
...result.data,
|
||||
};
|
||||
},
|
||||
})),
|
||||
if (cached?.state?.data) {
|
||||
return cached.state.data;
|
||||
}
|
||||
}
|
||||
|
||||
const call = client.fetch("get_clan_details", {
|
||||
flake: {
|
||||
identifier: clanURI,
|
||||
},
|
||||
});
|
||||
const result = await call.result;
|
||||
|
||||
if (result.status === "error") {
|
||||
// todo should we create some specific error types?
|
||||
throw new Error(result.errors[0].message);
|
||||
}
|
||||
|
||||
return {
|
||||
uri: clanURI,
|
||||
...result.data,
|
||||
};
|
||||
},
|
||||
};
|
||||
}),
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
import { render } from "solid-js/web";
|
||||
|
||||
import "./index.css";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query";
|
||||
import { QueryClientProvider } from "@tanstack/solid-query";
|
||||
import { Routes } from "@/src/routes";
|
||||
import { Router } from "@solidjs/router";
|
||||
import { Layout } from "@/src/routes/Layout";
|
||||
import { SolidQueryDevtools } from "@tanstack/solid-query-devtools";
|
||||
import { ApiClientProvider } from "./hooks/ApiClient";
|
||||
import { callApi } from "./hooks/api";
|
||||
|
||||
export const client = new QueryClient();
|
||||
import { DefaultQueryClient } from "@/src/hooks/queries";
|
||||
|
||||
const root = document.getElementById("app");
|
||||
|
||||
@@ -26,7 +25,7 @@ if (import.meta.env.DEV) {
|
||||
render(
|
||||
() => (
|
||||
<ApiClientProvider client={{ fetch: callApi }}>
|
||||
<QueryClientProvider client={client}>
|
||||
<QueryClientProvider client={DefaultQueryClient}>
|
||||
{import.meta.env.DEV && <SolidQueryDevtools initialIsOpen={true} />}
|
||||
<Router root={Layout}>{Routes}</Router>
|
||||
</QueryClientProvider>
|
||||
|
||||
@@ -35,30 +35,84 @@ import { TextInput } from "@/src/components/Form/TextInput";
|
||||
import { createForm, FieldValues, reset } from "@modular-forms/solid";
|
||||
import { Sidebar } from "@/src/components/Sidebar/Sidebar";
|
||||
import { UseQueryResult } from "@tanstack/solid-query";
|
||||
import { ListClansModal } from "@/src/components/ListClansModal/ListClansModal";
|
||||
|
||||
export const ClanContext = createContext<{
|
||||
interface ClanContextProps {
|
||||
clanURI: string;
|
||||
machinesQuery: MachinesQueryResult;
|
||||
activeClanQuery: UseQueryResult<ClanDetailsWithURI>;
|
||||
otherClanQueries: UseQueryResult<ClanDetailsWithURI>[];
|
||||
}>();
|
||||
allClansQueries: UseQueryResult<ClanDetailsWithURI>[];
|
||||
|
||||
isLoading(): boolean;
|
||||
isError(): boolean;
|
||||
}
|
||||
|
||||
class DefaultClanContext implements ClanContextProps {
|
||||
public readonly clanURI: string;
|
||||
|
||||
public readonly activeClanQuery: UseQueryResult<ClanDetailsWithURI>;
|
||||
public readonly otherClanQueries: UseQueryResult<ClanDetailsWithURI>[];
|
||||
public readonly allClansQueries: UseQueryResult<ClanDetailsWithURI>[];
|
||||
|
||||
public readonly machinesQuery: MachinesQueryResult;
|
||||
|
||||
allQueries: UseQueryResult[];
|
||||
|
||||
constructor(
|
||||
clanURI: string,
|
||||
machinesQuery: MachinesQueryResult,
|
||||
activeClanQuery: UseQueryResult<ClanDetailsWithURI>,
|
||||
otherClanQueries: UseQueryResult<ClanDetailsWithURI>[],
|
||||
) {
|
||||
this.clanURI = clanURI;
|
||||
this.machinesQuery = machinesQuery;
|
||||
|
||||
this.activeClanQuery = activeClanQuery;
|
||||
this.otherClanQueries = otherClanQueries;
|
||||
this.allClansQueries = [activeClanQuery, ...otherClanQueries];
|
||||
|
||||
this.allQueries = [machinesQuery, activeClanQuery, ...otherClanQueries];
|
||||
}
|
||||
|
||||
isLoading(): boolean {
|
||||
return this.allQueries.some((q) => q.isLoading);
|
||||
}
|
||||
|
||||
isError(): boolean {
|
||||
return this.activeClanQuery.isError;
|
||||
}
|
||||
}
|
||||
|
||||
export const ClanContext = createContext<ClanContextProps>();
|
||||
|
||||
export const Clan: Component<RouteSectionProps> = (props) => {
|
||||
const clanURI = useClanURI();
|
||||
const activeClanQuery = useClanDetailsQuery(clanURI);
|
||||
|
||||
createEffect(() => {
|
||||
if (activeClanQuery.isError) {
|
||||
console.error("Error loading active clan", activeClanQuery.error);
|
||||
}
|
||||
});
|
||||
|
||||
const otherClanQueries = useClanListQuery(
|
||||
clanURIs().filter((uri) => uri !== clanURI),
|
||||
clanURIs().filter((uri) => uri != clanURI),
|
||||
clanURI,
|
||||
);
|
||||
|
||||
const machinesQuery = useMachinesQuery(clanURI);
|
||||
|
||||
return (
|
||||
<ClanContext.Provider
|
||||
value={{
|
||||
machinesQuery,
|
||||
activeClanQuery,
|
||||
otherClanQueries,
|
||||
}}
|
||||
value={
|
||||
new DefaultClanContext(
|
||||
clanURI,
|
||||
machinesQuery,
|
||||
activeClanQuery,
|
||||
otherClanQueries,
|
||||
)
|
||||
}
|
||||
>
|
||||
<Sidebar class={cx(styles.sidebar)} />
|
||||
{props.children}
|
||||
@@ -118,14 +172,13 @@ const MockCreateMachine = (props: MockProps) => {
|
||||
};
|
||||
|
||||
const ClanSceneController = (props: RouteSectionProps) => {
|
||||
const clanURI = useClanURI();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const ctx = useContext(ClanContext);
|
||||
if (!ctx) {
|
||||
throw new Error("ClanContext not found");
|
||||
}
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [dialogHandlers, setDialogHandlers] = createSignal<{
|
||||
resolve: ({ id }: { id: string }) => void;
|
||||
reject: (err: unknown) => void;
|
||||
@@ -142,7 +195,7 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
const api = callApi("create_machine", {
|
||||
opts: {
|
||||
clan_dir: {
|
||||
identifier: clanURI,
|
||||
identifier: ctx.clanURI,
|
||||
},
|
||||
machine: {
|
||||
name: values.name,
|
||||
@@ -166,29 +219,38 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
|
||||
const [showModal, setShowModal] = createSignal(false);
|
||||
|
||||
const [loadingError, setLoadingError] = createSignal<
|
||||
{ title: string; description: string } | undefined
|
||||
>();
|
||||
const [loadingCooldown, setLoadingCooldown] = createSignal(false);
|
||||
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
setLoadingCooldown(true);
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
if (ctx.activeClanQuery.isError) {
|
||||
setLoadingError({
|
||||
title: "Error loading clan",
|
||||
description: ctx.activeClanQuery.error.message,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const [selectedIds, setSelectedIds] = createSignal<Set<string>>(new Set());
|
||||
|
||||
const onMachineSelect = (ids: Set<string>) => {
|
||||
// Get the first selected ID and navigate to its machine details
|
||||
const selected = ids.values().next().value;
|
||||
if (selected) {
|
||||
navigate(buildMachinePath(clanURI, selected));
|
||||
navigate(buildMachinePath(ctx.clanURI, selected));
|
||||
}
|
||||
};
|
||||
|
||||
const machine = createMemo(() => maybeUseMachineName());
|
||||
|
||||
createEffect(() => {
|
||||
console.log("Selected clan:", clanURI);
|
||||
});
|
||||
|
||||
createEffect(
|
||||
on(machine, (machineId) => {
|
||||
if (machineId) {
|
||||
@@ -203,32 +265,11 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
}),
|
||||
);
|
||||
|
||||
// a combination of the individual clan details query status and the machines query status
|
||||
// the cube scene needs the machines query, the sidebar needs the clans query and machines query results
|
||||
// so we wait on both before removing the loader to avoid any loading artefacts
|
||||
const isLoading = (): boolean => {
|
||||
// check if the active clan query is still loading
|
||||
if (ctx.activeClanQuery.isLoading) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check the machines query first
|
||||
if (ctx.machinesQuery.isLoading) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise iterate the clans query and return early if we find a queries that is still loading
|
||||
for (const query of ctx.otherClanQueries) {
|
||||
if (query.isLoading) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Show when={loadingError()}>
|
||||
<ListClansModal error={loadingError()} />
|
||||
</Show>
|
||||
<Show when={showModal()}>
|
||||
<MockCreateMachine
|
||||
onClose={() => {
|
||||
@@ -249,7 +290,7 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
</Show>
|
||||
<div
|
||||
class={cx({
|
||||
[styles.fadeOut]: !ctx.machinesQuery.isLoading && loadingCooldown(),
|
||||
[styles.fadeOut]: !ctx.isLoading() && loadingCooldown(),
|
||||
})}
|
||||
>
|
||||
<Splash />
|
||||
@@ -258,13 +299,10 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
<CubeScene
|
||||
selectedIds={selectedIds}
|
||||
onSelect={onMachineSelect}
|
||||
isLoading={isLoading()}
|
||||
isLoading={ctx.isLoading()}
|
||||
cubesQuery={ctx.machinesQuery}
|
||||
onCreate={onCreate}
|
||||
sceneStore={() => {
|
||||
const clanURI = useClanURI();
|
||||
return store.sceneData?.[clanURI];
|
||||
}}
|
||||
sceneStore={() => store.sceneData?.[ctx.clanURI]}
|
||||
setMachinePos={(machineId: string, pos: [number, number]) => {
|
||||
console.log("calling setStore", machineId, pos);
|
||||
setStore(
|
||||
@@ -272,13 +310,13 @@ const ClanSceneController = (props: RouteSectionProps) => {
|
||||
if (!s.sceneData) {
|
||||
s.sceneData = {};
|
||||
}
|
||||
if (!s.sceneData[clanURI]) {
|
||||
s.sceneData[clanURI] = {};
|
||||
if (!s.sceneData[ctx.clanURI]) {
|
||||
s.sceneData[ctx.clanURI] = {};
|
||||
}
|
||||
if (!s.sceneData[clanURI][machineId]) {
|
||||
s.sceneData[clanURI][machineId] = { position: pos };
|
||||
if (!s.sceneData[ctx.clanURI][machineId]) {
|
||||
s.sceneData[ctx.clanURI][machineId] = { position: pos };
|
||||
} else {
|
||||
s.sceneData[clanURI][machineId].position = pos;
|
||||
s.sceneData[ctx.clanURI][machineId].position = pos;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -7,12 +7,18 @@ export interface ClanStoreType {
|
||||
clanURIs: string[];
|
||||
activeClanURI?: string;
|
||||
sceneData: Record<string, SceneData>;
|
||||
queryCache: {
|
||||
clanDetails: Record<string, string>;
|
||||
};
|
||||
}
|
||||
|
||||
const [store, setStore] = makePersisted(
|
||||
createStore<ClanStoreType>({
|
||||
clanURIs: [],
|
||||
sceneData: {},
|
||||
queryCache: {
|
||||
clanDetails: {},
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "clanStore",
|
||||
|
||||
32
pkgs/clan-app/ui/src/stores/clanDetails.ts
Normal file
32
pkgs/clan-app/ui/src/stores/clanDetails.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { produce } from "solid-js/store";
|
||||
import { AsyncStorage } from "@tanstack/query-persist-client-core";
|
||||
import { setStore, store } from "@/src/stores/clan";
|
||||
|
||||
class ClanDetailsStoreImpl implements AsyncStorage {
|
||||
entries() {
|
||||
return Object.entries(store.queryCache.clanDetails);
|
||||
}
|
||||
|
||||
getItem(key: string) {
|
||||
return store.queryCache.clanDetails[key];
|
||||
}
|
||||
|
||||
removeItem(key: string) {
|
||||
setStore(
|
||||
produce((state) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
||||
delete state.queryCache.clanDetails[key];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
setItem(key: string, value: string) {
|
||||
return setStore(
|
||||
produce((state) => {
|
||||
state.queryCache.clanDetails[key] = value;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const ClanDetailsStore = new ClanDetailsStoreImpl();
|
||||
Reference in New Issue
Block a user