Compare commits
2 Commits
2feea0d645
...
tweaks
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d00df2438 | |||
| 8a0c3c16ed |
@@ -81,8 +81,6 @@ See https://tea.xyz
|
|||||||
|
|
||||||
## Other
|
## Other
|
||||||
|
|
||||||
### Reference Material
|
|
||||||
|
|
||||||
```
|
```
|
||||||
// reference material from https://github.com/trentlarson/endorser-mobile/blob/8dc8e0353e0cc80ffa7ed89ded15c8b0da92726b/src/utility/idUtility.ts#L83
|
// reference material from https://github.com/trentlarson/endorser-mobile/blob/8dc8e0353e0cc80ffa7ed89ded15c8b0da92726b/src/utility/idUtility.ts#L83
|
||||||
|
|
||||||
@@ -185,9 +183,3 @@ export const createAndStoreIdentifier = async (mnemonicPassword) => {
|
|||||||
return importAndStoreIdentifier(mnemonic, mnemonicPassword, false, [])
|
return importAndStoreIdentifier(mnemonic, mnemonicPassword, false, [])
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Kudos
|
|
||||||
|
|
||||||
* [Máximo Fernández](https://medium.com/@maxfarenas) for the 3D [code](https://github.com/maxfer03/vue-three-ns) and [explanatory post](https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80)
|
|
||||||
* [Many libraries]() such as Veramo.io, Vuejs.org, threejs
|
|
||||||
* [Bush 3D model](https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439)
|
|
||||||
|
|||||||
56
package-lock.json
generated
56
package-lock.json
generated
@@ -13,7 +13,6 @@
|
|||||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||||
"@fortawesome/vue-fontawesome": "^3.0.3",
|
"@fortawesome/vue-fontawesome": "^3.0.3",
|
||||||
"@pvermeer/dexie-encrypted-addon": "^2.0.5",
|
"@pvermeer/dexie-encrypted-addon": "^2.0.5",
|
||||||
"@tweenjs/tween.js": "^20.0.3",
|
|
||||||
"@veramo/core": "^5.1.2",
|
"@veramo/core": "^5.1.2",
|
||||||
"@veramo/credential-w3c": "^5.1.4",
|
"@veramo/credential-w3c": "^5.1.4",
|
||||||
"@veramo/data-store": "^5.1.2",
|
"@veramo/data-store": "^5.1.2",
|
||||||
@@ -45,7 +44,6 @@
|
|||||||
"readable-stream": "^4.3.0",
|
"readable-stream": "^4.3.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
"three": "^0.152.2",
|
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-axios": "^3.5.2",
|
"vue-axios": "^3.5.2",
|
||||||
"vue-class-component": "^8.0.0-0",
|
"vue-class-component": "^8.0.0-0",
|
||||||
@@ -56,7 +54,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/ramda": "^0.28.23",
|
"@types/ramda": "^0.28.23",
|
||||||
"@types/three": "^0.152.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
||||||
"@typescript-eslint/parser": "^5.57.0",
|
"@typescript-eslint/parser": "^5.57.0",
|
||||||
"@vue/cli-plugin-babel": "~5.0.8",
|
"@vue/cli-plugin-babel": "~5.0.8",
|
||||||
@@ -7744,11 +7741,6 @@
|
|||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tweenjs/tween.js": {
|
|
||||||
"version": "20.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-20.0.3.tgz",
|
|
||||||
"integrity": "sha512-SYUe1UgY5HM05EB4+0B4arq2IPjvyzKXoklXKxSYrc2IFxGm1cBrqg5XbiB5uwbs0xY5j+rj986NAJMM0KZaUw=="
|
|
||||||
},
|
|
||||||
"node_modules/@types/bn.js": {
|
"node_modules/@types/bn.js": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz",
|
||||||
@@ -8014,31 +8006,6 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/stats.js": {
|
|
||||||
"version": "0.17.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz",
|
|
||||||
"integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@types/three": {
|
|
||||||
"version": "0.152.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.152.0.tgz",
|
|
||||||
"integrity": "sha512-9QdaV5bfZEqeQi0xkXLdnoJt7lgYZbppdBAgJSWRicdtZoCYJ34nS2QkdeuzXt+UXExofk4OWqMzdX71HeDOVg==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@tweenjs/tween.js": "~18.6.4",
|
|
||||||
"@types/stats.js": "*",
|
|
||||||
"@types/webxr": "*",
|
|
||||||
"fflate": "~0.6.9",
|
|
||||||
"lil-gui": "~0.17.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/three/node_modules/@tweenjs/tween.js": {
|
|
||||||
"version": "18.6.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-18.6.4.tgz",
|
|
||||||
"integrity": "sha512-lB9lMjuqjtuJrx7/kOkqQBtllspPIN+96OvTCeJ2j5FEzinoAXTdAMFnDAQT1KVPRlnYfBrqxtqP66vDM40xxQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@types/trusted-types": {
|
"node_modules/@types/trusted-types": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.2.tgz",
|
||||||
@@ -8056,12 +8023,6 @@
|
|||||||
"integrity": "sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==",
|
"integrity": "sha512-56/MAlX5WMsPVbOg7tAxnYvNYMMWr/QJiIp6BxVSW3JJXUVzzOn64qW8TzQyMSqSUFM2+PVI4aUHcHOzIz/1tg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/webxr": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.2.tgz",
|
|
||||||
"integrity": "sha512-szL74BnIcok9m7QwYtVmQ+EdIKwbjPANudfuvDrAF8Cljg9MKUlIoc1w5tjj9PMpeSH3U1Xnx//czQybJ0EfSw==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/@types/ws": {
|
"node_modules/@types/ws": {
|
||||||
"version": "8.5.3",
|
"version": "8.5.3",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.5.3.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.5.3.tgz",
|
||||||
@@ -14828,12 +14789,6 @@
|
|||||||
"optional": true,
|
"optional": true,
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/fflate": {
|
|
||||||
"version": "0.6.10",
|
|
||||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.6.10.tgz",
|
|
||||||
"integrity": "sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/figures": {
|
"node_modules/figures": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmmirror.com/figures/-/figures-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/figures/-/figures-2.0.0.tgz",
|
||||||
@@ -18349,12 +18304,6 @@
|
|||||||
"node": ">= 0.8.0"
|
"node": ">= 0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lil-gui": {
|
|
||||||
"version": "0.17.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lil-gui/-/lil-gui-0.17.0.tgz",
|
|
||||||
"integrity": "sha512-MVBHmgY+uEbmJNApAaPbtvNh1RCAeMnKym82SBjtp5rODTYKWtM+MXHCifLe2H2Ti1HuBGBtK/5SyG4ShQ3pUQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/lilconfig": {
|
"node_modules/lilconfig": {
|
||||||
"version": "2.0.6",
|
"version": "2.0.6",
|
||||||
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.0.6.tgz",
|
"resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.0.6.tgz",
|
||||||
@@ -25235,11 +25184,6 @@
|
|||||||
"url": "https://opencollective.com/webpack"
|
"url": "https://opencollective.com/webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/three": {
|
|
||||||
"version": "0.152.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/three/-/three-0.152.2.tgz",
|
|
||||||
"integrity": "sha512-Ff9zIpSfkkqcBcpdiFo2f35vA9ZucO+N8TNacJOqaEE6DrB0eufItVMib8bK8Pcju/ZNT6a7blE1GhTpkdsILw=="
|
|
||||||
},
|
|
||||||
"node_modules/throat": {
|
"node_modules/throat": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz",
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||||
"@fortawesome/vue-fontawesome": "^3.0.3",
|
"@fortawesome/vue-fontawesome": "^3.0.3",
|
||||||
"@pvermeer/dexie-encrypted-addon": "^2.0.5",
|
"@pvermeer/dexie-encrypted-addon": "^2.0.5",
|
||||||
"@tweenjs/tween.js": "^20.0.3",
|
|
||||||
"@veramo/core": "^5.1.2",
|
"@veramo/core": "^5.1.2",
|
||||||
"@veramo/credential-w3c": "^5.1.4",
|
"@veramo/credential-w3c": "^5.1.4",
|
||||||
"@veramo/data-store": "^5.1.2",
|
"@veramo/data-store": "^5.1.2",
|
||||||
@@ -45,7 +44,6 @@
|
|||||||
"readable-stream": "^4.3.0",
|
"readable-stream": "^4.3.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
"three": "^0.152.2",
|
|
||||||
"vue": "^3.2.47",
|
"vue": "^3.2.47",
|
||||||
"vue-axios": "^3.5.2",
|
"vue-axios": "^3.5.2",
|
||||||
"vue-class-component": "^8.0.0-0",
|
"vue-class-component": "^8.0.0-0",
|
||||||
@@ -56,7 +54,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/ramda": "^0.28.23",
|
"@types/ramda": "^0.28.23",
|
||||||
"@types/three": "^0.152.0",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
"@typescript-eslint/eslint-plugin": "^5.57.0",
|
||||||
"@typescript-eslint/parser": "^5.57.0",
|
"@typescript-eslint/parser": "^5.57.0",
|
||||||
"@vue/cli-plugin-babel": "~5.0.8",
|
"@vue/cli-plugin-babel": "~5.0.8",
|
||||||
|
|||||||
20
project.yaml
20
project.yaml
@@ -5,11 +5,9 @@
|
|||||||
|
|
||||||
- replace user-affecting console.logs with error messages (eg. catches)
|
- replace user-affecting console.logs with error messages (eg. catches)
|
||||||
|
|
||||||
- stats v1 :
|
|
||||||
- 01 show numeric stats
|
|
||||||
- 01 link to world for specific stats
|
|
||||||
|
|
||||||
- contacts v1 :
|
- contacts v1 :
|
||||||
|
- .2 warn about amounts when you cannot see them
|
||||||
|
- .1 add confirmation message when 'copy' succeeds
|
||||||
- .5 switch to prod server
|
- .5 switch to prod server
|
||||||
- .5 Add page to show seed.
|
- .5 Add page to show seed.
|
||||||
- 01 Provide a way to import the non-sensitive data.
|
- 01 Provide a way to import the non-sensitive data.
|
||||||
@@ -35,16 +33,7 @@
|
|||||||
|
|
||||||
- backup all data
|
- backup all data
|
||||||
|
|
||||||
- .5 customize favicon
|
- Next Viable Product afterward
|
||||||
- .5 make advanced features harder to access
|
|
||||||
|
|
||||||
- Release Minimum Viable Product :
|
|
||||||
- Turn off stats-world or ensure it's usable (eg. cannot zoom out too far and lose world, cannot screenshot).
|
|
||||||
|
|
||||||
- Stats :
|
|
||||||
- 01 point out user's location on the world
|
|
||||||
- 01 present a credential selected from the stats
|
|
||||||
- 04 show gives spreading to other places
|
|
||||||
|
|
||||||
- Connect with phone contacts
|
- Connect with phone contacts
|
||||||
|
|
||||||
@@ -53,6 +42,3 @@
|
|||||||
- Peer DID
|
- Peer DID
|
||||||
|
|
||||||
- DIDComm
|
- DIDComm
|
||||||
|
|
||||||
- v video for multiple identities https://youtu.be/p8L87AeD76w
|
|
||||||
- v video for adding time to contacts https://youtu.be/7Yylczevp10
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.5 MiB |
@@ -1,11 +0,0 @@
|
|||||||
Model Information:
|
|
||||||
* title: Lupine Plant
|
|
||||||
* source: https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439
|
|
||||||
* author: rufusrockwell (https://sketchfab.com/rufusrockwell)
|
|
||||||
|
|
||||||
Model License:
|
|
||||||
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
|
||||||
* requirements: Author must be credited. Commercial use is allowed.
|
|
||||||
|
|
||||||
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
|
|
||||||
This work is based on "Lupine Plant" (https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439) by rufusrockwell (https://sketchfab.com/rufusrockwell) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
|
|
||||||
Binary file not shown.
@@ -1,229 +0,0 @@
|
|||||||
{
|
|
||||||
"accessors": [
|
|
||||||
{
|
|
||||||
"bufferView": 2,
|
|
||||||
"componentType": 5126,
|
|
||||||
"count": 2759,
|
|
||||||
"max": [
|
|
||||||
41.3074951171875,
|
|
||||||
40.37548828125,
|
|
||||||
87.85917663574219
|
|
||||||
],
|
|
||||||
"min": [
|
|
||||||
-35.245540618896484,
|
|
||||||
-36.895416259765625,
|
|
||||||
-0.9094290137290955
|
|
||||||
],
|
|
||||||
"type": "VEC3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bufferView": 2,
|
|
||||||
"byteOffset": 33108,
|
|
||||||
"componentType": 5126,
|
|
||||||
"count": 2759,
|
|
||||||
"max": [
|
|
||||||
0.9999382495880127,
|
|
||||||
0.9986748695373535,
|
|
||||||
0.9985831379890442
|
|
||||||
],
|
|
||||||
"min": [
|
|
||||||
-0.9998949766159058,
|
|
||||||
-0.9975876212120056,
|
|
||||||
-0.411094069480896
|
|
||||||
],
|
|
||||||
"type": "VEC3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bufferView": 3,
|
|
||||||
"componentType": 5126,
|
|
||||||
"count": 2759,
|
|
||||||
"max": [
|
|
||||||
0.9987699389457703,
|
|
||||||
0.9998998045921326,
|
|
||||||
0.9577858448028564,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
"min": [
|
|
||||||
-0.9987726807594299,
|
|
||||||
-0.9990445971488953,
|
|
||||||
-0.999801516532898,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
"type": "VEC4"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bufferView": 1,
|
|
||||||
"componentType": 5126,
|
|
||||||
"count": 2759,
|
|
||||||
"max": [
|
|
||||||
1.0061479806900024,
|
|
||||||
0.9993550181388855
|
|
||||||
],
|
|
||||||
"min": [
|
|
||||||
0.00279300007969141,
|
|
||||||
0.0011620000004768372
|
|
||||||
],
|
|
||||||
"type": "VEC2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"bufferView": 0,
|
|
||||||
"componentType": 5125,
|
|
||||||
"count": 6378,
|
|
||||||
"type": "SCALAR"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"asset": {
|
|
||||||
"extras": {
|
|
||||||
"author": "rufusrockwell (https://sketchfab.com/rufusrockwell)",
|
|
||||||
"license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)",
|
|
||||||
"source": "https://sketchfab.com/3d-models/lupine-plant-bf30f1110c174d4baedda0ed63778439",
|
|
||||||
"title": "Lupine Plant"
|
|
||||||
},
|
|
||||||
"generator": "Sketchfab-12.68.0",
|
|
||||||
"version": "2.0"
|
|
||||||
},
|
|
||||||
"bufferViews": [
|
|
||||||
{
|
|
||||||
"buffer": 0,
|
|
||||||
"byteLength": 25512,
|
|
||||||
"name": "floatBufferViews",
|
|
||||||
"target": 34963
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"buffer": 0,
|
|
||||||
"byteLength": 22072,
|
|
||||||
"byteOffset": 25512,
|
|
||||||
"byteStride": 8,
|
|
||||||
"name": "floatBufferViews",
|
|
||||||
"target": 34962
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"buffer": 0,
|
|
||||||
"byteLength": 66216,
|
|
||||||
"byteOffset": 47584,
|
|
||||||
"byteStride": 12,
|
|
||||||
"name": "floatBufferViews",
|
|
||||||
"target": 34962
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"buffer": 0,
|
|
||||||
"byteLength": 44144,
|
|
||||||
"byteOffset": 113800,
|
|
||||||
"byteStride": 16,
|
|
||||||
"name": "floatBufferViews",
|
|
||||||
"target": 34962
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"buffers": [
|
|
||||||
{
|
|
||||||
"byteLength": 157944,
|
|
||||||
"uri": "scene.bin"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"images": [
|
|
||||||
{
|
|
||||||
"uri": "textures/lambert2SG_baseColor.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"uri": "textures/lambert2SG_normal.png"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"materials": [
|
|
||||||
{
|
|
||||||
"alphaCutoff": 0.2,
|
|
||||||
"alphaMode": "MASK",
|
|
||||||
"doubleSided": true,
|
|
||||||
"name": "lambert2SG",
|
|
||||||
"normalTexture": {
|
|
||||||
"index": 1
|
|
||||||
},
|
|
||||||
"pbrMetallicRoughness": {
|
|
||||||
"baseColorTexture": {
|
|
||||||
"index": 0
|
|
||||||
},
|
|
||||||
"metallicFactor": 0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"meshes": [
|
|
||||||
{
|
|
||||||
"name": "Object_0",
|
|
||||||
"primitives": [
|
|
||||||
{
|
|
||||||
"attributes": {
|
|
||||||
"NORMAL": 1,
|
|
||||||
"POSITION": 0,
|
|
||||||
"TANGENT": 2,
|
|
||||||
"TEXCOORD_0": 3
|
|
||||||
},
|
|
||||||
"indices": 4,
|
|
||||||
"material": 0,
|
|
||||||
"mode": 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"nodes": [
|
|
||||||
{
|
|
||||||
"children": [
|
|
||||||
1
|
|
||||||
],
|
|
||||||
"matrix": [
|
|
||||||
1.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
2.220446049250313e-16,
|
|
||||||
-1.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
2.220446049250313e-16,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
1.0
|
|
||||||
],
|
|
||||||
"name": "Sketchfab_model"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"children": [
|
|
||||||
2
|
|
||||||
],
|
|
||||||
"name": "LupineSF.obj.cleaner.materialmerger.gles"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"mesh": 0,
|
|
||||||
"name": "Object_2"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"samplers": [
|
|
||||||
{
|
|
||||||
"magFilter": 9729,
|
|
||||||
"minFilter": 9987,
|
|
||||||
"wrapS": 10497,
|
|
||||||
"wrapT": 10497
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"scene": 0,
|
|
||||||
"scenes": [
|
|
||||||
{
|
|
||||||
"name": "Sketchfab_Scene",
|
|
||||||
"nodes": [
|
|
||||||
0
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"textures": [
|
|
||||||
{
|
|
||||||
"sampler": 0,
|
|
||||||
"source": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"sampler": 0,
|
|
||||||
"source": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.7 MiB |
@@ -1,104 +0,0 @@
|
|||||||
// from https://medium.com/nicasource/building-an-interactive-web-portfolio-with-vue-three-js-part-three-implementing-three-js-452cb375ef80
|
|
||||||
|
|
||||||
import * as TWEEN from "@tweenjs/tween.js";
|
|
||||||
import * as THREE from "three";
|
|
||||||
|
|
||||||
import { createCamera } from "./components/camera.js";
|
|
||||||
import { createLights } from "./components/lights.js";
|
|
||||||
import { createScene } from "./components/scene.js";
|
|
||||||
import { loadLandmarks } from "./components/objects/landmarks.js";
|
|
||||||
import { createTerrain } from "./components/objects/terrain.js";
|
|
||||||
import { Loop } from "./systems/Loop.js";
|
|
||||||
import { Resizer } from "./systems/Resizer.js";
|
|
||||||
import { createControls } from "./systems/controls.js";
|
|
||||||
import { createRenderer } from "./systems/renderer.js";
|
|
||||||
|
|
||||||
const COLOR1 = "#dddddd";
|
|
||||||
const COLOR2 = "#0055aa";
|
|
||||||
|
|
||||||
class World {
|
|
||||||
constructor(container, vue) {
|
|
||||||
this.PLATFORM_BORDER = 5;
|
|
||||||
this.PLATFORM_EDGE_FOR_UNKNOWNS = 10;
|
|
||||||
this.PLATFORM_SIZE = 100; // note that the loadLandmarks calculations may still assume 100
|
|
||||||
|
|
||||||
this.update = this.update.bind(this);
|
|
||||||
|
|
||||||
// Instances of camera, scene, and renderer
|
|
||||||
this.camera = createCamera();
|
|
||||||
this.scene = createScene(COLOR2);
|
|
||||||
this.renderer = createRenderer();
|
|
||||||
|
|
||||||
// necessary for models, says https://threejs.org/docs/index.html#examples/en/loaders/GLTFLoader
|
|
||||||
this.renderer.outputColorSpace = THREE.SRGBColorSpace;
|
|
||||||
|
|
||||||
this.light = null;
|
|
||||||
this.lights = [];
|
|
||||||
this.bushes = [];
|
|
||||||
|
|
||||||
// Initialize Loop
|
|
||||||
this.loop = new Loop(this.camera, this.scene, this.renderer);
|
|
||||||
|
|
||||||
container.append(this.renderer.domElement);
|
|
||||||
|
|
||||||
// Orbit Controls
|
|
||||||
const controls = createControls(this.camera, this.renderer.domElement);
|
|
||||||
|
|
||||||
// Light Instance, with optional light helper
|
|
||||||
const { light } = createLights(COLOR1);
|
|
||||||
|
|
||||||
// Terrain Instance
|
|
||||||
const terrain = createTerrain({
|
|
||||||
color: COLOR1,
|
|
||||||
height: this.PLATFORM_SIZE + this.PLATFORM_BORDER * 2,
|
|
||||||
width:
|
|
||||||
this.PLATFORM_SIZE +
|
|
||||||
this.PLATFORM_BORDER * 2 +
|
|
||||||
this.PLATFORM_EDGE_FOR_UNKNOWNS * 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loop.updatables.push(controls);
|
|
||||||
this.loop.updatables.push(light);
|
|
||||||
this.loop.updatables.push(terrain);
|
|
||||||
|
|
||||||
this.scene.add(light, terrain);
|
|
||||||
|
|
||||||
loadLandmarks(vue, this, this.scene, this.loop);
|
|
||||||
|
|
||||||
requestAnimationFrame(this.update);
|
|
||||||
|
|
||||||
// Responsive handler
|
|
||||||
const resizer = new Resizer(container, this.camera, this.renderer);
|
|
||||||
resizer.onResize = () => {
|
|
||||||
this.render();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
update(time) {
|
|
||||||
TWEEN.update(time);
|
|
||||||
this.lights.forEach((light) => {
|
|
||||||
light.updateMatrixWorld();
|
|
||||||
light.target.updateMatrixWorld();
|
|
||||||
});
|
|
||||||
this.lights.forEach((bush) => {
|
|
||||||
bush.updateMatrixWorld();
|
|
||||||
});
|
|
||||||
requestAnimationFrame(this.update);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
// draw a single frame
|
|
||||||
this.renderer.render(this.scene, this.camera);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Animation handlers
|
|
||||||
start() {
|
|
||||||
this.loop.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
this.loop.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { World };
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { PerspectiveCamera } from "three";
|
|
||||||
|
|
||||||
function createCamera() {
|
|
||||||
const camera = new PerspectiveCamera(
|
|
||||||
35, // fov = Field Of View
|
|
||||||
1, // aspect ratio (dummy value)
|
|
||||||
0.1, // near clipping plane
|
|
||||||
350 // far clipping plane
|
|
||||||
);
|
|
||||||
|
|
||||||
// move the camera back so we can view the scene
|
|
||||||
camera.position.set(0, 100, 200);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
camera.tick = () => {};
|
|
||||||
|
|
||||||
return camera;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { createCamera };
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { DirectionalLight, DirectionalLightHelper } from "three";
|
|
||||||
|
|
||||||
function createLights(color) {
|
|
||||||
const light = new DirectionalLight(color, 4);
|
|
||||||
const lightHelper = new DirectionalLightHelper(light, 0);
|
|
||||||
light.position.set(60, 100, 30);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
light.tick = () => {};
|
|
||||||
|
|
||||||
return { light, lightHelper };
|
|
||||||
}
|
|
||||||
|
|
||||||
export { createLights };
|
|
||||||
@@ -1,225 +0,0 @@
|
|||||||
import axios from "axios";
|
|
||||||
import * as R from "ramda";
|
|
||||||
import * as THREE from "three";
|
|
||||||
import { GLTFLoader } from "three/addons/loaders/GLTFLoader";
|
|
||||||
import * as SkeletonUtils from "three/addons/utils/SkeletonUtils";
|
|
||||||
import * as TWEEN from "@tweenjs/tween.js";
|
|
||||||
import { AppString } from "@/constants/app";
|
|
||||||
import { accountsDB, db } from "@/db";
|
|
||||||
import { MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
|
||||||
import { accessToken } from "@/libs/crypto";
|
|
||||||
|
|
||||||
const ANIMATION_DURATION_SECS = 10;
|
|
||||||
const BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
||||||
const ENDORSER_ENTITY_PREFIX = "https://endorser.ch/entity/";
|
|
||||||
|
|
||||||
export async function loadLandmarks(vue, world, scene, loop) {
|
|
||||||
const endorserApiServer = AppString.DEFAULT_ENDORSER_API_SERVER;
|
|
||||||
try {
|
|
||||||
await db.open();
|
|
||||||
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
|
||||||
const activeDid = settings?.activeDid || "";
|
|
||||||
await accountsDB.open();
|
|
||||||
const accounts = await accountsDB.accounts.toArray();
|
|
||||||
const account = R.find((acc) => acc.did === activeDid, accounts);
|
|
||||||
const identity = JSON.parse(account?.identity || "undefined");
|
|
||||||
const token = await accessToken(identity);
|
|
||||||
|
|
||||||
const url =
|
|
||||||
endorserApiServer + "/api/v2/report/claims?claimType=GiveAction";
|
|
||||||
const headers = {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
Authorization: "Bearer " + token,
|
|
||||||
};
|
|
||||||
const resp = await axios.get(url, { headers: headers });
|
|
||||||
if (resp.status === 200) {
|
|
||||||
const minDate = resp.data.data[resp.data.data.length - 1].issuedAt;
|
|
||||||
const maxDate = resp.data.data[0].issuedAt;
|
|
||||||
const minTimeMillis = new Date(minDate).getTime();
|
|
||||||
const fullTimeMillis = new Date(maxDate).getTime() - minTimeMillis;
|
|
||||||
// ratio of animation time to real time
|
|
||||||
const fakeRealRatio = (ANIMATION_DURATION_SECS * 1000) / fullTimeMillis;
|
|
||||||
|
|
||||||
// load plant model first because it takes a second
|
|
||||||
const loader = new GLTFLoader();
|
|
||||||
// choose the right plant
|
|
||||||
const modelLoc = "/models/lupine_plant/scene.gltf", // push with pokies
|
|
||||||
modScale = 0.1;
|
|
||||||
//const modelLoc = "/models/round_bush/scene.gltf", // green & pink
|
|
||||||
// modScale = 1;
|
|
||||||
//const modelLoc = "/models/coreopsis-flower.glb", // 3 flowers
|
|
||||||
// modScale = 2;
|
|
||||||
//const modelLoc = "/models/a_bush/scene.gltf", // purple leaves
|
|
||||||
// modScale = 15;
|
|
||||||
|
|
||||||
// calculate positions for each claim, especially because some are random
|
|
||||||
const locations = resp.data.data.map((claim) =>
|
|
||||||
locForGive(claim, world.PLATFORM_SIZE, world.PLATFORM_EDGE_FOR_UNKNOWNS)
|
|
||||||
);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
||||||
loader.load(
|
|
||||||
modelLoc,
|
|
||||||
function (gltf) {
|
|
||||||
gltf.scene.scale.set(0, 0, 0);
|
|
||||||
for (let i = 0; i < resp.data.data.length; i++) {
|
|
||||||
// claim is a GiveServerRecord (see endorserServer.ts)
|
|
||||||
const claim = resp.data.data[i];
|
|
||||||
const newPlant = SkeletonUtils.clone(gltf.scene);
|
|
||||||
|
|
||||||
const loc = locations[i];
|
|
||||||
newPlant.position.set(loc.x, 0, loc.z);
|
|
||||||
|
|
||||||
world.scene.add(newPlant);
|
|
||||||
const timeDelayMillis =
|
|
||||||
fakeRealRatio *
|
|
||||||
(new Date(claim.issuedAt).getTime() - minTimeMillis);
|
|
||||||
new TWEEN.Tween(newPlant.scale)
|
|
||||||
.delay(timeDelayMillis)
|
|
||||||
.to({ x: modScale, y: modScale, z: modScale }, 5000)
|
|
||||||
.start();
|
|
||||||
world.bushes = [...world.bushes, newPlant];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
function (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// calculate when lights shine on appearing claim area
|
|
||||||
for (let i = 0; i < resp.data.data.length; i++) {
|
|
||||||
// claim is a GiveServerRecord (see endorserServer.ts)
|
|
||||||
const claim = resp.data.data[i];
|
|
||||||
|
|
||||||
const loc = locations[i];
|
|
||||||
const light = createLight();
|
|
||||||
light.position.set(loc.x, 20, loc.z);
|
|
||||||
light.target.position.set(loc.x, 0, loc.z);
|
|
||||||
loop.updatables.push(light);
|
|
||||||
scene.add(light);
|
|
||||||
scene.add(light.target);
|
|
||||||
|
|
||||||
// now figure out the timing and shine a light
|
|
||||||
const timeDelayMillis =
|
|
||||||
fakeRealRatio * (new Date(claim.issuedAt).getTime() - minTimeMillis);
|
|
||||||
new TWEEN.Tween(light)
|
|
||||||
.delay(timeDelayMillis)
|
|
||||||
.to({ intensity: 100 }, 10)
|
|
||||||
.chain(
|
|
||||||
new TWEEN.Tween(light.position)
|
|
||||||
.to({ y: 5 }, 5000)
|
|
||||||
.onComplete(() => {
|
|
||||||
scene.remove(light);
|
|
||||||
light.dispose();
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.start();
|
|
||||||
world.lights = [...world.lights, light];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
"Got bad server response status & data of",
|
|
||||||
resp.status,
|
|
||||||
resp.data
|
|
||||||
);
|
|
||||||
vue.setAlert(
|
|
||||||
"Error With Server",
|
|
||||||
"There was an error retrieving your claims from the server."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log("Got exception contacting server:", error);
|
|
||||||
vue.setAlert(
|
|
||||||
"Error With Server",
|
|
||||||
"There was a problem retrieving your claims from the server."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param giveClaim
|
|
||||||
* @returns {x:float, z:float} where -50 <= x & z < 50
|
|
||||||
*/
|
|
||||||
function locForGive(giveClaim, platformWidth, borderWidth) {
|
|
||||||
let loc;
|
|
||||||
if (giveClaim?.claim?.recipient?.identifier) {
|
|
||||||
// this is directly to a person
|
|
||||||
loc = locForEthrDid(giveClaim.claim.recipient.identifier);
|
|
||||||
loc = { x: loc.x - platformWidth / 2, z: loc.z - platformWidth / 2 };
|
|
||||||
} else if (giveClaim?.object?.isPartOf?.identifier) {
|
|
||||||
// this is probably to a project
|
|
||||||
const objId = giveClaim.object.isPartOf.identifier;
|
|
||||||
if (objId.startsWith(ENDORSER_ENTITY_PREFIX)) {
|
|
||||||
loc = locForUlid(objId.substring(ENDORSER_ENTITY_PREFIX.length));
|
|
||||||
loc = { x: loc.x - platformWidth / 2, z: loc.z - platformWidth / 2 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!loc) {
|
|
||||||
// it must be outside our known addresses so let's put it somewhere random
|
|
||||||
const leftSide = Math.random() < 0.5;
|
|
||||||
loc = {
|
|
||||||
x: leftSide
|
|
||||||
? -platformWidth / 2 - borderWidth / 2
|
|
||||||
: platformWidth / 2 + borderWidth / 2,
|
|
||||||
z: Math.random() * platformWidth - platformWidth / 2,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a deterministic x & z location based on the randomness in a ULID.
|
|
||||||
*
|
|
||||||
* We'll use the first 20 bits for the x coordinate and next 20 for the z.
|
|
||||||
* That's a bit over a trillion locations... should be enough for pretty
|
|
||||||
* unique locations for this fun kind of application within a network
|
|
||||||
* -- though they're not guaranteed unique without the full ID.
|
|
||||||
* (We're leaving 40 bits for other properties.)
|
|
||||||
*
|
|
||||||
* @param ulid
|
|
||||||
* @returns {x: float, z: float} where 0 <= x & z < 100
|
|
||||||
*/
|
|
||||||
function locForUlid(ulid) {
|
|
||||||
// The random parts of a ULID come after the first 10 characters.
|
|
||||||
const randomness = ulid.substring(10);
|
|
||||||
|
|
||||||
// That leaves 16 characters of randomness, or 80 bits.
|
|
||||||
|
|
||||||
// We're currently only using 32 possible x and z values
|
|
||||||
// because the display is pretty low-fidelity at this point.
|
|
||||||
|
|
||||||
// Similar code is below.
|
|
||||||
const x = (100 * BASE32.indexOf(randomness.substring(0, 1))) / 32;
|
|
||||||
const z = (100 * BASE32.indexOf(randomness.substring(4, 5))) / 32;
|
|
||||||
return { x, z };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See locForUlid
|
|
||||||
* @param did
|
|
||||||
* @returns {x: float, z: float} where 0 <= x & z < 100
|
|
||||||
*/
|
|
||||||
function locForEthrDid(did) {
|
|
||||||
// "did:ethr:0x..."
|
|
||||||
if (did.length < 51) {
|
|
||||||
return { x: 0, z: 0 };
|
|
||||||
} else {
|
|
||||||
const randomness = did.substring(11);
|
|
||||||
// We'll take the first 4 bits for 16 possible x & z values.
|
|
||||||
const xOff = parseInt(Number("0x" + randomness.substring(0, 1)), 10);
|
|
||||||
const x = (xOff * 100) / 16;
|
|
||||||
// ... and since we're reserving 20 bits total for x, start with character 5.
|
|
||||||
const zOff = parseInt(Number("0x" + randomness.substring(5, 6)), 10);
|
|
||||||
const z = (zOff * 100) / 16;
|
|
||||||
return { x, z };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLight() {
|
|
||||||
const light = new THREE.SpotLight(0xffffff, 0, 0, Math.PI / 8, 0.5, 0);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
light.tick = () => {};
|
|
||||||
return light;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { PlaneGeometry, MeshLambertMaterial, Mesh, TextureLoader } from "three";
|
|
||||||
|
|
||||||
export function createTerrain(props) {
|
|
||||||
const loader = new TextureLoader();
|
|
||||||
const height = loader.load("img/textures/forest-floor.png");
|
|
||||||
// w h
|
|
||||||
const geometry = new PlaneGeometry(props.width, props.height, 64, 64);
|
|
||||||
|
|
||||||
const material = new MeshLambertMaterial({
|
|
||||||
color: props.color,
|
|
||||||
flatShading: true,
|
|
||||||
map: height,
|
|
||||||
//displacementMap: height,
|
|
||||||
//displacementScale: 5,
|
|
||||||
});
|
|
||||||
|
|
||||||
const plane = new Mesh(geometry, material);
|
|
||||||
plane.position.set(0, 0, 0);
|
|
||||||
plane.rotation.x -= Math.PI * 0.5;
|
|
||||||
|
|
||||||
//Storing our original vertices position on a new attribute
|
|
||||||
plane.geometry.attributes.position.originalPosition =
|
|
||||||
plane.geometry.attributes.position.array;
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
plane.tick = () => {};
|
|
||||||
|
|
||||||
return plane;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { Color, Scene } from "three";
|
|
||||||
|
|
||||||
function createScene(color) {
|
|
||||||
const scene = new Scene();
|
|
||||||
|
|
||||||
scene.background = new Color(color);
|
|
||||||
//scene.fog = new Fog(color, 60, 90);
|
|
||||||
return scene;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { createScene };
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
import { Clock } from "three";
|
|
||||||
|
|
||||||
const clock = new Clock();
|
|
||||||
|
|
||||||
class Loop {
|
|
||||||
constructor(camera, scene, renderer) {
|
|
||||||
this.camera = camera;
|
|
||||||
this.scene = scene;
|
|
||||||
this.renderer = renderer;
|
|
||||||
this.updatables = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
this.renderer.setAnimationLoop(() => {
|
|
||||||
this.tick();
|
|
||||||
// render a frame
|
|
||||||
this.renderer.render(this.scene, this.camera);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stop() {
|
|
||||||
this.renderer.setAnimationLoop(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
tick() {
|
|
||||||
const delta = clock.getDelta();
|
|
||||||
for (const object of this.updatables) {
|
|
||||||
object.tick(delta);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Loop };
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
const setSize = (container, camera, renderer) => {
|
|
||||||
// These are great for full-screen, which adjusts to a window.
|
|
||||||
const height = window.innerHeight;
|
|
||||||
const width = window.innerWidth - 50;
|
|
||||||
// These are better for fitting in a container, which stays that size.
|
|
||||||
//const height = container.scrollHeight;
|
|
||||||
//const width = container.scrollWidth;
|
|
||||||
|
|
||||||
camera.aspect = width / height;
|
|
||||||
camera.updateProjectionMatrix();
|
|
||||||
|
|
||||||
renderer.setSize(width, height);
|
|
||||||
renderer.setPixelRatio(window.devicePixelRatio);
|
|
||||||
};
|
|
||||||
|
|
||||||
class Resizer {
|
|
||||||
constructor(container, camera, renderer) {
|
|
||||||
// set initial size on load
|
|
||||||
setSize(container, camera, renderer);
|
|
||||||
|
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
// set the size again if a resize occurs
|
|
||||||
setSize(container, camera, renderer);
|
|
||||||
// perform any custom actions
|
|
||||||
this.onResize();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
||||||
onResize() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export { Resizer };
|
|
||||||
38
src/components/World/systems/controls.js
vendored
38
src/components/World/systems/controls.js
vendored
@@ -1,38 +0,0 @@
|
|||||||
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
|
|
||||||
import { MathUtils } from "three";
|
|
||||||
|
|
||||||
function createControls(camera, canvas) {
|
|
||||||
const controls = new OrbitControls(camera, canvas);
|
|
||||||
|
|
||||||
//enable controls?
|
|
||||||
controls.enabled = true;
|
|
||||||
controls.autoRotate = false;
|
|
||||||
//controls.autoRotateSpeed = 0.2;
|
|
||||||
|
|
||||||
// control limits
|
|
||||||
// It's recommended to set some control boundaries,
|
|
||||||
// to prevent the user from clipping with the objects.
|
|
||||||
|
|
||||||
// y axis
|
|
||||||
controls.minPolarAngle = MathUtils.degToRad(40); // default
|
|
||||||
controls.maxPolarAngle = MathUtils.degToRad(75);
|
|
||||||
|
|
||||||
// x axis
|
|
||||||
// controls.minAzimuthAngle = ...
|
|
||||||
// controls.maxAzimuthAngle = ...
|
|
||||||
|
|
||||||
//smooth camera:
|
|
||||||
// remember to add to loop updatables to work
|
|
||||||
controls.enableDamping = true;
|
|
||||||
|
|
||||||
//controls.enableZoom = false;
|
|
||||||
controls.maxDistance = 250;
|
|
||||||
|
|
||||||
//controls.enablePan = false;
|
|
||||||
|
|
||||||
controls.tick = () => controls.update();
|
|
||||||
|
|
||||||
return controls;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { createControls };
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { WebGLRenderer } from "three";
|
|
||||||
|
|
||||||
function createRenderer() {
|
|
||||||
const renderer = new WebGLRenderer({ antialias: true });
|
|
||||||
|
|
||||||
// turn on the physically correct lighting model
|
|
||||||
// (The browser complains: "THREE.WebGLRenderer: .physicallyCorrectLights has been removed. Set enderer.useLegacyLights instead." However, that changes the lighting in a way that doesn't look better.)
|
|
||||||
renderer.physicallyCorrectLights = true;
|
|
||||||
|
|
||||||
return renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { createRenderer };
|
|
||||||
@@ -121,14 +121,6 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
component: () =>
|
component: () =>
|
||||||
import(/* webpackChunkName: "start" */ "../views/StartView.vue"),
|
import(/* webpackChunkName: "start" */ "../views/StartView.vue"),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/statistics",
|
|
||||||
name: "statistics",
|
|
||||||
component: () =>
|
|
||||||
import(
|
|
||||||
/* webpackChunkName: "statistics" */ "../views/StatisticsView.vue"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
/** @type {*} */
|
/** @type {*} */
|
||||||
|
|||||||
@@ -206,7 +206,7 @@
|
|||||||
|
|
||||||
<div class="flex py-2">
|
<div class="flex py-2">
|
||||||
<button class="text-center text-md text-blue-500" @click="checkLimits()">
|
<button class="text-center text-md text-blue-500" @click="checkLimits()">
|
||||||
Check Limits
|
Check Registration and Claim Limits
|
||||||
</button>
|
</button>
|
||||||
<div v-if="!!limits?.nextWeekBeginDateTime" class="px-9">
|
<div v-if="!!limits?.nextWeekBeginDateTime" class="px-9">
|
||||||
<span class="font-bold">Rate Limits</span>
|
<span class="font-bold">Rate Limits</span>
|
||||||
@@ -233,17 +233,6 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<button class="text-blue-500 px-2">
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'statistics' }"
|
|
||||||
class="block text-center py-3 px-1"
|
|
||||||
>
|
|
||||||
See Your Statistics
|
|
||||||
</router-link>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-bind:class="computedAlertClassNames()">
|
<div v-bind:class="computedAlertClassNames()">
|
||||||
<button
|
<button
|
||||||
class="close-button bg-slate-200 w-8 leading-loose rounded-full absolute top-2 right-2"
|
class="close-button bg-slate-200 w-8 leading-loose rounded-full absolute top-2 right-2"
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
<fa icon="circle-check" class="text-green-600 fa-fw ml-1" />
|
<fa icon="circle-check" class="text-green-600 fa-fw ml-1" />
|
||||||
<span class="tooltiptext">Confirmed</span>
|
<span class="tooltiptext">Confirmed</span>
|
||||||
</span>
|
</span>
|
||||||
<button v-else class="tooltip" @click="confirm(record)">
|
<button v-else class="tooltip" @click="confirmGiven(record)">
|
||||||
<fa icon="circle" class="text-blue-600 fa-fw ml-1" />
|
<fa icon="circle" class="text-blue-600 fa-fw ml-1" />
|
||||||
<span class="tooltiptext">Unconfirmed</span>
|
<span class="tooltiptext">Unconfirmed</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -239,7 +239,11 @@ export default class ContactsView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async confirm(record: GiveServerRecord) {
|
async confirmGiven(record: GiveServerRecord) {
|
||||||
|
if (!confirm("Are you sure you want to mark this as confirmed?")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Make claim
|
// Make claim
|
||||||
// I use clone here because otherwise it gets a Proxy object.
|
// I use clone here because otherwise it gets a Proxy object.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
|||||||
@@ -49,18 +49,6 @@
|
|||||||
Your Contacts
|
Your Contacts
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div class="flex justify-between py-2">
|
|
||||||
<span />
|
|
||||||
<span>
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'help' }"
|
|
||||||
class="text-xs uppercase bg-blue-500 text-white px-1.5 py-1 rounded-md ml-1"
|
|
||||||
>
|
|
||||||
Help
|
|
||||||
</router-link>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- New Contact -->
|
<!-- New Contact -->
|
||||||
<div class="mb-4 flex">
|
<div class="mb-4 flex">
|
||||||
<input
|
<input
|
||||||
@@ -177,11 +165,12 @@
|
|||||||
: (givenByMeUnconfirmed[contact.did] || 0)
|
: (givenByMeUnconfirmed[contact.did] || 0)
|
||||||
/* eslint-enable prettier/prettier */
|
/* eslint-enable prettier/prettier */
|
||||||
}}
|
}}
|
||||||
<span
|
<span class="tooltiptext-left">
|
||||||
v-if="givenByMeDescriptions[contact.did]"
|
{{
|
||||||
class="tooltiptext-left"
|
givenByMeDescriptions[contact.did]
|
||||||
>
|
? "Most recently: " + givenByMeDescriptions[contact.did]
|
||||||
{{ givenByMeDescriptions[contact.did] }}
|
: "(None given yet.)"
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
|
class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
|
||||||
@@ -202,11 +191,12 @@
|
|||||||
: (givenToMeUnconfirmed[contact.did] || 0)
|
: (givenToMeUnconfirmed[contact.did] || 0)
|
||||||
/* eslint-enable prettier/prettier */
|
/* eslint-enable prettier/prettier */
|
||||||
}}
|
}}
|
||||||
<span
|
<span class="tooltiptext-left">
|
||||||
v-if="givenToMeDescriptions[contact.did]"
|
{{
|
||||||
class="tooltiptext-left"
|
givenToMeDescriptions[contact.did]
|
||||||
>
|
? "Most recently: " + givenToMeDescriptions[contact.did]
|
||||||
{{ givenToMeDescriptions[contact.did] }}
|
: "(None received yet.)"
|
||||||
|
}}
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
|
class="text-md uppercase bg-slate-500 text-white px-1.5 py-2 rounded-md mb-6"
|
||||||
@@ -342,8 +332,8 @@ export default class ContactsView extends Vue {
|
|||||||
const contactUnconfirmed: Record<string, number> = {};
|
const contactUnconfirmed: Record<string, number> = {};
|
||||||
const allData: Array<GiveServerRecord> = resp.data.data;
|
const allData: Array<GiveServerRecord> = resp.data.data;
|
||||||
for (const give of allData) {
|
for (const give of allData) {
|
||||||
if (give.unit == "HUR") {
|
const recipDid: string = give.recipientDid;
|
||||||
const recipDid: string = give.recipientDid;
|
if (recipDid && give.unit == "HUR") {
|
||||||
if (give.amountConfirmed) {
|
if (give.amountConfirmed) {
|
||||||
const prevAmount = contactConfirmed[recipDid] || 0;
|
const prevAmount = contactConfirmed[recipDid] || 0;
|
||||||
contactConfirmed[recipDid] = prevAmount + give.amount;
|
contactConfirmed[recipDid] = prevAmount + give.amount;
|
||||||
@@ -357,7 +347,6 @@ export default class ContactsView extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//console.log("Done retrieving gives", contactConfirmed);
|
|
||||||
this.givenByMeDescriptions = contactDescriptions;
|
this.givenByMeDescriptions = contactDescriptions;
|
||||||
this.givenByMeConfirmed = contactConfirmed;
|
this.givenByMeConfirmed = contactConfirmed;
|
||||||
this.givenByMeUnconfirmed = contactUnconfirmed;
|
this.givenByMeUnconfirmed = contactUnconfirmed;
|
||||||
|
|||||||
@@ -48,20 +48,20 @@
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- CONTENT -->
|
<!-- CONTENT -->
|
||||||
<section id="Content" class="p-6 pb-24">
|
<section id="Content" class="p-4 pb-24">
|
||||||
<!-- Heading -->
|
<!-- Heading -->
|
||||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
||||||
Help
|
Help
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-xl font-semibold">Introduction</h2>
|
<h2 class="text-xl font-semibold py-4">Introduction</h2>
|
||||||
<p>
|
<p>
|
||||||
This app is a window into data that you and your friends own, focused on
|
This app is a window into data that you and your friends own, focused on
|
||||||
gifts and collaboration.
|
gifts and collaboration.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">How do I backup all my data?</h2>
|
<h2 class="text-xl font-semibold py-4">How do I backup all my data?</h2>
|
||||||
<p>
|
<p>
|
||||||
There are two parts to backup your data: the identifier secrets and the
|
There are two parts to backup your data: the identifier secrets and the
|
||||||
other data such as settings, contacts, etc.
|
other data such as settings, contacts, etc.
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<ul class="list-disc list-inside">
|
<ul class="list-disc list-inside">
|
||||||
<li>
|
<li>
|
||||||
Go to Your Identity <fa icon="circle-user" class="fa-fw" /> page.
|
Go to your Identity <fa icon="circle-user" class="fa-fw" /> page.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Click on "Backup Identifier Seed" and follow the instructions.
|
Click on "Backup Identifier Seed" and follow the instructions.
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
<ul class="list-disc list-inside">
|
<ul class="list-disc list-inside">
|
||||||
<li>
|
<li>
|
||||||
Go to Your Identity <fa icon="circle-user" class="fa-fw" /> page.
|
Go to your Identity <fa icon="circle-user" class="fa-fw" /> page.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Click on "Download Settings...". That will save a file to your
|
Click on "Download Settings...". That will save a file to your
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">How do I restore my data?</h2>
|
<h2 class="text-xl font-semibold py-4">How do I restore my data?</h2>
|
||||||
<p>
|
<p>
|
||||||
There are two parts to restore your data: the identity secrets and the
|
There are two parts to restore your data: the identity secrets and the
|
||||||
other data such as settings, contacts, etc.
|
other data such as settings, contacts, etc.
|
||||||
@@ -135,30 +135,24 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">
|
<h2 class="text-xl font-semibold py-4">
|
||||||
|
How do I get registered to make claims?
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
Contact a current user. They will be able to register you on their
|
||||||
|
Contacts <fa icon="users" class="fa-fw" /> screen. Note that they have a
|
||||||
|
limited number of registrations.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 class="text-xl font-semibold py-4">
|
||||||
How do I add someone to my contacts?
|
How do I add someone to my contacts?
|
||||||
</h2>
|
</h2>
|
||||||
<p>
|
<p>
|
||||||
Tell them to copy their ID, which typically starts with "did:ethr:...",
|
Tell them to copy their ID, which typically starts with "did:ethr:...",
|
||||||
and send it to you. Go to the Contacts
|
and send it to you. Go to the Contacts
|
||||||
<fa icon="circle-user" class="fa-fw" /> page and enter that into the top
|
<fa icon="users" class="fa-fw" /> page and enter that into the top form.
|
||||||
form. You may add a name by adding a comma followed by their name; you
|
You may add a name by adding a comma followed by their name; you may
|
||||||
may also add their public key by adding another comma followed by the
|
also add their public key by adding another comma followed by the key.
|
||||||
key.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h2 class="text-xl font-semibold">
|
|
||||||
I know there is a record from someone, so why can't I see that info?
|
|
||||||
</h2>
|
|
||||||
<p>
|
|
||||||
Sometimes someone else has made claims -- meaning they have given time,
|
|
||||||
or recognized time given, or declared a project -- but you cannot see
|
|
||||||
anything associated with them. The reason may be because they have not
|
|
||||||
given you permission to see their information. Ask them to add you to
|
|
||||||
their contact list and make sure the eye next to your name is open like
|
|
||||||
this
|
|
||||||
<fa icon="eye" class="fa-fw" /> and not closed like this
|
|
||||||
<fa icon="eye-slash" class="fa-fw" />.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1,212 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- QUICK NAV -->
|
|
||||||
<nav id="QuickNav" class="fixed bottom-0 left-0 right-0 bg-slate-200 z-50">
|
|
||||||
<ul class="flex text-2xl p-2 gap-2">
|
|
||||||
<!-- Home Feed -->
|
|
||||||
<li class="basis-1/5 rounded-md text-slate-500">
|
|
||||||
<router-link :to="{ name: 'home' }" class="block text-center py-3 px-1">
|
|
||||||
<fa icon="house-chimney" class="fa-fw"></fa>
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<!-- Search -->
|
|
||||||
<li class="basis-1/5 rounded-md text-slate-500">
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'discover' }"
|
|
||||||
class="block text-center py-3 px-1"
|
|
||||||
>
|
|
||||||
<fa icon="magnifying-glass" class="fa-fw"></fa>
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<!-- Projects -->
|
|
||||||
<li class="basis-1/5 rounded-md text-slate-500">
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'projects' }"
|
|
||||||
class="block text-center py-3 px-1"
|
|
||||||
>
|
|
||||||
<fa icon="folder-open" class="fa-fw"></fa>
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<!-- Contacts -->
|
|
||||||
<li class="basis-1/5 rounded-md text-slate-500">
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'contacts' }"
|
|
||||||
class="block text-center py-3 px-1"
|
|
||||||
>
|
|
||||||
<fa icon="users" class="fa-fw"></fa>
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
<!-- Profile -->
|
|
||||||
<li class="basis-1/5 rounded-md bg-slate-400 text-white">
|
|
||||||
<router-link
|
|
||||||
:to="{ name: 'account' }"
|
|
||||||
class="block text-center py-3 px-1"
|
|
||||||
>
|
|
||||||
<fa icon="circle-user" class="fa-fw"></fa>
|
|
||||||
</router-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<!-- CONTENT -->
|
|
||||||
<section id="Content" class="p-6 pb-24">
|
|
||||||
<!-- Heading -->
|
|
||||||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
|
||||||
Your Statistics
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<button class="float-right" @click="captureGraphics()">Screenshot</button>
|
|
||||||
|
|
||||||
<!-- Another place to play with the sizing is in Resizer.setSize -->
|
|
||||||
<div id="scene-container" class="h-screen"></div>
|
|
||||||
|
|
||||||
<div v-bind:class="computedAlertClassNames()">
|
|
||||||
<button
|
|
||||||
class="close-button bg-slate-200 w-8 leading-loose rounded-full absolute top-2 right-2"
|
|
||||||
@click="onClickClose()"
|
|
||||||
>
|
|
||||||
<fa icon="xmark"></fa>
|
|
||||||
</button>
|
|
||||||
<h4 class="font-bold pr-5">{{ alertTitle }}</h4>
|
|
||||||
<p>{{ alertMessage }}</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { SVGRenderer } from "three/addons/renderers/SVGRenderer.js";
|
|
||||||
import { Component, Vue } from "vue-facing-decorator";
|
|
||||||
import { World } from "@/components/World/World.js";
|
|
||||||
|
|
||||||
@Component
|
|
||||||
export default class StatisticsView extends Vue {
|
|
||||||
world: World;
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
const container = document.querySelector("#scene-container");
|
|
||||||
const newWorld = new World(container, this);
|
|
||||||
newWorld.start();
|
|
||||||
this.world = newWorld;
|
|
||||||
}
|
|
||||||
|
|
||||||
public captureGraphics() {
|
|
||||||
/**
|
|
||||||
// from https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#examples
|
|
||||||
// Adds a blank image
|
|
||||||
const dataBlob = document
|
|
||||||
.querySelector("#scene-container")
|
|
||||||
.firstChild.toBlob((blob) => {
|
|
||||||
const newImg = document.createElement("img");
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
newImg.onload = () => {
|
|
||||||
// no longer need to read the blob so it's revoked
|
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
newImg.src = url;
|
|
||||||
document.body.appendChild(newImg);
|
|
||||||
});
|
|
||||||
**/
|
|
||||||
|
|
||||||
/**
|
|
||||||
// Yields a blank page with the iframe below
|
|
||||||
const dataUrl = document
|
|
||||||
.querySelector("#scene-container")
|
|
||||||
.firstChild.toDataURL("image/png");
|
|
||||||
**/
|
|
||||||
|
|
||||||
/**
|
|
||||||
// Yields a blank page with the iframe below
|
|
||||||
const dataUrl = this.world.renderer.domElement.toDataURL("image/png");
|
|
||||||
**/
|
|
||||||
|
|
||||||
/**
|
|
||||||
// Show the image in a new tab
|
|
||||||
const iframe = `
|
|
||||||
<iframe
|
|
||||||
src="${dataUrl}"
|
|
||||||
frameborder="0"
|
|
||||||
style="border:0; top:0px; left:0px; bottom:0px; right:0px; width:100%; height:100%;"
|
|
||||||
allowfullscreen>
|
|
||||||
</iframe>`;
|
|
||||||
const win = window.open();
|
|
||||||
win.document.open();
|
|
||||||
win.document.write(iframe);
|
|
||||||
win.document.close();
|
|
||||||
**/
|
|
||||||
|
|
||||||
// from https://stackoverflow.com/a/17407392/845494
|
|
||||||
// This yields a file with funny formatting.
|
|
||||||
//const image = const dataUrl.replace("image/png", "image/octet-stream");
|
|
||||||
|
|
||||||
/**
|
|
||||||
// Yields a blank image at the bottom of the page
|
|
||||||
// from https://discourse.threejs.org/t/save-screenshot-on-server/39900/3
|
|
||||||
const img = new Image();
|
|
||||||
img.src = this.world.renderer.domElement.toDataURL();
|
|
||||||
document.body.appendChild(img);
|
|
||||||
**/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This yields an SVG that only shows white and black highlights
|
|
||||||
// from https://stackoverflow.com/questions/27632621/exporting-from-three-js-scene-to-svg-or-other-vector-format
|
|
||||||
**/
|
|
||||||
const rendererSVG = new SVGRenderer();
|
|
||||||
rendererSVG.setSize(window.innerWidth, window.innerHeight);
|
|
||||||
rendererSVG.render(this.world.scene, this.world.camera);
|
|
||||||
//document.body.appendChild(rendererSVG.domElement);
|
|
||||||
ExportToSVG(rendererSVG, "test.svg");
|
|
||||||
}
|
|
||||||
|
|
||||||
alertTitle = "";
|
|
||||||
alertMessage = "";
|
|
||||||
isAlertVisible = false;
|
|
||||||
|
|
||||||
public setAlert(title, message) {
|
|
||||||
this.alertTitle = title;
|
|
||||||
this.alertMessage = message;
|
|
||||||
this.isAlertVisible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public onClickClose() {
|
|
||||||
this.isAlertVisible = false;
|
|
||||||
this.alertTitle = "";
|
|
||||||
this.alertMessage = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public computedAlertClassNames() {
|
|
||||||
return {
|
|
||||||
hidden: !this.isAlertVisible,
|
|
||||||
"dismissable-alert": true,
|
|
||||||
"bg-slate-100": true,
|
|
||||||
"p-5": true,
|
|
||||||
rounded: true,
|
|
||||||
"drop-shadow-lg": true,
|
|
||||||
fixed: true,
|
|
||||||
"top-3": true,
|
|
||||||
"inset-x-3": true,
|
|
||||||
"transition-transform": true,
|
|
||||||
"ease-in": true,
|
|
||||||
"duration-300": true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function ExportToSVG(rendererSVG, filename) {
|
|
||||||
const XMLS = new XMLSerializer();
|
|
||||||
const svgfile = XMLS.serializeToString(rendererSVG.domElement);
|
|
||||||
const svgData = svgfile;
|
|
||||||
const preface = '<?xml version="1.0" standalone="no"?>\r\n';
|
|
||||||
const svgBlob = new Blob([preface, svgData], {
|
|
||||||
type: "image/svg+xml;charset=utf-8",
|
|
||||||
});
|
|
||||||
const svgUrl = URL.createObjectURL(svgBlob);
|
|
||||||
const downloadLink = document.createElement("a");
|
|
||||||
|
|
||||||
downloadLink.href = svgUrl;
|
|
||||||
downloadLink.download = filename;
|
|
||||||
document.body.appendChild(downloadLink);
|
|
||||||
downloadLink.click();
|
|
||||||
document.body.removeChild(downloadLink);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -7,9 +7,4 @@ module.exports = defineConfig({
|
|||||||
topLevelAwait: true,
|
topLevelAwait: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pwa: {
|
|
||||||
iconPaths: {
|
|
||||||
faviconSVG: 'img/icons/safari-pinned-tab.svg',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user