diff --git a/package-lock.json b/package-lock.json index bd039a2..576fe8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,9 @@ "version": "0.0.1", "dependencies": { "@ardrive/turbo-sdk": "^1.21.0", - "@fortawesome/free-solid-svg-icons": "^6.7.2", - "@fortawesome/react-fontawesome": "^0.2.2", + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@fortawesome/react-fontawesome": "^0.2.3", "@permaweb/aoconnect": "^0.0.77", "ao-js-sdk": "^0.0.15", "arweave": "^1.15.5", @@ -761,6 +762,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -777,6 +779,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -793,6 +796,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -809,6 +813,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -825,6 +830,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -841,6 +847,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -857,6 +864,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -873,6 +881,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -889,6 +898,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -905,6 +915,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -921,6 +932,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -937,6 +949,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -953,6 +966,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -969,6 +983,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -985,6 +1000,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1001,6 +1017,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1017,6 +1034,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1033,6 +1051,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1049,6 +1068,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1065,6 +1085,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1081,6 +1102,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1097,6 +1119,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1113,6 +1136,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1151,85 +1175,6 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/@ethersproject/abstract-provider": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", @@ -1862,115 +1807,51 @@ } }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", - "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.0.0.tgz", + "integrity": "sha512-PGMrIYXLGA5K8RWy8zwBkd4vFi4z7ubxtet6Yn13Plf6krRTwPbdlCwlcfmoX0R7B4Z643QvrtHmdQ5fNtfFCg==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", - "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", - "peer": true, + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.0.0.tgz", + "integrity": "sha512-obBEF+zd98r/KtKVW6A+8UGWeaOoyMpl6Q9P3FzHsOnsg742aXsl8v+H/zp09qSSu/a/Hxe9LNKzbBaQq1CEbA==", + "license": "MIT", "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" + "@fortawesome/fontawesome-common-types": "7.0.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", - "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.0.0.tgz", + "integrity": "sha512-njSLAllkOddYDCXgTFboXn54Oe5FcvpkWq+FoetOHR64PbN0608kM02Lze0xtISGpXgP+i26VyXRQA0Irh3Obw==", + "license": "(CC-BY-4.0 AND MIT)", "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" + "@fortawesome/fontawesome-common-types": "7.0.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/react-fontawesome": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", - "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.3.tgz", + "integrity": "sha512-HlJco8RDY8NrzFVjy23b/7mNS4g9NegcrBG3n7jinwpc2x/AmSVk53IhWniLYM4szYLxRAFTAGwGn0EIlclDeQ==", + "license": "MIT", "dependencies": { "prop-types": "^15.8.1" }, "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.3" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" + "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", + "react": "^16.3 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause", - "peer": true - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2078,7 +1959,7 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -2093,7 +1974,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2103,7 +1984,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -2113,7 +1994,7 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -2130,7 +2011,7 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -2618,6 +2499,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2631,6 +2513,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2644,6 +2527,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2657,6 +2541,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2670,6 +2555,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2683,6 +2569,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2696,6 +2583,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2709,6 +2597,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2722,6 +2611,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2735,6 +2625,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2748,6 +2639,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2761,6 +2653,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2774,6 +2667,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2787,6 +2681,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2800,6 +2695,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2813,6 +2709,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2826,6 +2723,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2839,6 +2737,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2852,6 +2751,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3329,14 +3229,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC", - "peer": true - }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -3376,7 +3268,7 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -3385,17 +3277,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peer": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/aes-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", @@ -3414,24 +3295,6 @@ "node": ">= 8.0.0" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/algo-msgpack-with-bigint": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz", @@ -3580,14 +3443,6 @@ "dev": true, "license": "MIT" }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0", - "peer": true - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -4142,7 +3997,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/buffer-xor": { @@ -4218,17 +4073,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=6" - } - }, "node_modules/camel-case": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", @@ -4722,14 +4566,6 @@ } } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4839,20 +4675,6 @@ "dev": true, "license": "MIT" }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -5109,6 +4931,7 @@ "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -5152,96 +4975,6 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", @@ -5255,118 +4988,17 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "license": "BSD-2-Clause", - "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">=4.0" + "node": ">=4" } }, "node_modules/estree-walker": { @@ -5375,17 +5007,6 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ethers": { "version": "6.13.5", "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.5.tgz", @@ -5520,14 +5141,6 @@ "node": "> 0.1.90" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -5558,22 +5171,6 @@ "node": ">= 6" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/fast-stable-stringify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz", @@ -5606,20 +5203,6 @@ "tough-cookie": "^4.0.0" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, "node_modules/file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -5678,30 +5261,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true, - "license": "ISC", - "peer": true - }, "node_modules/fn.name": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", @@ -5804,18 +5363,11 @@ "node": ">=12" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC", - "peer": true - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5891,29 +5443,6 @@ "node": ">= 0.4" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5927,32 +5456,6 @@ "node": ">=10.13.0" } }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -6244,48 +5747,6 @@ "node": ">= 4" } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6430,17 +5891,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -6705,20 +6155,6 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "license": "MIT" }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -6742,30 +6178,6 @@ "bignumber.js": "^9.0.0" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT", - "peer": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -6851,17 +6263,6 @@ "node": ">= 6" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -6877,21 +6278,6 @@ "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", "license": "MIT" }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/libsodium-sumo": { "version": "0.7.15", "resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.7.15.tgz", @@ -6942,14 +6328,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/logform": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", @@ -7215,6 +6593,7 @@ "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "dev": true, "funding": [ { "type": "github", @@ -7503,8 +6882,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, "license": "ISC", + "optional": true, "dependencies": { "wrappy": "1" } @@ -7518,25 +6897,6 @@ "fn.name": "1.x.x" } }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/os-browserify": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", @@ -7610,20 +6970,6 @@ "tslib": "^2.0.3" } }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/parse-asn1": { "version": "5.1.7", "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.7.tgz", @@ -7684,17 +7030,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -7824,6 +7159,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -7896,6 +7232,7 @@ "version": "8.5.2", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -8041,17 +7378,6 @@ "dev": true, "license": "MIT" }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -8464,17 +7790,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -8495,24 +7810,6 @@ "node": ">=0.10.0" } }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", @@ -8527,6 +7824,7 @@ "version": "4.34.8", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -8927,7 +8225,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -8937,6 +8235,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -8946,7 +8245,7 @@ "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -9136,20 +8435,6 @@ "node": ">=8" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/structured-headers": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/structured-headers/-/structured-headers-2.0.0.tgz", @@ -9315,7 +8600,7 @@ "version": "5.39.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", - "devOptional": true, + "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", @@ -9334,7 +8619,7 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/text-encoding-utf-8": { @@ -9348,14 +8633,6 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "license": "MIT" }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT", - "peer": true - }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -9542,34 +8819,6 @@ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", "license": "Unlicense" }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typeforce": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", @@ -9648,17 +8897,6 @@ "browserslist": ">= 4.21.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/url": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/url/-/url-0.11.4.tgz", @@ -9743,6 +8981,7 @@ "version": "5.4.14", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", @@ -10016,17 +9255,6 @@ "node": ">= 6" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -10067,8 +9295,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true, - "license": "ISC" + "license": "ISC", + "optional": true }, "node_modules/ws": { "version": "7.4.6", diff --git a/package.json b/package.json index 2b7765c..8cccaea 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,9 @@ }, "dependencies": { "@ardrive/turbo-sdk": "^1.21.0", - "@fortawesome/free-solid-svg-icons": "^6.7.2", - "@fortawesome/react-fontawesome": "^0.2.2", + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@fortawesome/react-fontawesome": "^0.2.3", "@permaweb/aoconnect": "^0.0.77", "ao-js-sdk": "^0.0.15", "arweave": "^1.15.5", @@ -46,4 +47,4 @@ "vite": "^5.0.8", "vite-plugin-html": "^3.2.2" } -} \ No newline at end of file +} diff --git a/src/components/ActivityCard.tsx b/src/components/ActivityCard.tsx index 813a333..7be6aaa 100644 --- a/src/components/ActivityCard.tsx +++ b/src/components/ActivityCard.tsx @@ -26,16 +26,15 @@ interface ActivityCardProps { buttonText: string; theme: any; highlightSelectable?: boolean; - remainingTime?: string; - progress?: number; + className?: string; } export const ActivityCard: React.FC = ({ title, badge, badgeColor, - gradientFrom, // Kept for button, but not for top bar - gradientTo, // Kept for button, but not for top bar + gradientFrom, + gradientTo, tokenLogo, tokenBalance, tokenRequired, @@ -45,30 +44,43 @@ export const ActivityCard: React.FC = ({ isLoading, isDisabled, buttonText, - theme, highlightSelectable = false, - remainingTime, - progress }) => { + + // Helper to get reward icons + const getRewardIcon = (cost: any) => { + if (cost.icon === '⚡') return '🔋'; + if (cost.icon === '💝') return '❤️'; + if (cost.icon === '🧭') return '🧭'; + if (cost.icon === '🗡️') return '🗡️'; + return cost.icon; + }; + // Enhanced border effect for selectable items const borderStyle = !isDisabled && highlightSelectable - ? `border-2 border-${gradientFrom}` - : `border-2 ${theme.border}`; + ? `border border-[#814E3355]` + : 'border border-[#e7dfd2]'; // Enhanced glow effect for selectable items const glowEffect = !isDisabled && highlightSelectable ? `shadow-lg shadow-${gradientFrom}/20` : ''; - + // Enhanced scale effect for selectable items const hoverEffect = !isDisabled - ? 'hover:scale-105' - : ''; + ? 'hover:scale-[1.02]' + : 'hover:shadow-none hover:scale-100'; - // Enhanced button styling for better visual feedback - const buttonStyle = !isDisabled - ? `bg-gradient-to-r from-${gradientFrom} to-${gradientTo} hover:from-${gradientFrom}-700 hover:to-${gradientTo}-700 transform hover:scale-105 hover:shadow-md` - : 'bg-gray-400 cursor-not-allowed'; + // Button uses solid color based on badgeColor if active/enabled, else gray + const buttonBgMap: Record = { + yellow: 'bg-[#facc15] hover:bg-[#eab308] text-white', + green: 'bg-[#22c55e] hover:bg-[#16a34a] text-white', + red: 'bg-[#ef4444] hover:bg-[#dc2626] text-white', + blue: 'bg-[#3b82f6] hover:bg-[#2563eb] text-white' + }; + const buttonStyle = !isDisabled + ? `${buttonBgMap[badgeColor] || 'bg-blue-500 hover:bg-blue-600 text-white'} font-medium shadow-lg hover:shadow-xl` + : 'bg-gray-300 cursor-not-allowed text-white hover:scale-100 hover:shadow-none'; // Add a highlight indicator for selectable items const SelectableIndicator = () => { @@ -80,118 +92,89 @@ export const ActivityCard: React.FC = ({ return null; }; + const badgeTextMap: Record = { + yellow: 'text-[#78350f]', + green: 'text-[#166534]', + red: 'text-[#7f1d1d]', + blue: 'text-[#1e3a8a]' + }; + return (
- - {/* --- NEW Integrated Progress/Header Bar --- */} - {(() => { - const isActivityCompleted = progress === 100; - const isActivityInProgress = progress !== undefined && progress < 100; - const baseBgColor = theme.isDarkMode ? 'bg-[#2A1912]' : 'bg-[#F4E4C1]'; // Theme-aligned dark and light backgrounds - - return ( -
- {isActivityCompleted ? ( -
- COMPLETE -
- ) : isActivityInProgress ? ( -
-
- {remainingTime && {remainingTime}} -
- ) : ( -
- )} -
- ); - })()} - - {/* Selectable indicator (ensure it's visible on top of the new header) */} + className={`activity-card relative overflow-hidden bg-[#fcf8f3] ${borderStyle} rounded-lg hover:shadow-xl transition-all duration-300 ${hoverEffect} ${glowEffect}`}> - - {/* Main content - Adjusted pt-3 to account for h-2 header */} -
- {/* Header (Title and Badge) */} -
{/* Reduced mb from mb-2 */} -

{title}

- +
+ {/* Header */} +
+
+

{title}

+
+
{badge} - +
- {/* Token info and requirements */} -
- {/* Token display */} -
- {tokenLogo && ( - +
+ {tokenLogo ? ( + Token + ) : ( + 🪙 )} - = tokenRequired ? 'text-green-500' : 'text-red-500'}`}> - {tokenBalance}/{tokenRequired} - -
- - {/* Costs and Rewards side by side */} -
- {/* Costs section */} -
- {costs.map((cost, index) => { - // Extract just the number from the text - const number = cost.text.match(/-?\d+/)?.[0] || ''; - // Replace energy emoji with battery if present - const icon = cost.icon === '⚡' ? '🔋' : cost.icon; - return ( -
- {icon} - {number} -
- ); - })} -
- - {/* Rewards section */} -
- {rewards.map((reward, index) => { - // Extract just the number from the text - const number = reward.text.match(/\d+/)?.[0] || ''; - // Replace energy emoji with battery if present - const icon = reward.icon === '⚡' ? '🔋' : reward.icon; - return ( -
- {icon} - +{number} -
- ); - })} -
+ {tokenBalance}/{tokenRequired} + {/* Cost display */} + {costs.map((cost, index) => { + const number = cost.text.match(/-?\d+/)?.[0] || ''; + const icon = getRewardIcon(cost); + return ( +
+ {icon} + + {number} + +
+ ); + })} + {/* Reward display */} + {rewards.map((reward, index) => { + const number = reward.text.match(/\d+/)?.[0] || ''; + const icon = getRewardIcon(reward); + return ( +
+ {icon} + +{number} +
+ ); + })}
- {/* Action button - always at bottom */} -
- -
+ {/* Action Button */} +
); diff --git a/src/components/Inventory.tsx b/src/components/Inventory.tsx index ad4f8cf..722c65c 100644 --- a/src/components/Inventory.tsx +++ b/src/components/Inventory.tsx @@ -171,7 +171,7 @@ const Inventory = () => { const hasWallet = !!wallet?.address; return ( -
+
toggleSection('main')}> 👜 diff --git a/src/components/LootBoxUtil.tsx b/src/components/LootBoxUtil.tsx index f5873d5..8943b78 100644 --- a/src/components/LootBoxUtil.tsx +++ b/src/components/LootBoxUtil.tsx @@ -14,6 +14,7 @@ interface LootBoxProps { externalLootBoxes?: LootBox[]; // Flag to control whether this component should load data independently loadDataIndependently?: boolean; + loading?: boolean; } // Type to represent a loot box with rarity/level @@ -22,10 +23,11 @@ interface LootBox { displayName: string; } -const LootBoxUtil = ({ - className = '', - externalLootBoxes, - loadDataIndependently = true +const LootBoxUtil = ({ + className = '', + externalLootBoxes, + loadDataIndependently = true, + loading = false }: LootBoxProps): JSX.Element => { const { wallet, darkMode, triggerRefresh, refreshTrigger } = useWallet(); const { tokenBalances, retryToken } = useTokens(); @@ -34,26 +36,26 @@ const LootBoxUtil = ({ const [isOpening, setIsOpening] = useState(false); const [openResult, setOpenResult] = useState(null); const [showConfetti, setShowConfetti] = useState(false); - const [assets, setAssets] = useState<{[key: string]: {name: string, ticker: string, logo?: string}}>({}); + const [assets, setAssets] = useState<{ [key: string]: { name: string, ticker: string, logo?: string } }>({}); const [isShaking, setIsShaking] = useState(false); const [isExploding, setIsExploding] = useState(false); const [selectedRarity, setSelectedRarity] = useState(null); const [isFadingOut, setIsFadingOut] = useState(false); const [isCollecting, setIsCollecting] = useState(false); const [showCloseButton, setShowCloseButton] = useState(false); - const [collectingItems, setCollectingItems] = useState<{content: React.ReactNode, x: number, y: number}[]>([]); + const [collectingItems, setCollectingItems] = useState<{ content: React.ReactNode, x: number, y: number }[]>([]); const [timerProgress, setTimerProgress] = useState(100); const timerRef = useRef(null); - + const theme = currentTheme(darkMode); - + /** * Maps a numerical rarity level to its human-readable display name * @param rarity - Numerical value representing the rarity level (1-5) * @returns String representation of the rarity */ const getRarityName = (rarity: number): string => { - switch(rarity) { + switch (rarity) { case 1: return 'Common'; case 2: return 'Uncommon'; case 3: return 'Rare'; @@ -62,19 +64,15 @@ const LootBoxUtil = ({ default: return `Level ${rarity}`; } }; - + /** * Processes raw lootbox data from API response into structured LootBox objects */ const processLootBoxData = (responseResult: any): LootBox[] => { const boxes: LootBox[] = []; - - // Check if response.result is an array of arrays if (Array.isArray(responseResult) && responseResult.length > 0) { - // Process each loot box entry responseResult.forEach((box: any) => { if (Array.isArray(box)) { - // Each entry in the array is a separate loot box box.forEach((rarityLevel: number) => { boxes.push({ rarity: rarityLevel, @@ -82,7 +80,6 @@ const LootBoxUtil = ({ }); }); } else if (typeof box === 'number') { - // Single number represents rarity directly boxes.push({ rarity: box, displayName: getRarityName(box) @@ -90,7 +87,6 @@ const LootBoxUtil = ({ } }); } - return boxes; }; @@ -98,43 +94,29 @@ const LootBoxUtil = ({ useEffect(() => { if (externalLootBoxes) { setLootBoxes(externalLootBoxes); - setIsLoading(false); // Ensure loading state is turned off + setIsLoading(false); } }, [externalLootBoxes]); - + // Load loot boxes when wallet or refresh trigger changes, but only if loadDataIndependently is true useEffect(() => { - // Skip loading if component is configured to not load independently or if external data is provided if (!loadDataIndependently || externalLootBoxes) return; - const loadLootBoxes = async () => { if (!wallet?.address) return; - setIsLoading(true); try { - console.log('[LootBoxUtil] Loading lootbox data independently'); const response = await getLootBoxes(wallet.address); - if (response?.result) { const boxes = processLootBoxData(response.result); - console.log('[LootBoxUtil] Processed loot boxes:', boxes); - - // If we're not currently opening a box, update the boxes state if (!isOpening) { setLootBoxes(boxes); - } else { - console.log('[LootBoxUtil] Not updating loot boxes during opening animation'); } } else { - console.warn('[LootBoxUtil] No loot box data in response'); - // Only set empty boxes if we're not in the middle of opening a box if (!isOpening) { setLootBoxes([]); } } } catch (error) { - console.error('[LootBoxUtil] Error loading loot boxes:', error); - // Only update if not currently opening if (!isOpening) { setLootBoxes([]); } @@ -142,17 +124,13 @@ const LootBoxUtil = ({ setIsLoading(false); } }; - loadLootBoxes(); }, [wallet?.address, refreshTrigger, loadDataIndependently, externalLootBoxes, isOpening]); - + // Map assets for token name mapping useEffect(() => { if (!wallet?.address || !tokenBalances) return; - - const assetMap: {[key: string]: {name: string, ticker: string, logo?: string}} = {}; - - // Convert tokenBalances object to asset map + const assetMap: { [key: string]: { name: string, ticker: string, logo?: string } } = {}; Object.keys(tokenBalances).forEach((processId) => { const asset = tokenBalances[processId as SupportedAssetId]; if (asset) { @@ -163,10 +141,9 @@ const LootBoxUtil = ({ }; } }); - setAssets(assetMap); }, [wallet?.address, tokenBalances]); - + /** * Returns CSS classes for styling loot box based on its rarity * @param rarity - Numerical rarity level @@ -174,21 +151,15 @@ const LootBoxUtil = ({ */ const getRarityColorClass = (rarity: number): string => { switch (rarity) { - case 1: // Common - return 'bg-gray-700 text-gray-100 border-gray-500'; - case 2: // Uncommon - return 'bg-green-700 text-green-100 border-green-500'; - case 3: // Rare - return 'bg-blue-700 text-blue-100 border-blue-500'; - case 4: // Epic - return 'bg-purple-700 text-purple-100 border-purple-500'; - case 5: // Legendary - return 'bg-yellow-700 text-yellow-100 border-yellow-500'; - default: - return 'bg-gray-700 text-gray-100 border-gray-500'; + case 1: return 'bg-gray-50 text-gray-700 border-gray-300'; + case 2: return 'bg-green-50 text-green-700 border-green-300'; + case 3: return 'bg-blue-50 text-blue-700 border-blue-300'; + case 4: return 'bg-purple-50 text-purple-700 border-purple-300'; + case 5: return 'bg-yellow-50 text-yellow-700 border-yellow-300'; + default: return 'bg-gray-50 text-gray-700 border-gray-300'; } }; - + /** * Returns CSS classes for glow/shadow effects based on rarity * @param rarity - Numerical rarity level @@ -196,56 +167,41 @@ const LootBoxUtil = ({ */ const getRarityGlowClass = (rarity: number): string => { switch (rarity) { - case 1: // Common - return ''; - case 2: // Uncommon - return 'shadow-sm shadow-green-400'; - case 3: // Rare - return 'shadow-md shadow-blue-400'; - case 4: // Epic - return 'shadow-lg shadow-purple-400 animate-pulse'; - case 5: // Legendary - return 'shadow-xl shadow-yellow-400 animate-pulse'; - default: - return ''; + case 1: return ''; + case 2: return 'drop-shadow-[0_0_4px_rgba(34,197,94,0.5)]'; + case 3: return 'drop-shadow-[0_0_4px_rgba(59,130,246,0.5)]'; + case 4: return 'drop-shadow-[0_0_6px_rgba(168,85,247,0.5)]'; + case 5: return 'drop-shadow-[0_0_8px_rgba(253,224,71,0.7)]'; + default: return ''; } }; - - /** * Starts countdown timer for auto-closing rewards display * Creates a 5-second countdown with visual progress bar */ const startTimer = () => { - // Clear any existing timer if (timerRef.current) { clearInterval(timerRef.current); } - - // Reset timer to 100% setTimerProgress(100); - - // Decrease by 2% every 100ms (5 seconds total = 50 steps * 100ms) timerRef.current = setInterval(() => { setTimerProgress(prev => { const newProgress = Math.max(0, prev - 2); - - // When timer reaches zero, close the rewards - if (newProgress === 0) { - handleCloseWinnings(); + if (newProgress <= 2) { + if (!isCollecting) { + handleCloseWinnings(true); + } if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } } - return newProgress; }); }, 100); }; - - // Clean up timer on unmount + useEffect(() => { return () => { if (timerRef.current) { @@ -253,83 +209,58 @@ const LootBoxUtil = ({ } }; }, []); - - // Handle closing the winnings display with animation - const handleCloseWinnings = () => { - // Don't allow closing multiple times + + /** + * Handle closing the winnings display with animation and refresh + * @param autoClose - true if called by timer, false if by user + */ + const handleCloseWinnings = (autoClose = false) => { if (isCollecting) return; - - // Clear timer if exists if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; } - - // Get all reward items from the current result if (openResult?.result && Array.isArray(openResult.result)) { - // Find all berry elements in the DOM const berryElements = document.querySelectorAll('.berry-emoji'); - const collectionItems: {content: React.ReactNode, x: number, y: number}[] = []; - - // Extract position and content for each reward + const collectionItems: { content: React.ReactNode, x: number, y: number }[] = []; berryElements.forEach((element, index) => { const rect = element.getBoundingClientRect(); - - // Calculate center position of the element const x = rect.left + rect.width / 2; const y = rect.top + rect.height / 2; - - // Get the token ID from the reward result const tokenId = openResult.result[index]?.token; let content: React.ReactNode; - - // If we have token ID and it exists in ASSET_INFO, use its logo if (tokenId && ASSET_INFO[tokenId] && ASSET_INFO[tokenId].logo) { content = ( - {ASSET_INFO[tokenId].name ); } else { - // Fallback to emoji if no logo available const emoji = element.textContent || '🌟'; content = emoji; } - - // Add this item to the collection collectionItems.push({ content, x, y }); }); - - // Update state with collection items setCollectingItems(collectionItems); } - - // Start collection animation setIsCollecting(true); setShowCloseButton(false); - - // Hide confetti setShowConfetti(false); - - // After collection animation completes (1 second), reset the UI and refresh setTimeout(() => { - console.log('[LootBoxUtil] Resetting UI and refreshing data'); setSelectedRarity(null); setIsExploding(false); setIsFadingOut(false); setIsCollecting(false); - setIsOpening(false); // Fix: Ensure isOpening is reset + setIsOpening(false); setOpenResult(null); - setCollectingItems([]); // Clear collecting items - setTimerProgress(100); // Reset timer progress - - // Trigger a refresh to update the loot boxes + setCollectingItems([]); + setTimerProgress(100); if (triggerRefresh) { triggerRefresh(); } - }, 1000); + }, autoClose ? 1200 : 1000); }; /** @@ -337,143 +268,84 @@ const LootBoxUtil = ({ * @param rarity - Rarity level of the loot box to open */ const handleOpenLootBox = async (rarity: number) => { - // Check if we can open a box - added extra check for lootBoxes if (!wallet?.address || isOpening || lootBoxes.filter(box => box.rarity === rarity).length === 0) return; - - // Reset states to ensure clean start setIsOpening(true); setOpenResult(null); setSelectedRarity(rarity); - setTimerProgress(100); // Reset timer to full - + setTimerProgress(100); try { - // Start shaking animation with increasing intensity setIsShaking(true); - - // Get the results from server, passing the rarity parameter - console.log('[LootBoxUtil] Sending request to open loot box with rarity:', rarity); - - // Custom refresh callback to only refresh the tokens we received const refreshReceivedTokens = (result: LootBoxResponse) => { - // Only refresh tokens that were received from the loot box if (result?.result && Array.isArray(result.result)) { const receivedTokens: SupportedAssetId[] = []; - - // Extract token IDs from the result result.result.forEach((item: any) => { if (item && typeof item === 'object' && item.token) { - // Check if this token is a supported asset ID const tokenId = item.token as SupportedAssetId; if (SUPPORTED_ASSET_IDS.includes(tokenId)) { receivedTokens.push(tokenId); } } }); - - // If we have tokens to refresh, do it individually instead of refreshing all if (receivedTokens.length > 0) { - console.log('[LootBoxUtil] Refreshing only received tokens:', receivedTokens); receivedTokens.forEach(tokenId => { retryToken(tokenId); }); - } else { - console.log('[LootBoxUtil] No valid tokens to refresh'); } - } else { - console.warn('[LootBoxUtil] Invalid result format for token refresh'); } }; - - // Pass rarity but do NOT pass global triggerRefresh as we want to use our custom refresh logic const result = await openLootBoxWithRarity(wallet, rarity); - - console.log('[LootBoxUtil] Received loot box result:', result); - - if (result && result.result) { - console.log('[LootBoxUtil] Loot received:', JSON.stringify(result.result)); - // Apply our custom token refresh for only the received tokens - refreshReceivedTokens(result); - } else { - console.warn('[LootBoxUtil] No loot data in result'); - } - - // Once we have the result from chain, stop shaking and start the explosion setIsShaking(false); setIsExploding(true); - - // Small pause before confetti await new Promise(resolve => setTimeout(resolve, 150)); - - // Show confetti with the result setShowConfetti(true); - if (result) { - // Store the structured result setOpenResult(result); - - // Manually update local state to reduce the loot box count setLootBoxes(prevBoxes => { const updatedBoxes = [...prevBoxes]; - // Find the box with matching rarity and remove one instance const boxIndex = updatedBoxes.findIndex(box => box.rarity === rarity); if (boxIndex >= 0) { - // Remove one instance of this box updatedBoxes.splice(boxIndex, 1); } return updatedBoxes; }); - - // Show close button after a short delay to allow user to see what they won first setTimeout(() => { setShowCloseButton(true); - - // Start the timer when showing results startTimer(); }, 500); - - // No need for the automatic 5-second timeout anymore as we're using the timer bar - // The return cleanup is also not needed as we handle cleanup in the useEffect } else { - // If no result, reset everything after 2 seconds setTimeout(() => { setShowConfetti(false); setSelectedRarity(null); setIsExploding(false); setIsOpening(false); - // Trigger global refresh here since we got no valid result if (triggerRefresh) { triggerRefresh(); } }, 2000); } } catch (error) { - console.error('[LootBoxUtil] Error opening loot box:', error); setIsShaking(false); setIsExploding(false); setSelectedRarity(null); setShowConfetti(false); setIsOpening(false); - // Trigger global refresh on error to ensure UI is in sync if (triggerRefresh) { triggerRefresh(); } } }; - - // We've removed getTokenName and getBerryColor functions - // They have been replaced with simpler implementations - + // Get Berry Emoji or Logo const getBerryEmoji = (tokenId: string): React.ReactNode => { // Check if we have this asset in ASSET_INFO constants if (ASSET_INFO[tokenId]) { const asset = ASSET_INFO[tokenId]; const name = asset.name; - + // If we have a logo, return an image element if (asset.logo) { return ( - {name} ); } - + // If no logo, use question mark return '❓'; } - + // Handle undefined token case safely if (!tokenId) { console.warn("LootBoxUtil: Undefined token encountered in getBerryEmoji"); return "❓"; } - + // Default fallback return "❓"; }; - + // Group lootboxes by rarity - const groupedLootboxes = lootBoxes.reduce<{[key: number]: number}>((acc, box) => { + const groupedLootboxes = lootBoxes.reduce<{ [key: number]: number }>((acc, box) => { acc[box.rarity] = (acc[box.rarity] || 0) + 1; return acc; }, {}); - + // Sort rarity levels for consistent display - const rarityLevels = Object.keys(groupedLootboxes).map(Number).sort((a, b) => a - b); - + const rarityLevels = [1, 2, 3, 4, 5]; + // Render each rarity section const renderRaritySection = (rarity: number) => { const count = groupedLootboxes[rarity] || 0; const rarityName = getRarityName(rarity); const colorClass = getRarityColorClass(rarity); const glowClass = getRarityGlowClass(rarity); - + const isSelected = selectedRarity === rarity && isOpening; - + + const badgeStyleMap = { + 1: 'bg-gray-500 text-white border border-white', + 2: 'bg-green-500 text-white border border-white', + 3: 'bg-blue-500 text-white border border-white', + 4: 'bg-purple-500 text-white border border-white', + 5: 'bg-yellow-500 text-white border border-white', + }; + return ( -
-
- {count > 0 ? ( -
!isOpening && handleOpenLootBox(rarity)} +
+ {count > 0 ? ( +
!isOpening && handleOpenLootBox(rarity)} + > + {/* Badge */} + -
- 📦 -
- {rarityName} -
- {count} -
+ {count} +
+ {/* Icon */} +
3 ? 'animate-pulse' : ''}`}> + 📦
- ) : ( -
{rarityName} +
+ {Array.from({ length: rarity }, (_, i) => ( + + ))} +
+ {/* Open Button */} + +
+ ) : ( +
+
📦
+ {rarityName} +
+ {Array.from({ length: rarity }, (_, i) => ( + + ))}
- )} -
+ +
+ )}
); }; - + return ( <> {/* Floating collecting items that animate to wallet */} {collectingItems.map((item, index) => ( -
))} - -
- {showConfetti && ( -
- -
- )} - {isLoading ? ( -

Loading treasures...

- ) : lootBoxes.length === 0 && !openResult ? ( -

Your vault is empty. Complete activities to earn treasure!

- ) : ( -
- {/* Show either the rewards OR the loot box selection, never both */} - {openResult && openResult.result ? ( -
+
+

Treasure Vault

+ {showConfetti && ( +
+ +
+ )} + {(isLoading || loading) ? ( +

Loading treasures...

+ ) : lootBoxes.length === 0 && !openResult ? ( +

Your vault is empty. Complete activities to earn treasure!

+ ) : ( +
+ {/* Show either the rewards OR the loot box selection, never both */} + {openResult && openResult.result ? ( +
- {/* Timer bar */} -
-
+ > + {/* Timer bar */} +
+
+
+ {/* Close button (X) in the top-right corner */} + {showCloseButton && ( + + )} +
+

Rewards:

+
+
+ {Array.isArray(openResult.result) && openResult.result.length > 0 ? ( + openResult.result.map((item, index) => ( +
+ + {getBerryEmoji(item.token)} + + x{item.quantity} +
+ )) + ) : ( +
+

No rewards received. Try again!

+
+ )} +
+

+ {Array.isArray(openResult.result) && openResult.result.length > 0 ? 'Added to inventory!' : 'Better luck next time!'} +

- {/* Close button (X) in the top-right corner */} - {showCloseButton && ( - - )} -
-

Rewards:

-
-
- {Array.isArray(openResult.result) && openResult.result.length > 0 ? ( - openResult.result.map((item, index) => ( -
- - {getBerryEmoji(item.token)} - - x{item.quantity} -
- )) - ) : ( -
-

No rewards received. Try again!

+
+ 📦 +
+ {getRarityName(selectedRarity)} +
+ {Array.from({ length: selectedRarity }, (_, i) => ( + + ))}
- )} -
-

- {Array.isArray(openResult.result) && openResult.result.length > 0 ? 'Added to inventory!' : 'Better luck next time!'} -

-
- ) : isOpening && selectedRarity !== null ? ( - /* When opening, only show the selected treasure */ -
- {/* Only show the single selected box during animation */} -
-
- 📦
- {getRarityName(selectedRarity)}
-
- ) : ( - /* Default view: show all available treasures */ - <> -
-
+ ) : ( + /* Default view: show all available treasures */ + <> +
{rarityLevels.map(renderRaritySection)}
-
-
- Click on a loot box to open it -
- - )} -
- )} +
+ Click on a loot box to open it +
+ + )} +
+ )} +
); diff --git a/src/components/MonsterActivities.tsx b/src/components/MonsterActivities.tsx index 2bf2b16..6d44c9a 100644 --- a/src/components/MonsterActivities.tsx +++ b/src/components/MonsterActivities.tsx @@ -43,30 +43,31 @@ interface MonsterActivitiesProps { activities?: Activities; theme: Theme; className?: string; + onEffectTrigger?: (effect: string) => void; + onTriggerReturn?: () => void; } const MonsterActivities: React.FC = ({ monster: monsterProp, activities: activitiesProp, theme, - className = '' + className = '', + onEffectTrigger, + onTriggerReturn }) => { const navigate = useNavigate(); const { triggerRefresh, wallet } = useWallet(); const { tokenBalances, refreshAllTokens } = useTokens(); const { monster: contextMonster, formatTimeRemaining, calculateProgress, refreshMonsterAfterActivity } = useMonster(); - - // Use monster from props if provided, otherwise from context + const monster = monsterProp || contextMonster; - - // Use activities directly from props - must be provided const activities = activitiesProp!; - + const [isFeeding, setIsFeeding] = useState(false); const [isPlaying, setIsPlaying] = useState(false); const [isInBattle, setIsInBattle] = useState(false); const [isOnMission, setIsOnMission] = useState(false); - + // No monster data available if (!monster) { return ( @@ -75,16 +76,16 @@ const MonsterActivities: React.FC = ({
); } - + // Calculate if any activity is complete (timeUp is true) - const timeUp = monster.status.type !== 'Home' && - monster.status.until_time && - Date.now() > monster.status.until_time; - + const timeUp = monster.status.type !== 'Home' && + monster.status.until_time && + Date.now() > monster.status.until_time; + // Get token balances from token context const berryBalance = tokenBalances[activities.feed.cost.token as SupportedAssetId]?.balance || 0; const fuelBalance = tokenBalances[activities.battle.cost.token as SupportedAssetId]?.balance || 0; - + // Calculate if monster can feed const canFeed = monster.status.type === 'Home' && berryBalance >= activities.feed.cost.amount; @@ -114,40 +115,40 @@ const MonsterActivities: React.FC = ({ } else if (monster.status.type === 'Mission') { missionButtonText = timeUp ? "Return" : "Exploring..."; } - + // Check if all requirements are met for each activity - const canPlay = (monster.status.type === 'Home' && - berryBalance >= activities.play.cost.amount && - monster.energy >= activities.play.energyCost) || - (monster.status.type === 'Play' && timeUp); + const canPlay = (monster.status.type === 'Home' && + berryBalance >= activities.play.cost.amount && + monster.energy >= activities.play.energyCost) || + (monster.status.type === 'Play' && timeUp); - const canMission = (monster.status.type === 'Home' && - fuelBalance >= activities.mission.cost.amount && - monster.energy >= activities.mission.energyCost && - monster.happiness >= activities.mission.happinessCost) || - (monster.status.type === 'Mission' && timeUp); + const canMission = (monster.status.type === 'Home' && + fuelBalance >= activities.mission.cost.amount && + monster.energy >= activities.mission.energyCost && + monster.happiness >= activities.mission.happinessCost) || + (monster.status.type === 'Mission' && timeUp); const isBattleTime = monster.status.type === 'Battle'; const canReturn = isBattleTime && Date.now() > monster.status.until_time; - const canBattle = (monster.status.type === 'Home' && - fuelBalance >= activities.battle.cost.amount && - monster.energy >= activities.battle.energyCost && - monster.happiness >= activities.battle.happinessCost) || - (monster.status.type === 'Battle' && canReturn); + const canBattle = (monster.status.type === 'Home' && + fuelBalance >= activities.battle.cost.amount && + monster.energy >= activities.battle.energyCost && + monster.happiness >= activities.battle.happinessCost) || + (monster.status.type === 'Battle' && canReturn); // Handle feed monster const monsterInteraction = async (actionType) => { if (!monster) return; - + const actionKey = actionType.toLowerCase(); const config = activities[actionKey]; const targetProcessId = AdminSkinChanger; - + const isCurrentAction = monster.status.type.toLowerCase() === actionKey; const canReturn = isCurrentAction && Date.now() > monster.status.until_time; - + const asset = tokenBalances[config.cost.token as SupportedAssetId]; - + if (!canReturn && (!asset || asset.balance < config.cost.amount)) { console.error(`Not enough resources for action: ${actionType}`, { token: config.cost.token, @@ -157,19 +158,19 @@ const MonsterActivities: React.FC = ({ }); return; } - + try { if (actionType === 'FEED') setIsFeeding(true); else if (actionType === 'PLAY') setIsPlaying(true); else if (actionType === 'BATTLE') setIsInBattle(true); else if (actionType === 'MISSION') setIsOnMission(true); - + if (!wallet) { console.error('No wallet connected'); return; } const signer = await createDataItemSigner(wallet); - + await message({ process: canReturn ? targetProcessId : config.cost.token, tags: canReturn ? [ @@ -185,17 +186,26 @@ const MonsterActivities: React.FC = ({ }, () => { // First trigger the regular refresh triggerRefresh(); - + // Then schedule the forced monster data refresh after delay console.log(`[MonsterActivities] ${actionType} completed, scheduling monster refresh`); refreshMonsterAfterActivity(); + + if (canReturn && onTriggerReturn) { + onTriggerReturn(); + } + if (actionType === 'FEED' && onEffectTrigger) { + const healingEffects = ['Small Heal', 'Medium Heal', 'Large Heal', 'Full Heal']; + const randomHeal = healingEffects[Math.floor(Math.random() * healingEffects.length)]; + onEffectTrigger(randomHeal); + } }); //executeActivity(signer,actionType,canReturn,config.cost.token,config.cost.amount.toString()) - + if (actionType === 'BATTLE' && !canReturn) navigate('/battle'); - + refreshAllTokens(); - + } catch (error) { console.error(`Error handling ${actionType}:`, error); } finally { @@ -205,106 +215,107 @@ const MonsterActivities: React.FC = ({ else if (actionType === 'MISSION') setIsOnMission(false); } }; + return ( -
-
- monsterInteraction('FEED')} - isLoading={isFeeding} - isDisabled={!canFeed} - theme={theme} - highlightSelectable={!isFeeding && canFeed} - /> - - = (activities.play.energyCost || 0) } - ]} - rewards={[ - { icon: "💝", text: `+${activities.play.happinessGain} Happy`, color: "pink-500" } - ]} - onAction={() => monsterInteraction('PLAY')} - isLoading={isPlaying || (monster.status.type === 'Play' && !timeUp)} - isDisabled={!canPlay || (monster.status.type !== 'Home' && (monster.status.type !== 'Play' || (monster.status.type === 'Play' && !timeUp)))} - remainingTime={monster.status.type === 'Play' && monster.status.until_time ? formatTimeRemaining(monster.status.until_time) : undefined} - progress={monster.status.type === 'Play' && monster.status.since && monster.status.until_time ? calculateProgress(monster.status.since, monster.status.until_time) : undefined} - theme={theme} - highlightSelectable={!isPlaying && canPlay} - /> - - = (activities.battle.energyCost || 0) }, - { icon: "💝", text: `-${activities.battle.happinessCost} Happy`, isAvailable: monster.happiness >= (activities.battle.happinessCost || 0) } - ]} - rewards={[ - { icon: "⚔️", text: "GLORY", color: "purple-500" } // Updated reward text - ]} - onAction={() => monsterInteraction('BATTLE')} - isLoading={isInBattle || (monster.status.type === 'Battle' && !timeUp)} - isDisabled={!canBattle || (monster.status.type !== 'Home' && (monster.status.type !== 'Battle' || (monster.status.type === 'Battle' && !timeUp)))} - remainingTime={monster.status.type === 'Battle' && monster.status.until_time ? formatTimeRemaining(monster.status.until_time) : undefined} - progress={monster.status.type === 'Battle' && monster.status.since && monster.status.until_time ? calculateProgress(monster.status.since, monster.status.until_time) : undefined} - theme={theme} - highlightSelectable={!isInBattle && canBattle} - /> - - = (activities.mission.energyCost || 0) }, - { icon: "💝", text: `-${activities.mission.happinessCost} Happy`, isAvailable: monster.happiness >= (activities.mission.happinessCost || 0) } - ]} - rewards={[ - { icon: "✨", text: "+LOOT", color: "blue-500" } // Updated reward text - ]} - onAction={() => monsterInteraction('MISSION')} - isLoading={isOnMission || (monster.status.type === 'Mission' && !timeUp)} - isDisabled={!canMission || (monster.status.type !== 'Home' && (monster.status.type !== 'Mission' || (monster.status.type === 'Mission' && !timeUp)))} - remainingTime={monster.status.type === 'Mission' && monster.status.until_time ? formatTimeRemaining(monster.status.until_time) : undefined} - progress={monster.status.type === 'Mission' && monster.status.since && monster.status.until_time ? calculateProgress(monster.status.since, monster.status.until_time) : undefined} - theme={theme} - highlightSelectable={!isOnMission && canMission} - /> +
+
+ {/* Header */} +

Activities

+ + {/* Activities Grid */} +
+ monsterInteraction('FEED')} + isLoading={isFeeding} + isDisabled={!canFeed} + theme={theme} + highlightSelectable={!isFeeding && canFeed} + /> + + = (activities.play.energyCost || 0) } + ]} + rewards={[ + { icon: "💝", text: `+${activities.play.happinessGain} Happy`, color: "pink-500" } + ]} + onAction={() => monsterInteraction('PLAY')} + isLoading={isPlaying || (monster.status.type === 'Play' && !timeUp)} + isDisabled={!canPlay || (monster.status.type !== 'Home' && (monster.status.type !== 'Play' || (monster.status.type === 'Play' && !timeUp)))} + theme={theme} + highlightSelectable={!isPlaying && canPlay} + /> + + = (activities.battle.energyCost || 0) }, + { icon: "💝", text: `-${activities.battle.happinessCost} Happy`, isAvailable: monster.happiness >= (activities.battle.happinessCost || 0) } + ]} + rewards={[ + { icon: "⚔️", text: "GLORY", color: "purple-500" } + ]} + onAction={() => monsterInteraction('BATTLE')} + isLoading={isInBattle || (monster.status.type === 'Battle' && !timeUp)} + isDisabled={!canBattle || (monster.status.type !== 'Home' && (monster.status.type !== 'Battle' || (monster.status.type === 'Battle' && !timeUp)))} + theme={theme} + highlightSelectable={!isInBattle && canBattle} + /> + + = (activities.mission.energyCost || 0) }, + { icon: "💝", text: `-${activities.mission.happinessCost} Happy`, isAvailable: monster.happiness >= (activities.mission.happinessCost || 0) } + ]} + rewards={[ + { icon: "💰", text: "+LOOT", color: "blue-500" } + ]} + onAction={() => monsterInteraction('MISSION')} + isLoading={isOnMission || (monster.status.type === 'Mission' && !timeUp)} + isDisabled={!canMission || (monster.status.type !== 'Home' && (monster.status.type !== 'Mission' || (monster.status.type === 'Mission' && !timeUp)))} + theme={theme} + highlightSelectable={!isOnMission && canMission} + /> +
); diff --git a/src/components/MonsterStatusWindow.tsx b/src/components/MonsterStatusWindow.tsx index 87720cb..b3eb192 100644 --- a/src/components/MonsterStatusWindow.tsx +++ b/src/components/MonsterStatusWindow.tsx @@ -18,12 +18,32 @@ interface MonsterStatusWindowProps { monster: MonsterStats; theme: Theme; onShowCard?: () => void; + formatTimeRemaining?: (until: number) => string; + calculateProgress?: (since: number, until: number) => number; + isActivityComplete?: (monster: MonsterStats) => boolean; + currentEffect?: string | null; + onEffectTrigger?: (effect: string) => void; + triggerReturn?: boolean; + onReturnComplete?: () => void; + isLevelingUp?: boolean; + onLevelUp?: () => void; + getFibonacciExp?: (level: number) => number; } const MonsterStatusWindow: React.FC = ({ monster, theme, onShowCard, + formatTimeRemaining, + calculateProgress, + isActivityComplete, + currentEffect, + onEffectTrigger, + triggerReturn, + onReturnComplete, + isLevelingUp, + onLevelUp, + getFibonacciExp, }) => { // State for monster roaming behavior const [currentAnimation, setCurrentAnimation] = useState('idleRight'); @@ -33,27 +53,125 @@ const MonsterStatusWindow: React.FC = ({ const [isWalking, setIsWalking] = useState(false); const [containerSize, setContainerSize] = useState({ width: 0, height: 0 }); const [selectedBackground, setSelectedBackground] = useState('home'); + const [forestBackgrounds] = useState(['forest']); // Multiple nature backgrounds for Play/Exploring + const [currentForestIndex, setCurrentForestIndex] = useState(0); + + // State for transition animations + const [isExitAnimation, setIsExitAnimation] = useState(false); + const [isEntranceAnimation, setIsEntranceAnimation] = useState(false); + const [isReturnAnimation, setIsReturnAnimation] = useState(false); + const [hasCompletedEntrance, setHasCompletedEntrance] = useState(false); + const [previousActivityType, setPreviousActivityType] = useState(''); + const [backgroundPosition, setBackgroundPosition] = useState(0); + + // State for real-time progress animation + const [currentTime, setCurrentTime] = useState(Date.now()); // Refs for timers and animation const roamingTimerRef = useRef(); const walkingTimerRef = useRef(); const containerRef = useRef(null); + const animationRef = useRef(); // Constants for movement - const WALK_SPEED = 1; + const WALK_SPEED = 2; + const BACKGROUND_SCROLL_SPEED = 2.5; const monsterSize = containerSize.width * 0.25; const fullWalkDistance = (containerSize.width - monsterSize) / 2; const walkDistance = fullWalkDistance; + // Check if monster is in exploring or playing state + const isExploringOrPlaying = monster.status.type.toLowerCase() === 'exploring' || + monster.status.type.toLowerCase() === 'play'; + + // Set background based on monster status with multiple forest options + useEffect(() => { + switch (monster.status.type.toLowerCase()) { + case 'home': + setSelectedBackground('home'); + setBackgroundPosition(0); // Always keep centered + setAnimationControl({ type: 'once' }); // Reset to normal animation + break; + case 'play': + // Select random forest background when starting play + const randomPlayIndex = Math.floor(Math.random() * forestBackgrounds.length); + setCurrentForestIndex(randomPlayIndex); + setSelectedBackground(forestBackgrounds[randomPlayIndex]); + setBackgroundPosition(0); // Always keep centered + break; + case 'exploring': + // Select random forest background when starting exploring + const randomExploreIndex = Math.floor(Math.random() * forestBackgrounds.length); + setCurrentForestIndex(randomExploreIndex); + setSelectedBackground(forestBackgrounds[randomExploreIndex]); + setBackgroundPosition(0); // Always keep centered + break; + case 'mission': + const missionBg = Math.random() > 0.5 ? 'greenhouse' : 'beach'; + setSelectedBackground(missionBg); + setBackgroundPosition(0); // Always keep centered + break; + default: + setSelectedBackground('home'); + setBackgroundPosition(0); // Always keep centered + break; + } + }, [monster.status.type, forestBackgrounds]); - // Set background based on monster status + // Handle manual return trigger useEffect(() => { - switch(monster.status.type.toLowerCase()) { - case 'home': setSelectedBackground('home'); break; - case 'play': setSelectedBackground('forest'); break; - case 'mission': setSelectedBackground(Math.random() > 0.5 ? 'greenhouse' : 'beach'); break; - default: setSelectedBackground('home'); break; + if (triggerReturn && isExploringOrPlaying && !isReturnAnimation && !isExitAnimation && !isEntranceAnimation) { + console.log(`[MonsterStatusWindow] Manual return triggered`); + setIsReturnAnimation(true); + setIsExitAnimation(false); + setIsEntranceAnimation(false); + setHasCompletedEntrance(false); + setCurrentAnimation('walkLeft'); + setPosition(0); // Start from center in current background + + // Clear roaming timers + if (roamingTimerRef.current) { + clearTimeout(roamingTimerRef.current); + } + } + }, [triggerReturn, isExploringOrPlaying, isReturnAnimation, isExitAnimation, isEntranceAnimation]); + + // Track activity changes for transition animations + useEffect(() => { + const currentActivity = monster.status.type.toLowerCase(); + + // When switching from home to play/exploring, trigger exit animation + if ((currentActivity === 'play' || currentActivity === 'exploring') && + previousActivityType === 'home') { + console.log(`[MonsterStatusWindow] Starting exit animation from home to ${currentActivity}`); + setIsExitAnimation(true); + setIsReturnAnimation(false); + setIsEntranceAnimation(false); + setHasCompletedEntrance(false); + setCurrentAnimation('walkRight'); + setPosition(0); // Start from center } - }, [monster.status.type]); + // When switching from play/exploring to home, trigger return animation (auto return) + else if (currentActivity === 'home' && + (previousActivityType === 'play' || previousActivityType === 'exploring') && + !triggerReturn) { // Only auto-return if not manually triggered + console.log(`[MonsterStatusWindow] Auto return animation from ${previousActivityType} to home`); + setIsReturnAnimation(true); + setIsExitAnimation(false); + setIsEntranceAnimation(false); + setHasCompletedEntrance(false); + setCurrentAnimation('walkLeft'); + setPosition(0); // Start from center in current background + } + // When already in play/exploring state, trigger entrance animation if not completed + else if (isExploringOrPlaying && !hasCompletedEntrance && !isExitAnimation && !isReturnAnimation) { + console.log(`[MonsterStatusWindow] Starting entrance animation for ${currentActivity}`); + setIsEntranceAnimation(true); + setPosition(-walkDistance); // Start from left edge + setCurrentAnimation('walkRight'); + } + + setPreviousActivityType(currentActivity); + }, [monster.status.type, isExploringOrPlaying, walkDistance, hasCompletedEntrance, isExitAnimation, isReturnAnimation, previousActivityType, triggerReturn]); // Handle container resizing useEffect(() => { @@ -69,143 +187,437 @@ const MonsterStatusWindow: React.FC = ({ return () => resizeObserver.disconnect(); }, []); - // Natural roaming behavior system + // Handle walking animation with exit/entrance/return/continuous scrolling useEffect(() => { - if (walkDistance <= 0) return; - - const startRoaming = () => { - // Random action: idle left, idle right, walk left, or walk right - const actions = ['idleLeft', 'idleRight', 'walkLeft', 'walkRight'] as const; - const randomAction = actions[Math.floor(Math.random() * actions.length)]; - - if (randomAction.startsWith('idle')) { - // Just idle in the specified direction - const timestamp = new Date().toISOString(); - console.log(`[${timestamp}] MonsterStatusWindow: Setting idle animation:`, randomAction); - setIsWalking(false); - setCurrentAnimation(randomAction as AnimationType); - setAnimationControl({ type: 'once' }); // Idle animations should be once/static - setDirection(randomAction === 'idleLeft' ? 'left' : 'right'); - - // Schedule next action after idle period - roamingTimerRef.current = setTimeout(startRoaming, 1500 + Math.random() * 2500); + if (isExitAnimation || isEntranceAnimation || isReturnAnimation || isExploringOrPlaying) { + setIsWalking(true); + if (isReturnAnimation) { + setDirection('left'); + setCurrentAnimation('walkLeft'); } else { - // Start walking in the specified direction - const walkDirection = randomAction === 'walkLeft' ? 'left' : 'right'; - const timestamp = new Date().toISOString(); - console.log(`[${timestamp}] MonsterStatusWindow: Starting walking animation:`, randomAction, 'direction:', walkDirection); - setDirection(walkDirection); - setIsWalking(true); - setCurrentAnimation(randomAction as AnimationType); - setAnimationControl({ type: 'perpetual' }); // Walking animations should be perpetual - - // Walk for a random duration, then stop and idle - const walkDuration = 1000 + Math.random() * 2000; - console.log(`[${timestamp}] MonsterStatusWindow: Walking duration set to:`, walkDuration, 'ms'); - walkingTimerRef.current = setTimeout(() => { - const endTimestamp = new Date().toISOString(); - console.log(`[${endTimestamp}] MonsterStatusWindow: Ending walking, switching to idle:`, walkDirection === 'left' ? 'idleLeft' : 'idleRight'); - setIsWalking(false); - setCurrentAnimation(walkDirection === 'left' ? 'idleLeft' : 'idleRight'); - setAnimationControl({ type: 'once' }); // Switch back to once for idle - - // Schedule next action after walking - roamingTimerRef.current = setTimeout(startRoaming, 1000 + Math.random() * 2000); - }, walkDuration); + setDirection('right'); + setCurrentAnimation('walkRight'); + } + } else { + // For other activities (like home), use the original random walking logic + if (!isWalking || walkDistance <= 0) { + setCurrentAnimation('idleRight'); + return; + } + } + + const moveMonster = (timestamp: number) => { + if (isExitAnimation) { + // Exit animation: monster walks from center to right edge (in home background) + setPosition(prevPos => { + let newPos = prevPos + WALK_SPEED; + + // When monster reaches right edge, complete exit and immediately trigger entrance + if (newPos >= walkDistance) { + newPos = walkDistance; + console.log(`[MonsterStatusWindow] Exit animation complete, triggering entrance`); + setIsExitAnimation(false); + // Background changes to forest immediately, then start entrance animation + setIsEntranceAnimation(true); + setPosition(-walkDistance); // Monster appears from left edge + } + + return newPos; + }); + } else if (isReturnAnimation) { + // Return animation: monster walks from center to left edge (in forest background) + setPosition(prevPos => { + let newPos = prevPos - WALK_SPEED; + + // When monster reaches left edge, complete return and enter home from left + if (newPos <= -walkDistance) { + newPos = -walkDistance; + console.log(`[MonsterStatusWindow] Return animation complete, entering home`); + setIsReturnAnimation(false); + // Background changes to home immediately, then enter from left + setPosition(-walkDistance); + setIsEntranceAnimation(true); + + // Notify parent that return is complete + if (onReturnComplete) { + onReturnComplete(); + } + } + + return newPos; + }); + } else if (isEntranceAnimation && !hasCompletedEntrance) { + // Entrance animation: monster walks from left to center (in current background) + setPosition(prevPos => { + let newPos = prevPos + WALK_SPEED; + + // When monster reaches center, stop entrance animation + if (newPos >= 0) { + newPos = 0; + console.log(`[MonsterStatusWindow] Entrance animation complete`); + setIsEntranceAnimation(false); + // Only set completed entrance for exploring/playing activities + if (isExploringOrPlaying) { + setHasCompletedEntrance(true); + } else { + // For home activities, reset all states + setHasCompletedEntrance(false); + setPosition(0); + setBackgroundPosition(0); + } + } + + return newPos; + }); + } + + // Background scrolling for exploring/playing monsters (after entrance, no other animations) + if (isExploringOrPlaying && hasCompletedEntrance && !isExitAnimation && !isEntranceAnimation && !isReturnAnimation) { + // Keep monster at center position when exploring/playing + setPosition(0); + + // Scroll background continuously + setBackgroundPosition(prevPos => { + let newPos = prevPos + BACKGROUND_SCROLL_SPEED; + + return newPos; + }); + } + + // Original walking logic for other activities (home, etc.) - only when no transitions + if (!isExitAnimation && !isEntranceAnimation && !isReturnAnimation && !isExploringOrPlaying) { + setPosition(prevPos => { + // Calculate movement based on direction + const directionMultiplier = direction === 'right' ? 1 : -1; + let newPos = prevPos + (WALK_SPEED * directionMultiplier); + + // Handle direction change at restricted boundaries + if (newPos >= walkDistance) { + newPos = walkDistance - 0.1; + setDirection('left'); + setCurrentAnimation('walkLeft'); + } else if (newPos <= -walkDistance) { + newPos = -walkDistance + 0.1; + setDirection('right'); + setCurrentAnimation('walkRight'); + } else { + setCurrentAnimation(direction === 'right' ? 'walkRight' : 'walkLeft'); + } + + return newPos; + }); } + + animationRef.current = requestAnimationFrame(moveMonster); }; - // Start the roaming behavior - startRoaming(); + animationRef.current = requestAnimationFrame(moveMonster); + + return () => { + if (animationRef.current) { + cancelAnimationFrame(animationRef.current); + } + }; + }, [isWalking, walkDistance, direction, isExitAnimation, isEntranceAnimation, isReturnAnimation, isExploringOrPlaying, hasCompletedEntrance]); + + // Handle idle/walking state changes for home activities only + useEffect(() => { + if (isExitAnimation || isEntranceAnimation || isReturnAnimation || isExploringOrPlaying) { + // Don't change state for special animations or exploring/playing + return; + } + + const decideState = () => { + const shouldWalk = Math.random() < 0.3; // 30% chance to walk + setIsWalking(shouldWalk); + + // Set a random time for the next state change (between 2-5 seconds) + const nextStateChange = 2000 + Math.random() * 3000; + + roamingTimerRef.current = setTimeout(() => { + decideState(); + }, nextStateChange); + }; + + decideState(); return () => { if (roamingTimerRef.current) clearTimeout(roamingTimerRef.current); - if (walkingTimerRef.current) clearTimeout(walkingTimerRef.current); + if (animationRef.current) cancelAnimationFrame(animationRef.current); }; - }, [walkDistance]); + }, [isExitAnimation, isEntranceAnimation, isReturnAnimation, isExploringOrPlaying]); - // Handle position updates during walking + // Handle roaming for exploring/playing after entrance is complete useEffect(() => { - if (!isWalking || walkDistance <= 0) return; - - const timestamp = new Date().toISOString(); - console.log(`[${timestamp}] MonsterStatusWindow: Starting position updates for walking in direction:`, direction); - - const updatePosition = () => { - setPosition(prevPos => { - const directionMultiplier = direction === 'right' ? 1 : -1; - let newPos = prevPos + (WALK_SPEED * directionMultiplier); - - // Respect boundaries - if (newPos >= walkDistance) { - newPos = walkDistance - 0.1; - } else if (newPos <= -walkDistance) { - newPos = -walkDistance + 0.1; - } - - return newPos; - }); + if (!isExploringOrPlaying || !hasCompletedEntrance || isExitAnimation || isEntranceAnimation || isReturnAnimation) { + return; + } + + console.log(`[MonsterStatusWindow] Starting exploring/playing behavior - monster stays center, background scrolls`); + + // For play/explore: monster stays in center with continuous running animation + setIsWalking(true); // Always running when playing/exploring + setPosition(0); // Keep monster at center + + console.log(`[MonsterStatusWindow] Play/Explore mode: isWalking=true, position=0, background will scroll`); + + // Start with right direction for running + setDirection('right'); + setCurrentAnimation('walkRight'); + // Set perpetual animation for continuous running effect + setAnimationControl({ type: 'perpetual' }); + + // Faster direction changes for more dynamic running effect + const changeDirection = () => { + const newDirection = Math.random() < 0.5 ? 'left' : 'right'; + setDirection(newDirection); + setCurrentAnimation(newDirection === 'right' ? 'walkRight' : 'walkLeft'); + // Keep perpetual animation + setAnimationControl({ type: 'perpetual' }); + + // Shorter intervals for more active running animation + const directionChangeInterval = 1500 + Math.random() * 1500; // 1.5-3 seconds + + roamingTimerRef.current = setTimeout(() => { + changeDirection(); + }, directionChangeInterval); }; - const intervalId = setInterval(updatePosition, 33); // ~30fps - + // Start the continuous animation cycle + changeDirection(); + return () => { - const endTimestamp = new Date().toISOString(); - console.log(`[${endTimestamp}] MonsterStatusWindow: Stopping position updates`); - clearInterval(intervalId); + if (roamingTimerRef.current) clearTimeout(roamingTimerRef.current); }; - }, [isWalking, direction, walkDistance]); + }, [isExploringOrPlaying, hasCompletedEntrance, isExitAnimation, isEntranceAnimation, isReturnAnimation]); + + // Real-time progress bar animation + useEffect(() => { + if (monster.status.type === 'Home' || !monster.status.until_time) { + return; + } + + const updateTimer = setInterval(() => { + setCurrentTime(Date.now()); + }, 1000); // Update every second + + return () => clearInterval(updateTimer); + }, [monster.status.type, monster.status.until_time]); + + // Static background - no scrolling to avoid motion sickness + // The running effect is now purely from monster animation + useEffect(() => { + // Reset background position to center when starting/stopping activities + if (isExploringOrPlaying && hasCompletedEntrance && !isExitAnimation && !isEntranceAnimation && !isReturnAnimation) { + // Keep background perfectly centered during activities + setBackgroundPosition(0); + } + }, [isExploringOrPlaying, hasCompletedEntrance, isExitAnimation, isEntranceAnimation, isReturnAnimation]); + + // Disabled background rotation to keep it simple and stable + // Background stays consistent during activities to avoid visual distraction + + // Helper functions for the design + const getEnvironmentName = (status: string) => { + switch (status.toLowerCase()) { + case 'home': return 'Living Room'; + case 'play': return 'Forest Playground'; + case 'exploring': return 'Whispering Woods'; + case 'mission': return selectedBackground === 'greenhouse' ? 'Mystical Greenhouse' : 'Sunny Beach'; + default: return 'Cozy Den'; + } + }; + + const getEnvironmentDescription = (status: string) => { + switch (status.toLowerCase()) { + case 'home': return 'A warm and inviting space with a comfy couch.'; + case 'play': return 'Running around and having fun in nature.'; + case 'exploring': return 'Discovering new paths and hidden treasures.'; + case 'mission': return selectedBackground === 'greenhouse' ? 'A magical place filled with exotic plants.' : 'A peaceful shoreline with gentle waves.'; + default: return 'A peaceful resting place.'; + } + }; + + const getTimeOfDay = () => { + const hour = new Date().getHours(); + return hour >= 6 && hour < 18 ? 'Day' : 'Night'; + }; + + // Check if activity is complete using real-time + const activityTimeUp = monster.status.type !== 'Home' && + monster.status.until_time && + currentTime >= monster.status.until_time; return ( -
-
-
- Status: {monster.status.type} +
+
+
+

+ Monster Status +

+

Keep an eye on your companion

- -
+ {/* Status Card */} +
+ {/* Environment Display */}
- - {monsterSize > 0 && ( -
+
+ + {/* Time of Day Badge */} +
+ + {getTimeOfDay() === "Day" ? '☀️' : '☾'}{" "} + {getTimeOfDay()} + +
+ + {/* Location Badge */} +
+ 📍 {getEnvironmentName(monster.status.type)} +
+ + {/* Activity Status Badge */} + {(monster.status.type !== 'Home' && (monster.status.until_time || activityTimeUp)) && ( +
+ + {activityTimeUp + ? 'Complete!' + : (() => { + if (!monster.status.until_time) return ''; + const remaining = Math.max(0, monster.status.until_time - currentTime); + const h = Math.floor(remaining / 3600000); + const m = Math.floor((remaining % 3600000) / 60000); + const s = Math.floor((remaining % 60000) / 1000); + return h > 0 + ? `${h}h ${m}m ${s}s` + : m > 0 + ? `${m}m ${s}s` + : `${s}s`; + })() + } + +
+ )} + + {/* View NFT Card Button Overlay */} + + + {/* Monster Sprite */} + {monsterSize > 0 && ( +
+ { + // Clear the effect after animation completes + if (onEffectTrigger) { + onEffectTrigger(''); + } + }} + /> +
+ )} +
+ + {/* Monster and Status Info */} +
+
+
+

Status: {monster.status.type}

+ {/* Level Up Button - Always show for debugging */} + +
+

{getEnvironmentDescription(monster.status.type)}

+ + {/* Progress Bar for activities */} + {monster.status.type !== 'Home' && monster.status.until_time && ( +
+ {/* Progress Bar with clear background */} +
+
{ + const totalDuration = monster.status.until_time - monster.status.since; + const elapsed = currentTime - monster.status.since; + return Math.max(0, (elapsed / totalDuration) * 100); + })())}%` + }} + /> +
+ {/* Time remaining with better visibility */} +
+

+ {activityTimeUp ? '✅ Activity completed!' : (() => { + const remaining = Math.max(0, monster.status.until_time - currentTime); + const hours = Math.floor(remaining / (1000 * 60 * 60)); + const minutes = Math.floor((remaining % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = Math.floor((remaining % (1000 * 60)) / 1000); + + if (hours > 0) { + return `⏰ ${hours}h ${minutes}m ${seconds}s remaining`; + } else if (minutes > 0) { + return `⏰ ${minutes}m ${seconds}s remaining`; + } else { + return `⏰ ${seconds}s remaining`; + } + })()} +

+

+ {Math.round(Math.min(100, (() => { + const totalDuration = monster.status.until_time - monster.status.since; + const elapsed = currentTime - monster.status.since; + return Math.max(0, (elapsed / totalDuration) * 100); + })()))}% +

+
+
+ )}
- )} +
- {onShowCard && ( + {/* {onShowCard && (
- )} + )} */}
diff --git a/src/pages/MonsterManagement.tsx b/src/pages/MonsterManagement.tsx index 7f598fc..7bc998e 100644 --- a/src/pages/MonsterManagement.tsx +++ b/src/pages/MonsterManagement.tsx @@ -22,17 +22,16 @@ export const MonsterManagement: React.FC = (): JSX.Element => { const navigate = useNavigate(); // Include refreshTrigger for lootbox updates const { wallet, walletStatus, darkMode, connectWallet, setDarkMode, triggerRefresh } = useWallet(); - const { - monster: localMonster, - lootBoxes, + const { + monster: localMonster, + lootBoxes, isLoadingLootBoxes, timeUpdateTrigger, - getRarityName, formatTimeRemaining, calculateProgress, refreshMonsterAfterActivity } = useMonster(); - + const [isPurchaseModalOpen, setIsPurchaseModalOpen] = useState(false); const [showConfetti, setShowConfetti] = useState(false); const [isAdopting, setIsAdopting] = useState(false); @@ -40,6 +39,7 @@ export const MonsterManagement: React.FC = (): JSX.Element => { const [showStatModal, setShowStatModal] = useState(false); const [currentEffect, setCurrentEffect] = useState(null); const [showCardModal, setShowCardModal] = useState(false); + const [triggerReturn, setTriggerReturn] = useState(false); const theme = currentTheme(darkMode); const [, setForceUpdate] = useState({}); const effectTimeoutRef = useRef(null); @@ -50,15 +50,15 @@ export const MonsterManagement: React.FC = (): JSX.Element => { console.log('[MonsterManagement] Effect already playing, ignoring trigger:', effect); return; } - + console.log('[MonsterManagement] Triggering effect:', effect); setCurrentEffect(effect); - + // Clear any existing timeout if (effectTimeoutRef.current) { clearTimeout(effectTimeoutRef.current); } - + // Auto-clear the effect after 1 second (allowing effect animation to complete naturally) // This is a fallback - the effect should complete via the animation system effectTimeoutRef.current = setTimeout(() => { @@ -67,18 +67,16 @@ export const MonsterManagement: React.FC = (): JSX.Element => { }, 1000); }; - // Handle effect completion from the sprite component - const handleEffectComplete = () => { - console.log('[MonsterManagement] Effect completed, clearing state'); - - // Clear the timeout since effect completed naturally - if (effectTimeoutRef.current) { - clearTimeout(effectTimeoutRef.current); - effectTimeoutRef.current = null; - } - - // Clear the effect state - setCurrentEffect(null); + // Handle return animation completion + const handleReturnComplete = () => { + console.log('[MonsterManagement] Return animation completed'); + setTriggerReturn(false); // Reset trigger + }; + + // Function to trigger return animation (can be called by Activity buttons) + const triggerReturnAnimation = () => { + console.log('[MonsterManagement] Triggering return animation'); + setTriggerReturn(true); }; // Force a re-render when time update trigger changes in the context @@ -86,13 +84,13 @@ export const MonsterManagement: React.FC = (): JSX.Element => { if (localMonster && localMonster.status && localMonster.status.type !== 'Home') { // Just update the UI when the timer changes in the context setForceUpdate({}); - + // Log the current progress if (localMonster.status.until_time) { const now = Date.now(); const progress = calculateProgress(localMonster.status.since, localMonster.status.until_time); console.log(`[MonsterManagement] Progress update: ${Math.round(progress * 100)}%`); - + // If the activity just completed, log it if (now >= localMonster.status.until_time) { console.log('[MonsterManagement] Activity detected as complete'); @@ -112,10 +110,10 @@ export const MonsterManagement: React.FC = (): JSX.Element => { await connectWallet(); return; } - + setIsLevelingUp(true); console.log('Leveling up monster with stats:', stats); - + const signer = createDataItemSigner(wallet); await message({ process: "j7NcraZUL6GZlgdPEoph12Q5rk_dydvQDecLNxYi8rI", @@ -132,7 +130,7 @@ export const MonsterManagement: React.FC = (): JSX.Element => { console.log('[MonsterManagement] Activity executed, refreshing soon...'); // Trigger the regular refresh triggerRefresh(); - + // Also schedule the forced monster refresh to get the updated monster state console.log('[MonsterManagement] Scheduling monster refresh after level up'); refreshMonsterAfterActivity(); @@ -146,13 +144,13 @@ export const MonsterManagement: React.FC = (): JSX.Element => { const handleAdoptMonster = async () => { if (isAdopting) return; // Prevent multiple clicks - + setIsAdopting(true); try { await adoptMonster(wallet, walletStatus, () => { // Trigger regular refresh triggerRefresh(); - + // Schedule monster refresh after adoption console.log('[MonsterManagement] Monster adoption initiated, scheduling refresh'); refreshMonsterAfterActivity(); @@ -189,7 +187,7 @@ export const MonsterManagement: React.FC = (): JSX.Element => { const getFibonacciExp = (level: number) => { if (level === 0) return 1; if (level === 1) return 2; - + let a = 1, b = 2; for (let i = 2; i <= level; i++) { const next = a + b; @@ -224,27 +222,39 @@ export const MonsterManagement: React.FC = (): JSX.Element => { // Use monster directly from context instead of walletStatus to ensure we have the latest state const monster = localMonster || walletStatus.monster; const activities = walletStatus.monster.activities; - + return ( <> -
- {/* Main layout - Left side (status + level up) and Right side (stats, lootbox, activities) */} -
+
+ {/* Main layout - Use monster-layout-container for responsive grid */} +
{/* Left Side - Status Window and Level Up (65%) */} -
+
{/* Monster Status Window - Expanded height */} -
- + setShowCardModal(true)} + currentEffect={currentEffect} + onEffectTrigger={(effect: string) => { + if (effect === '') { + setCurrentEffect(null); + } else { + setCurrentEffect(effect); + } + }} + triggerReturn={triggerReturn} + onReturnComplete={handleReturnComplete} + isLevelingUp={isLevelingUp} + onLevelUp={handleLevelUp} />
- + {/* Level Up Section - Moved to left side */} {monster.status.type === 'Home' && monster.exp >= getFibonacciExp(monster.level) && ( -
-
+
+

Level Up Available

Your monster has enough experience to level up

@@ -252,7 +262,7 @@ export const MonsterManagement: React.FC = (): JSX.Element => { @@ -260,42 +270,29 @@ export const MonsterManagement: React.FC = (): JSX.Element => {
)}
- +
+ +
{/* Right Side Panel - Monster Stats, Loot Box, and Activities (35%) */} -
- {/* Monster Stats Display - Equal height (1/3) */} -
- -
- - {/* Loot Box Section - Equal height (1/3) */} -
- -
- - {/* Activities Section - Equal height (1/3) */} -
- -
+
+
- + {/* Monster Card Modal */} - setShowCardModal(false)} monster={monster} @@ -326,7 +323,7 @@ export const MonsterManagement: React.FC = (): JSX.Element => { theme={theme} darkMode={darkMode} /> - + {showConfetti && ( { ) : ( renderMonsterCard )} - - {/* Loot boxes now appear within the monster card component */}