mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
graphql and onchain storage support (#1794)
* support storage type publish * adding storageType to edit form * add IPFS type * fix testst and rollback ipfs typing * update oceanjs lib * fix Ipfs type * Update package-lock.json * added graphql and smartcontract options UI (WIP) * update package.json * removed graphql and smartcontracts * make various fixes for edit and publish with IPFS (missing Arweave) * removed no-case-declarations lines * moved ipfs utils * renamed getFileUrlInfo to getFileInfo * added is-ipfs to jest mock * Update package-lock.json * fix things * npm is fun * rename url to file in getFileInfo * refactor publish form (storage type field) * fix tab value when changing tabs * refactoring edit form * more refactor edit form * fix edit validation * fix validation when input is empty on edit form * fix validation when loading asset in edit form * change URL to file confirmed * change messages based on service type * Update form.json * fix FileInput tests and added ipfs / arweave tests * removed unnecessary comment * Update index.tsx * adding graphql / onchain to fieldInput * merged main and fix storages (WIP) * restore graphql / smart contract * added headers to graphql and connect fields * fix tests temp * fix publish preview file object * added codemirror component and change layout * added codemirror to edit and normalizing ddo debug preview * added codemirror to edit form * fix typing issues * style and logic changes to codemirror component * fix test (temp fix) * Avoiding getInitialPaymentCollector failure (#1816) * early return is no web3 or ddo * Creating test for MetaFull * adding test: src/components/Asset/AssetContent/MetaSecondary.test.tsx * Adding test for bookmarks * Adding test for displaying payment collector * Removing comments * Renaming assetAquarius * Renaming assetWithAccessDetails * Ensuring that the payment collector is shown even without a wallet connected * Removing broken test * Using getDummyWeb3 for fetching the payment collector address * WIP * update ocean lib * parse abi obj on publish * fix previewDebugPatch function * Update package-lock.json * remove logs * fix build error * fix stupid typo * upgrade codemirror * simplify checkJson function * fix preview json display * validate json on sm storage * cleanup * fix edit validation * fix initial state tabs storage type * remove logs * fix text headers * google validation (#1835) * Updating validation to exclude any google link * Updating Yup validation * Checking if domain includes google.com * Updating isGoogleUrl function * Moving isGoogleUrl into @utils/url/index file * isGoogleUrl function * Updating tests * Adding additional tests for other google domains * Updating tests * Updating isGoogleUrl file path * upgrade ocean lib * cleanup * edit mode fixes * added tests for graphql and sm * Updating pricing message (#1842) * Bump @storybook/addon-essentials from 6.5.13 to 6.5.15 (#1841) Bumps [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/addons/essentials) from 6.5.13 to 6.5.15. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/v6.5.15/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v6.5.15/addons/essentials) --- updated-dependencies: - dependency-name: "@storybook/addon-essentials" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @types/jest from 29.2.3 to 29.2.5 (#1840) Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.2.3 to 29.2.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: "@types/jest" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump prettier from 2.8.0 to 2.8.1 (#1837) Bumps [prettier](https://github.com/prettier/prettier) from 2.8.0 to 2.8.1. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.8.0...2.8.1) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump react-select from 5.6.1 to 5.7.0 (#1839) Bumps [react-select](https://github.com/JedWatson/react-select) from 5.6.1 to 5.7.0. - [Release notes](https://github.com/JedWatson/react-select/releases) - [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.6.1...react-select@5.7.0) --- updated-dependencies: - dependency-name: react-select dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump react-tabs from 5.1.0 to 6.0.0 (#1838) Bumps [react-tabs](https://github.com/reactjs/react-tabs) from 5.1.0 to 6.0.0. - [Release notes](https://github.com/reactjs/react-tabs/releases) - [Commits](https://github.com/reactjs/react-tabs/compare/v5.1.0...v6.0.0) --- updated-dependencies: - dependency-name: react-tabs dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix asset route (#1836) * updating the buy button message for free assets * Updating pricing text for compute and algorithms * Updating tests * Adding a seperate sentence about paying gas fees for network charges with free assets * Fixing tests Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro> * Showing hosting type in File Info (#1846) * Bump @storybook/addon-essentials from 6.5.13 to 6.5.15 (#1841) Bumps [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/addons/essentials) from 6.5.13 to 6.5.15. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/v6.5.15/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v6.5.15/addons/essentials) --- updated-dependencies: - dependency-name: "@storybook/addon-essentials" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @types/jest from 29.2.3 to 29.2.5 (#1840) Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.2.3 to 29.2.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest) --- updated-dependencies: - dependency-name: "@types/jest" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump prettier from 2.8.0 to 2.8.1 (#1837) Bumps [prettier](https://github.com/prettier/prettier) from 2.8.0 to 2.8.1. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/2.8.0...2.8.1) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump react-select from 5.6.1 to 5.7.0 (#1839) Bumps [react-select](https://github.com/JedWatson/react-select) from 5.6.1 to 5.7.0. - [Release notes](https://github.com/JedWatson/react-select/releases) - [Changelog](https://github.com/JedWatson/react-select/blob/master/docs/CHANGELOG.md) - [Commits](https://github.com/JedWatson/react-select/compare/react-select@5.6.1...react-select@5.7.0) --- updated-dependencies: - dependency-name: react-select dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump react-tabs from 5.1.0 to 6.0.0 (#1838) Bumps [react-tabs](https://github.com/reactjs/react-tabs) from 5.1.0 to 6.0.0. - [Release notes](https://github.com/reactjs/react-tabs/releases) - [Commits](https://github.com/reactjs/react-tabs/compare/v5.1.0...v6.0.0) --- updated-dependencies: - dependency-name: react-tabs dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix asset route (#1836) * Adding hosting type to the file info component * Writting smart contract hosting type across two lines Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro> * add initial price value for not supported price assets (#1851) * Fix compute basetoken issue (#1829) * fix lint * add dynamic provider fees * fixes * cleanup and more fixes * bump oceanlib to 2.6.0 * fix 404 styling (#1850) * cleanup and remove logs * fix smart contract check * restore headers * removed unnecessary message * fix error SM with headers * added headers as object to normalize file * fix input error in url file * adding methods to url storage type * test pipeline (now it's stuck) * cleanup * fix MethodInput test * fix arweave fileObj * Update index.tsx * fix provider url in form test * fixing test errors * fix test * fix help labels * cleanup leftovers * cleanup dependencies codemirror * remove any from normalizeFile * edit comment * remove any from oceanTheme --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Matthias Kretschmann <m@kretschmann.io> Co-authored-by: Jamie Hewitt <jamie@oceanprotocol.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: mihaisc <mihai.scarlat@smartcontrol.ro> Co-authored-by: Jamie Hewitt <jamie.hewitt15@gmail.com> Co-authored-by: Bogdan Fazakas <bogdan.fazakas@gmail.com>
This commit is contained in:
parent
223c8bf302
commit
b6f3bcbe68
@ -64,7 +64,72 @@
|
|||||||
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
||||||
"prominentHelp": true,
|
"prominentHelp": true,
|
||||||
"type": "files",
|
"type": "files",
|
||||||
"required": true
|
"required": true,
|
||||||
|
"innerFields": [
|
||||||
|
{
|
||||||
|
"value": "headers",
|
||||||
|
"title": "Headers",
|
||||||
|
"label": "Headers",
|
||||||
|
"placeholder_value": "Authorization",
|
||||||
|
"help": "This HEADERS will be stored encrypted after publishing.",
|
||||||
|
"type": "headers",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "graphql",
|
||||||
|
"title": "Graphql",
|
||||||
|
"label": "URL",
|
||||||
|
"placeholder": "e.g. http://172.15.0.15:8000/subgraphs/name/oceanprotocol/ocean-subgraph",
|
||||||
|
"help": "This URL will be stored encrypted after publishing.",
|
||||||
|
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
||||||
|
"prominentHelp": true,
|
||||||
|
"type": "files",
|
||||||
|
"required": true,
|
||||||
|
"headers": true,
|
||||||
|
"innerFields": [
|
||||||
|
{
|
||||||
|
"value": "headers",
|
||||||
|
"title": "Headers",
|
||||||
|
"label": "Headers",
|
||||||
|
"placeholder_value": "Authorization",
|
||||||
|
"help": "This HEADERS will be stored encrypted after publishing.",
|
||||||
|
"type": "headers",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "query",
|
||||||
|
"title": "Query",
|
||||||
|
"label": "Query",
|
||||||
|
"placeholder": "query{\n nfts(\n orderBy: createdTimestamp,\n orderDirection:desc\n ){\n id\n symbol\n createdTimestamp\n }\n}",
|
||||||
|
"help": "This QUERY will be stored encrypted after publishing.",
|
||||||
|
"type": "codeeditor",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "smartcontract",
|
||||||
|
"title": "Smart Contract",
|
||||||
|
"label": "Address",
|
||||||
|
"placeholder": "e.g. 0x8149276f275EEFAc110D74AFE8AFECEaeC7d1593",
|
||||||
|
"help": "This ADDRESS will be stored encrypted after publishing. **Please make sure that the endpoint is accessible over the internet and is not protected by a firewall or by credentials.**",
|
||||||
|
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
||||||
|
"prominentHelp": true,
|
||||||
|
"type": "files",
|
||||||
|
"required": true,
|
||||||
|
"innerFields": [
|
||||||
|
{
|
||||||
|
"value": "abi",
|
||||||
|
"title": "ABI",
|
||||||
|
"label": "ABI",
|
||||||
|
"placeholder": "{\n 'inputs': [],\n 'name': 'swapOceanFee',\n 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}],\n 'stateMutability': 'view',\n 'type': 'function'\n}",
|
||||||
|
"help": "This ABI will be stored encrypted after publishing.",
|
||||||
|
"type": "codeeditor",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sortOptions": false,
|
"sortOptions": false,
|
||||||
|
@ -138,7 +138,72 @@
|
|||||||
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
||||||
"prominentHelp": true,
|
"prominentHelp": true,
|
||||||
"type": "files",
|
"type": "files",
|
||||||
"required": true
|
"required": true,
|
||||||
|
"methods": true,
|
||||||
|
"innerFields": [
|
||||||
|
{
|
||||||
|
"value": "headers",
|
||||||
|
"title": "Headers",
|
||||||
|
"label": "Headers",
|
||||||
|
"placeholder_value": "Authorization",
|
||||||
|
"help": "This HEADERS will be stored encrypted after publishing.",
|
||||||
|
"type": "headers",
|
||||||
|
"required": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "graphql",
|
||||||
|
"title": "Graphql",
|
||||||
|
"label": "URL",
|
||||||
|
"placeholder": "e.g. http://172.15.0.15:8000/subgraphs/name/oceanprotocol/ocean-subgraph",
|
||||||
|
"help": "This URL will be stored encrypted after publishing.",
|
||||||
|
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
||||||
|
"prominentHelp": true,
|
||||||
|
"type": "files",
|
||||||
|
"required": true,
|
||||||
|
"innerFields": [
|
||||||
|
{
|
||||||
|
"value": "headers",
|
||||||
|
"title": "Headers",
|
||||||
|
"label": "Headers",
|
||||||
|
"placeholder_value": "Authorization",
|
||||||
|
"help": "This HEADERS will be stored encrypted after publishing.",
|
||||||
|
"type": "headers",
|
||||||
|
"required": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "query",
|
||||||
|
"title": "Query",
|
||||||
|
"label": "Query",
|
||||||
|
"placeholder": "query{\n nfts(\n orderBy: createdTimestamp,\n orderDirection:desc\n ){\n id\n symbol\n createdTimestamp\n }\n}",
|
||||||
|
"help": "This QUERY will be stored encrypted after publishing.",
|
||||||
|
"type": "codeeditor",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "smartcontract",
|
||||||
|
"title": "Smartcontract",
|
||||||
|
"label": "Address",
|
||||||
|
"placeholder": "e.g. 0x8149276f275EEFAc110D74AFE8AFECEaeC7d1593",
|
||||||
|
"help": "This ADDRESS will be stored encrypted after publishing.",
|
||||||
|
"computeHelp": "For a compute dataset, your file should match the file type required by the algorithm, and should not exceed 1 GB in file size. ",
|
||||||
|
"prominentHelp": true,
|
||||||
|
"type": "files",
|
||||||
|
"required": true,
|
||||||
|
"innerFields": [
|
||||||
|
{
|
||||||
|
"value": "abi",
|
||||||
|
"title": "ABI",
|
||||||
|
"label": "ABI",
|
||||||
|
"placeholder": "{\n 'inputs': [],\n 'name': 'swapOceanFee',\n 'outputs': [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}],\n 'stateMutability': 'view',\n 'type': 'function'\n}",
|
||||||
|
"help": "This ABI will be stored encrypted after publishing.",
|
||||||
|
"type": "codeeditor",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"sortOptions": false,
|
"sortOptions": false,
|
||||||
|
409
package-lock.json
generated
409
package-lock.json
generated
@ -10,6 +10,8 @@
|
|||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/language": "^6.3.1",
|
||||||
"@coingecko/cryptoformat": "^0.5.4",
|
"@coingecko/cryptoformat": "^0.5.4",
|
||||||
"@loadable/component": "^5.15.2",
|
"@loadable/component": "^5.15.2",
|
||||||
"@oceanprotocol/art": "^3.2.0",
|
"@oceanprotocol/art": "^3.2.0",
|
||||||
@ -17,6 +19,7 @@
|
|||||||
"@oceanprotocol/typographies": "^0.1.0",
|
"@oceanprotocol/typographies": "^0.1.0",
|
||||||
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
|
"@uiw/react-codemirror": "^4.19.5",
|
||||||
"@urql/exchange-refocus": "^1.0.0",
|
"@urql/exchange-refocus": "^1.0.0",
|
||||||
"@walletconnect/web3-provider": "^1.8.0",
|
"@walletconnect/web3-provider": "^1.8.0",
|
||||||
"axios": "^1.2.0",
|
"axios": "^1.2.0",
|
||||||
@ -58,7 +61,7 @@
|
|||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-essentials": "^6.5.15",
|
"@storybook/addon-essentials": "^6.5.13",
|
||||||
"@storybook/builder-webpack5": "^6.5.13",
|
"@storybook/builder-webpack5": "^6.5.13",
|
||||||
"@storybook/manager-webpack5": "^6.5.13",
|
"@storybook/manager-webpack5": "^6.5.13",
|
||||||
"@storybook/react": "^6.5.13",
|
"@storybook/react": "^6.5.13",
|
||||||
@ -76,6 +79,7 @@
|
|||||||
"@types/remove-markdown": "^0.3.1",
|
"@types/remove-markdown": "^0.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||||
"@typescript-eslint/parser": "^5.43.0",
|
"@typescript-eslint/parser": "^5.43.0",
|
||||||
|
"@uiw/codemirror-themes": "^4.19.1",
|
||||||
"apollo": "^2.34.0",
|
"apollo": "^2.34.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
@ -2039,6 +2043,102 @@
|
|||||||
"node": ">=0.1.95"
|
"node": ">=0.1.95"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@codemirror/autocomplete": {
|
||||||
|
"version": "6.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.0.tgz",
|
||||||
|
"integrity": "sha512-HLF2PnZAm1s4kGs30EiqKMgD7XsYaQ0XJnMR0rofEWQ5t5D60SfqpDIkIh1ze5tiEbyUWm8+VJ6W1/erVvBMIA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.6.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/commands": {
|
||||||
|
"version": "6.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.3.tgz",
|
||||||
|
"integrity": "sha512-wUw1+vb34Ultv0Q9m/OVB7yizGXgtoDbkI5f5ErM8bebwLyUYjicdhJTKhTvPTpgkv8dq/BK0lQ3K5pRf2DAJw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.2.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lang-json": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@lezer/json": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/language": {
|
||||||
|
"version": "6.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.3.2.tgz",
|
||||||
|
"integrity": "sha512-g42uHhOcEMAXjmozGG+rdom5UsbyfMxQFh7AbkeoaNImddL6Xt4cQDL0+JxmG7+as18rUAvZaqzP/TjsciVIrA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0",
|
||||||
|
"style-mod": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/lint": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/search": {
|
||||||
|
"version": "6.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.2.3.tgz",
|
||||||
|
"integrity": "sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/state": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA=="
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/theme-one-dark": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-AiTHtFRu8+vWT9wWUWDM+cog6ZwgivJogB1Tm/g40NIpLwph7AnmxrSzWfvJN5fBVufsuwBxecQCNmdcR5D7Aw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/highlight": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@codemirror/view": {
|
||||||
|
"version": "6.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.7.2.tgz",
|
||||||
|
"integrity": "sha512-HeK2GyycxceaQVyvYVYXmn1vUKYYBsHCcfGRSsFO+3fRRtwXx2STK0YiFBmiWx2vtU9gUAJgIUXUN8a0osI8Ng==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/state": "^6.1.4",
|
||||||
|
"style-mod": "^4.0.0",
|
||||||
|
"w3c-keyname": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@coingecko/cryptoformat": {
|
"node_modules/@coingecko/cryptoformat": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@coingecko/cryptoformat/-/cryptoformat-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@coingecko/cryptoformat/-/cryptoformat-0.5.4.tgz",
|
||||||
@ -4112,6 +4212,36 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@lezer/common": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng=="
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/highlight": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/json": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@lezer/lr": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz",
|
||||||
|
"integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@loadable/component": {
|
"node_modules/@loadable/component": {
|
||||||
"version": "5.15.2",
|
"version": "5.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz",
|
||||||
@ -17736,6 +17866,67 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@uiw/codemirror-extensions-basic-setup": {
|
||||||
|
"version": "4.19.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.19.5.tgz",
|
||||||
|
"integrity": "sha512-1zt7ZPJ01xKkSW/KDy0FZNga0bngN1fC594wCVG7FBi60ehfcAucpooQ+JSPScKXopxcb+ugPKZvVLzr9/OfzA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/commands": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/search": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@codemirror/autocomplete": ">=6.0.0",
|
||||||
|
"@codemirror/commands": ">=6.0.0",
|
||||||
|
"@codemirror/language": ">=6.0.0",
|
||||||
|
"@codemirror/lint": ">=6.0.0",
|
||||||
|
"@codemirror/search": ">=6.0.0",
|
||||||
|
"@codemirror/state": ">=6.0.0",
|
||||||
|
"@codemirror/view": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@uiw/codemirror-themes": {
|
||||||
|
"version": "4.19.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.19.5.tgz",
|
||||||
|
"integrity": "sha512-BWCTwQJaGiOc+nYqPLQDjmCtIojaCEKx2aO1bOTyGw0fisKwGw9Csll+bi9ujqA+vk6qYJmXI0P5K7kVs8fbdA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@codemirror/language": ">=6.0.0",
|
||||||
|
"@codemirror/state": ">=6.0.0",
|
||||||
|
"@codemirror/view": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@uiw/react-codemirror": {
|
||||||
|
"version": "4.19.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.19.5.tgz",
|
||||||
|
"integrity": "sha512-ZCHh8d7beXbF8/t7F1+yHht6A9Y6CdKeOkZq4A09lxJEnyTQrj1FMf2zvfaqc7K23KNjkTCtSlbqKKbVDgrWaw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/runtime": "^7.18.6",
|
||||||
|
"@codemirror/commands": "^6.1.0",
|
||||||
|
"@codemirror/state": "^6.1.1",
|
||||||
|
"@codemirror/theme-one-dark": "^6.0.0",
|
||||||
|
"@uiw/codemirror-extensions-basic-setup": "4.19.5",
|
||||||
|
"codemirror": "^6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@babel/runtime": ">=7.11.0",
|
||||||
|
"@codemirror/state": ">=6.0.0",
|
||||||
|
"@codemirror/theme-one-dark": ">=6.0.0",
|
||||||
|
"@codemirror/view": ">=6.0.0",
|
||||||
|
"codemirror": ">=6.0.0",
|
||||||
|
"react": ">=16.8.0",
|
||||||
|
"react-dom": ">=16.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@urql/core": {
|
"node_modules/@urql/core": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@urql/core/-/core-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@urql/core/-/core-3.0.3.tgz",
|
||||||
@ -22190,6 +22381,20 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/codemirror": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||||
|
"dependencies": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/commands": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/search": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/collapse-white-space": {
|
"node_modules/collapse-white-space": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz",
|
||||||
@ -23070,6 +23275,11 @@
|
|||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/crelt": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA=="
|
||||||
|
},
|
||||||
"node_modules/cross-env": {
|
"node_modules/cross-env": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||||
@ -43370,6 +43580,11 @@
|
|||||||
"webpack": "^4.0.0 || ^5.0.0"
|
"webpack": "^4.0.0 || ^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/style-mod": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw=="
|
||||||
|
},
|
||||||
"node_modules/style-to-object": {
|
"node_modules/style-to-object": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
|
||||||
@ -45470,6 +45685,11 @@
|
|||||||
"browser-process-hrtime": "^1.0.0"
|
"browser-process-hrtime": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/w3c-keyname": {
|
||||||
|
"version": "2.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
|
||||||
|
"integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg=="
|
||||||
|
},
|
||||||
"node_modules/w3c-xmlserializer": {
|
"node_modules/w3c-xmlserializer": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
|
||||||
@ -48351,6 +48571,96 @@
|
|||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@codemirror/autocomplete": {
|
||||||
|
"version": "6.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.0.tgz",
|
||||||
|
"integrity": "sha512-HLF2PnZAm1s4kGs30EiqKMgD7XsYaQ0XJnMR0rofEWQ5t5D60SfqpDIkIh1ze5tiEbyUWm8+VJ6W1/erVvBMIA==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.6.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@codemirror/commands": {
|
||||||
|
"version": "6.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.3.tgz",
|
||||||
|
"integrity": "sha512-wUw1+vb34Ultv0Q9m/OVB7yizGXgtoDbkI5f5ErM8bebwLyUYjicdhJTKhTvPTpgkv8dq/BK0lQ3K5pRf2DAJw==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.2.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@codemirror/lang-json": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@lezer/json": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@codemirror/language": {
|
||||||
|
"version": "6.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.3.2.tgz",
|
||||||
|
"integrity": "sha512-g42uHhOcEMAXjmozGG+rdom5UsbyfMxQFh7AbkeoaNImddL6Xt4cQDL0+JxmG7+as18rUAvZaqzP/TjsciVIrA==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/common": "^1.0.0",
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0",
|
||||||
|
"style-mod": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@codemirror/lint": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@codemirror/search": {
|
||||||
|
"version": "6.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.2.3.tgz",
|
||||||
|
"integrity": "sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"crelt": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@codemirror/state": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA=="
|
||||||
|
},
|
||||||
|
"@codemirror/theme-one-dark": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-AiTHtFRu8+vWT9wWUWDM+cog6ZwgivJogB1Tm/g40NIpLwph7AnmxrSzWfvJN5fBVufsuwBxecQCNmdcR5D7Aw==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0",
|
||||||
|
"@lezer/highlight": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@codemirror/view": {
|
||||||
|
"version": "6.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.7.2.tgz",
|
||||||
|
"integrity": "sha512-HeK2GyycxceaQVyvYVYXmn1vUKYYBsHCcfGRSsFO+3fRRtwXx2STK0YiFBmiWx2vtU9gUAJgIUXUN8a0osI8Ng==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/state": "^6.1.4",
|
||||||
|
"style-mod": "^4.0.0",
|
||||||
|
"w3c-keyname": "^2.2.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@coingecko/cryptoformat": {
|
"@coingecko/cryptoformat": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/@coingecko/cryptoformat/-/cryptoformat-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/@coingecko/cryptoformat/-/cryptoformat-0.5.4.tgz",
|
||||||
@ -49918,6 +50228,36 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@lezer/common": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng=="
|
||||||
|
},
|
||||||
|
"@lezer/highlight": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==",
|
||||||
|
"requires": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@lezer/json": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-zbAuUY09RBzCoCA3lJ1+ypKw5WSNvLqGMtasdW6HvVOqZoCpPr8eWrsGnOVWGKGn8Rh21FnrKRVlJXrGAVUqRw==",
|
||||||
|
"requires": {
|
||||||
|
"@lezer/highlight": "^1.0.0",
|
||||||
|
"@lezer/lr": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@lezer/lr": {
|
||||||
|
"version": "1.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz",
|
||||||
|
"integrity": "sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==",
|
||||||
|
"requires": {
|
||||||
|
"@lezer/common": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@loadable/component": {
|
"@loadable/component": {
|
||||||
"version": "5.15.2",
|
"version": "5.15.2",
|
||||||
"resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz",
|
"resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz",
|
||||||
@ -60444,6 +60784,44 @@
|
|||||||
"eslint-visitor-keys": "^3.3.0"
|
"eslint-visitor-keys": "^3.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@uiw/codemirror-extensions-basic-setup": {
|
||||||
|
"version": "4.19.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.19.5.tgz",
|
||||||
|
"integrity": "sha512-1zt7ZPJ01xKkSW/KDy0FZNga0bngN1fC594wCVG7FBi60ehfcAucpooQ+JSPScKXopxcb+ugPKZvVLzr9/OfzA==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/commands": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/search": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@uiw/codemirror-themes": {
|
||||||
|
"version": "4.19.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.19.5.tgz",
|
||||||
|
"integrity": "sha512-BWCTwQJaGiOc+nYqPLQDjmCtIojaCEKx2aO1bOTyGw0fisKwGw9Csll+bi9ujqA+vk6qYJmXI0P5K7kVs8fbdA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@uiw/react-codemirror": {
|
||||||
|
"version": "4.19.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.19.5.tgz",
|
||||||
|
"integrity": "sha512-ZCHh8d7beXbF8/t7F1+yHht6A9Y6CdKeOkZq4A09lxJEnyTQrj1FMf2zvfaqc7K23KNjkTCtSlbqKKbVDgrWaw==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/runtime": "^7.18.6",
|
||||||
|
"@codemirror/commands": "^6.1.0",
|
||||||
|
"@codemirror/state": "^6.1.1",
|
||||||
|
"@codemirror/theme-one-dark": "^6.0.0",
|
||||||
|
"@uiw/codemirror-extensions-basic-setup": "4.19.5",
|
||||||
|
"codemirror": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@urql/core": {
|
"@urql/core": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@urql/core/-/core-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@urql/core/-/core-3.0.3.tgz",
|
||||||
@ -64050,6 +64428,20 @@
|
|||||||
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
|
"integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"codemirror": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||||
|
"requires": {
|
||||||
|
"@codemirror/autocomplete": "^6.0.0",
|
||||||
|
"@codemirror/commands": "^6.0.0",
|
||||||
|
"@codemirror/language": "^6.0.0",
|
||||||
|
"@codemirror/lint": "^6.0.0",
|
||||||
|
"@codemirror/search": "^6.0.0",
|
||||||
|
"@codemirror/state": "^6.0.0",
|
||||||
|
"@codemirror/view": "^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"collapse-white-space": {
|
"collapse-white-space": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz",
|
||||||
@ -64778,6 +65170,11 @@
|
|||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"crelt": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA=="
|
||||||
|
},
|
||||||
"cross-env": {
|
"cross-env": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||||
@ -80572,6 +80969,11 @@
|
|||||||
"schema-utils": "^3.0.0"
|
"schema-utils": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"style-mod": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw=="
|
||||||
|
},
|
||||||
"style-to-object": {
|
"style-to-object": {
|
||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.3.0.tgz",
|
||||||
@ -82187,6 +82589,11 @@
|
|||||||
"browser-process-hrtime": "^1.0.0"
|
"browser-process-hrtime": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"w3c-keyname": {
|
||||||
|
"version": "2.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz",
|
||||||
|
"integrity": "sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg=="
|
||||||
|
},
|
||||||
"w3c-xmlserializer": {
|
"w3c-xmlserializer": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
"storybook:build": "cross-env NODE_ENV=test build-storybook"
|
"storybook:build": "cross-env NODE_ENV=test build-storybook"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@codemirror/lang-json": "^6.0.1",
|
||||||
|
"@codemirror/language": "^6.3.1",
|
||||||
"@coingecko/cryptoformat": "^0.5.4",
|
"@coingecko/cryptoformat": "^0.5.4",
|
||||||
"@loadable/component": "^5.15.2",
|
"@loadable/component": "^5.15.2",
|
||||||
"@oceanprotocol/art": "^3.2.0",
|
"@oceanprotocol/art": "^3.2.0",
|
||||||
@ -30,6 +32,7 @@
|
|||||||
"@oceanprotocol/typographies": "^0.1.0",
|
"@oceanprotocol/typographies": "^0.1.0",
|
||||||
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
"@oceanprotocol/use-dark-mode": "^2.4.3",
|
||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
|
"@uiw/react-codemirror": "^4.19.5",
|
||||||
"@urql/exchange-refocus": "^1.0.0",
|
"@urql/exchange-refocus": "^1.0.0",
|
||||||
"@walletconnect/web3-provider": "^1.8.0",
|
"@walletconnect/web3-provider": "^1.8.0",
|
||||||
"axios": "^1.2.0",
|
"axios": "^1.2.0",
|
||||||
@ -71,7 +74,7 @@
|
|||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@storybook/addon-essentials": "^6.5.15",
|
"@storybook/addon-essentials": "^6.5.13",
|
||||||
"@storybook/builder-webpack5": "^6.5.13",
|
"@storybook/builder-webpack5": "^6.5.13",
|
||||||
"@storybook/manager-webpack5": "^6.5.13",
|
"@storybook/manager-webpack5": "^6.5.13",
|
||||||
"@storybook/react": "^6.5.13",
|
"@storybook/react": "^6.5.13",
|
||||||
@ -89,6 +92,7 @@
|
|||||||
"@types/remove-markdown": "^0.3.1",
|
"@types/remove-markdown": "^0.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||||
"@typescript-eslint/parser": "^5.43.0",
|
"@typescript-eslint/parser": "^5.43.0",
|
||||||
|
"@uiw/codemirror-themes": "^4.19.1",
|
||||||
"apollo": "^2.34.0",
|
"apollo": "^2.34.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
|
36
src/@utils/codemirror.ts
Normal file
36
src/@utils/codemirror.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { createTheme } from '@uiw/codemirror-themes'
|
||||||
|
import { json } from '@codemirror/lang-json'
|
||||||
|
|
||||||
|
export function checkJson(text: string) {
|
||||||
|
try {
|
||||||
|
JSON.parse(text)
|
||||||
|
return true
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
declare type Theme = 'light' | 'dark'
|
||||||
|
export const oceanTheme = (marketTheme: Theme, field) => {
|
||||||
|
let textColor = 'var(--font-color-text)'
|
||||||
|
if (
|
||||||
|
(field.name === 'files[0].abi' ||
|
||||||
|
field.name === 'services[0].files[0].abi') &&
|
||||||
|
!checkJson(field.value)
|
||||||
|
) {
|
||||||
|
textColor = 'var(--brand-alert-red)'
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTheme({
|
||||||
|
theme: marketTheme,
|
||||||
|
settings: {
|
||||||
|
background: 'var(--background-content)',
|
||||||
|
foreground: textColor,
|
||||||
|
selection: 'var(--background-highlight)',
|
||||||
|
lineHighlight: 'transparent',
|
||||||
|
gutterBackground: 'var(--background-body)',
|
||||||
|
gutterForeground: 'var(--font-color-text)'
|
||||||
|
},
|
||||||
|
styles: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export const extensions = [json()]
|
@ -1,4 +1,20 @@
|
|||||||
import { Asset, DDO, Service } from '@oceanprotocol/lib'
|
import {
|
||||||
|
ComputeEditForm,
|
||||||
|
MetadataEditForm
|
||||||
|
} from '@components/Asset/Edit/_types'
|
||||||
|
import { FormPublishData } from '@components/Publish/_types'
|
||||||
|
import {
|
||||||
|
Arweave,
|
||||||
|
Asset,
|
||||||
|
DDO,
|
||||||
|
FileInfo,
|
||||||
|
GraphqlQuery,
|
||||||
|
Ipfs,
|
||||||
|
Service,
|
||||||
|
Smartcontract,
|
||||||
|
UrlFile
|
||||||
|
} from '@oceanprotocol/lib'
|
||||||
|
import { checkJson } from './codemirror'
|
||||||
|
|
||||||
export function isValidDid(did: string): boolean {
|
export function isValidDid(did: string): boolean {
|
||||||
const regex = /did:op:[A-Za-z0-9]{64}/
|
const regex = /did:op:[A-Za-z0-9]{64}/
|
||||||
@ -70,3 +86,105 @@ export function secondsToString(numberOfSeconds: number): string {
|
|||||||
? `${seconds} second${numberEnding(seconds)}`
|
? `${seconds} second${numberEnding(seconds)}`
|
||||||
: 'less than a second'
|
: 'less than a second'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is required to make it work properly for preview/publish/edit/debug.
|
||||||
|
// TODO: find a way to only have FileInfo interface instead of FileExtended
|
||||||
|
interface FileExtended extends FileInfo {
|
||||||
|
url?: string
|
||||||
|
query?: string
|
||||||
|
transactionId?: string
|
||||||
|
address?: string
|
||||||
|
abi?: string
|
||||||
|
headers?: { key: string; value: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function normalizeFile(
|
||||||
|
storageType: string,
|
||||||
|
file: FileExtended,
|
||||||
|
chainId: number
|
||||||
|
) {
|
||||||
|
let fileObj
|
||||||
|
const headersProvider = {}
|
||||||
|
const headers = file[0]?.headers || file?.headers
|
||||||
|
if (headers && headers.length > 0) {
|
||||||
|
headers.map((el) => {
|
||||||
|
headersProvider[el.key] = el.value
|
||||||
|
return el
|
||||||
|
})
|
||||||
|
}
|
||||||
|
switch (storageType) {
|
||||||
|
case 'ipfs': {
|
||||||
|
fileObj = {
|
||||||
|
type: storageType,
|
||||||
|
hash: file[0]?.url || file?.url
|
||||||
|
} as Ipfs
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'arweave': {
|
||||||
|
fileObj = {
|
||||||
|
type: storageType,
|
||||||
|
transactionId:
|
||||||
|
file[0]?.url ||
|
||||||
|
file?.url ||
|
||||||
|
file[0]?.transactionId ||
|
||||||
|
file?.transactionId
|
||||||
|
} as Arweave
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'graphql': {
|
||||||
|
fileObj = {
|
||||||
|
type: storageType,
|
||||||
|
url: file[0]?.url || file?.url,
|
||||||
|
query: file[0]?.query || file?.query,
|
||||||
|
headers: headersProvider
|
||||||
|
} as GraphqlQuery
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'smartcontract': {
|
||||||
|
// clean obj
|
||||||
|
fileObj = {
|
||||||
|
chainId,
|
||||||
|
type: storageType,
|
||||||
|
address: file[0]?.address || file?.address || file[0]?.url || file?.url,
|
||||||
|
abi: checkJson(file[0]?.abi || file?.abi)
|
||||||
|
? JSON.parse(file[0]?.abi || file?.abi)
|
||||||
|
: file[0]?.abi || file?.abi
|
||||||
|
} as Smartcontract
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
fileObj = {
|
||||||
|
type: 'url',
|
||||||
|
index: 0,
|
||||||
|
url: file ? file[0]?.url || file?.url : null,
|
||||||
|
headers: headersProvider,
|
||||||
|
method: file.method
|
||||||
|
} as UrlFile
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fileObj
|
||||||
|
}
|
||||||
|
|
||||||
|
export function previewDebugPatch(
|
||||||
|
values: FormPublishData | Partial<MetadataEditForm> | ComputeEditForm,
|
||||||
|
chainId: number
|
||||||
|
) {
|
||||||
|
// handle file's object property dynamically
|
||||||
|
// without braking Yup and type validation
|
||||||
|
const buildValuesPreview = JSON.parse(JSON.stringify(values))
|
||||||
|
|
||||||
|
// fallback for edit mode under "edit compute settings"
|
||||||
|
if (!buildValuesPreview.services) return buildValuesPreview
|
||||||
|
|
||||||
|
const valuesService = buildValuesPreview.services
|
||||||
|
? buildValuesPreview.services[0]
|
||||||
|
: buildValuesPreview
|
||||||
|
valuesService.files[0] = normalizeFile(
|
||||||
|
valuesService.files[0].type,
|
||||||
|
valuesService.files[0],
|
||||||
|
chainId
|
||||||
|
)
|
||||||
|
|
||||||
|
return buildValuesPreview
|
||||||
|
}
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
consumeMarketFixedSwapFee
|
consumeMarketFixedSwapFee
|
||||||
} from '../../app.config'
|
} from '../../app.config'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
import { getEncryptedFiles, getFileInfo } from './provider'
|
||||||
|
|
||||||
async function initializeProvider(
|
async function initializeProvider(
|
||||||
asset: AssetExtended,
|
asset: AssetExtended,
|
||||||
@ -63,6 +64,11 @@ export async function order(
|
|||||||
const datatoken = new Datatoken(web3)
|
const datatoken = new Datatoken(web3)
|
||||||
const config = getOceanConfig(asset.chainId)
|
const config = getOceanConfig(asset.chainId)
|
||||||
|
|
||||||
|
const filesEncrypted = await getEncryptedFiles(
|
||||||
|
asset.services[0].files,
|
||||||
|
asset.services[0].serviceEndpoint
|
||||||
|
)
|
||||||
|
|
||||||
const initializeData = await initializeProvider(
|
const initializeData = await initializeProvider(
|
||||||
asset,
|
asset,
|
||||||
accountId,
|
accountId,
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Arweave,
|
Arweave,
|
||||||
|
GraphqlQuery,
|
||||||
|
Smartcontract,
|
||||||
ComputeAlgorithm,
|
ComputeAlgorithm,
|
||||||
ComputeAsset,
|
ComputeAsset,
|
||||||
ComputeEnvironment,
|
ComputeEnvironment,
|
||||||
@ -11,9 +13,24 @@ import {
|
|||||||
ProviderInstance,
|
ProviderInstance,
|
||||||
UrlFile
|
UrlFile
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
|
import { QueryHeader } from '@shared/FormInput/InputElement/Headers'
|
||||||
import Web3 from 'web3'
|
import Web3 from 'web3'
|
||||||
|
import { AbiItem } from 'web3-utils/types'
|
||||||
import { getValidUntilTime } from './compute'
|
import { getValidUntilTime } from './compute'
|
||||||
|
|
||||||
|
export async function getEncryptedFiles(
|
||||||
|
files: any,
|
||||||
|
providerUrl: string
|
||||||
|
): Promise<string> {
|
||||||
|
try {
|
||||||
|
// https://github.com/oceanprotocol/provider/blob/v4main/API.md#encrypt-endpoint
|
||||||
|
const response = await ProviderInstance.encrypt(files, providerUrl)
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing json: ' + error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function initializeProviderForCompute(
|
export async function initializeProviderForCompute(
|
||||||
dataset: AssetExtended,
|
dataset: AssetExtended,
|
||||||
algorithm: AssetExtended,
|
algorithm: AssetExtended,
|
||||||
@ -38,6 +55,11 @@ export async function initializeProviderForCompute(
|
|||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const filesEncrypted = await getEncryptedFiles(
|
||||||
|
dataset.services[0].files,
|
||||||
|
dataset.services[0].serviceEndpoint
|
||||||
|
)
|
||||||
|
|
||||||
return await ProviderInstance.initializeCompute(
|
return await ProviderInstance.initializeCompute(
|
||||||
[computeAsset],
|
[computeAsset],
|
||||||
computeAlgo,
|
computeAlgo,
|
||||||
@ -52,20 +74,6 @@ export async function initializeProviderForCompute(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Why do we have these one line functions ?!?!?!
|
|
||||||
export async function getEncryptedFiles(
|
|
||||||
files: any,
|
|
||||||
providerUrl: string
|
|
||||||
): Promise<string> {
|
|
||||||
try {
|
|
||||||
// https://github.com/oceanprotocol/provider/blob/v4main/API.md#encrypt-endpoint
|
|
||||||
const response = await ProviderInstance.encrypt(files, providerUrl)
|
|
||||||
return response
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error parsing json: ' + error.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getFileDidInfo(
|
export async function getFileDidInfo(
|
||||||
did: string,
|
did: string,
|
||||||
serviceId: string,
|
serviceId: string,
|
||||||
@ -88,36 +96,73 @@ export async function getFileDidInfo(
|
|||||||
export async function getFileInfo(
|
export async function getFileInfo(
|
||||||
file: string,
|
file: string,
|
||||||
providerUrl: string,
|
providerUrl: string,
|
||||||
storageType: string
|
storageType: string,
|
||||||
|
query?: string,
|
||||||
|
headers?: QueryHeader[],
|
||||||
|
abi?: string,
|
||||||
|
chainId?: number,
|
||||||
|
method?: string
|
||||||
): Promise<FileInfo[]> {
|
): Promise<FileInfo[]> {
|
||||||
try {
|
try {
|
||||||
let response
|
let response
|
||||||
|
const headersProvider = {}
|
||||||
|
if (headers?.length > 0) {
|
||||||
|
headers.map((el) => {
|
||||||
|
headersProvider[el.key] = el.value
|
||||||
|
return el
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
switch (storageType) {
|
switch (storageType) {
|
||||||
case 'ipfs': {
|
case 'ipfs': {
|
||||||
const fileIPFS: Ipfs = {
|
const fileIPFS: Ipfs = {
|
||||||
type: 'ipfs',
|
type: storageType,
|
||||||
hash: file
|
hash: file
|
||||||
}
|
}
|
||||||
|
|
||||||
response = await ProviderInstance.getFileInfo(fileIPFS, providerUrl)
|
response = await ProviderInstance.getFileInfo(fileIPFS, providerUrl)
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'arweave': {
|
case 'arweave': {
|
||||||
const fileArweave: Arweave = {
|
const fileArweave: Arweave = {
|
||||||
type: 'arweave',
|
type: storageType,
|
||||||
transactionId: file
|
transactionId: file
|
||||||
}
|
}
|
||||||
|
|
||||||
response = await ProviderInstance.getFileInfo(fileArweave, providerUrl)
|
response = await ProviderInstance.getFileInfo(fileArweave, providerUrl)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'graphql': {
|
||||||
|
const fileGraphql: GraphqlQuery = {
|
||||||
|
type: storageType,
|
||||||
|
url: file,
|
||||||
|
headers: headersProvider,
|
||||||
|
query
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await ProviderInstance.getFileInfo(fileGraphql, providerUrl)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'smartcontract': {
|
||||||
|
// clean obj
|
||||||
|
const fileSmartContract: Smartcontract = {
|
||||||
|
chainId,
|
||||||
|
type: storageType,
|
||||||
|
address: file,
|
||||||
|
abi: JSON.parse(abi) as AbiItem
|
||||||
|
}
|
||||||
|
|
||||||
|
response = await ProviderInstance.getFileInfo(
|
||||||
|
fileSmartContract,
|
||||||
|
providerUrl
|
||||||
|
)
|
||||||
|
break
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
const fileUrl: UrlFile = {
|
const fileUrl: UrlFile = {
|
||||||
type: 'url',
|
type: 'url',
|
||||||
index: 0,
|
index: 0,
|
||||||
url: file,
|
url: file,
|
||||||
method: 'get'
|
headers: headersProvider,
|
||||||
|
method
|
||||||
}
|
}
|
||||||
|
|
||||||
response = await ProviderInstance.getFileInfo(fileUrl, providerUrl)
|
response = await ProviderInstance.getFileInfo(fileUrl, providerUrl)
|
||||||
|
@ -9,6 +9,7 @@ export function sanitizeUrl(url: string) {
|
|||||||
// check if the url is a google domain
|
// check if the url is a google domain
|
||||||
export const isGoogleUrl = (url: string): boolean => {
|
export const isGoogleUrl = (url: string): boolean => {
|
||||||
if (!url || !isUrl(url)) return
|
if (!url || !isUrl(url)) return
|
||||||
|
|
||||||
const googleUrl = new URL(url)
|
const googleUrl = new URL(url)
|
||||||
return googleUrl.hostname.endsWith('google.com')
|
return googleUrl.hostname.endsWith('google.com')
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { isCID } from '@utils/ipfs'
|
import { isCID } from '@utils/ipfs'
|
||||||
import isUrl from 'is-url-superb'
|
import isUrl from 'is-url-superb'
|
||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
|
import web3 from 'web3'
|
||||||
import { isGoogleUrl } from './url/index'
|
import { isGoogleUrl } from './url/index'
|
||||||
|
|
||||||
export function testLinks(isEdit?: boolean) {
|
export function testLinks(isEdit?: boolean) {
|
||||||
@ -15,16 +16,18 @@ export function testLinks(isEdit?: boolean) {
|
|||||||
validField = true
|
validField = true
|
||||||
break
|
break
|
||||||
case 'url':
|
case 'url':
|
||||||
|
case 'graphql':
|
||||||
validField = isUrl(value?.toString() || '')
|
validField = isUrl(value?.toString() || '')
|
||||||
// if we're in publish, the field must be valid
|
// if we're in publish, the field must be valid
|
||||||
if (!validField) {
|
if (!validField) {
|
||||||
validField = false
|
validField = false
|
||||||
errorMessage = 'Must be a valid url.'
|
errorMessage = 'Must be a valid url.'
|
||||||
}
|
}
|
||||||
// we allow submit if we're in the edit page and the field is empty
|
// we allow submit on empty sample field
|
||||||
if (
|
if (
|
||||||
(!value?.toString() && isEdit) ||
|
!value?.toString() &&
|
||||||
(!value?.toString() && context.path === 'services[0].links[0].url')
|
(context.path === 'links[0].url' ||
|
||||||
|
context.path === 'services[0].links[0].url')
|
||||||
) {
|
) {
|
||||||
validField = true
|
validField = true
|
||||||
}
|
}
|
||||||
@ -40,11 +43,17 @@ export function testLinks(isEdit?: boolean) {
|
|||||||
errorMessage = !value?.toString() ? 'CID required.' : 'CID not valid.'
|
errorMessage = !value?.toString() ? 'CID required.' : 'CID not valid.'
|
||||||
break
|
break
|
||||||
case 'arweave':
|
case 'arweave':
|
||||||
validField = !value?.toString().includes('http')
|
validField = value && !value?.toString().includes('http')
|
||||||
errorMessage = !value?.toString()
|
errorMessage = !value?.toString()
|
||||||
? 'Transaction ID required.'
|
? 'Transaction ID required.'
|
||||||
: 'Transaction ID not valid.'
|
: 'Transaction ID not valid.'
|
||||||
break
|
break
|
||||||
|
case 'smartcontract':
|
||||||
|
validField = web3.utils.isAddress(value?.toString())
|
||||||
|
errorMessage = !value?.toString()
|
||||||
|
? 'Address required.'
|
||||||
|
: 'Address not valid.'
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validField) {
|
if (!validField) {
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
.textblock {
|
||||||
|
margin-top: var(--spacer);
|
||||||
|
}
|
@ -20,7 +20,7 @@ const mockMeta = {
|
|||||||
value: ''
|
value: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const mockField = {
|
const mockFieldUrl = {
|
||||||
value: 'https://hello.com',
|
value: 'https://hello.com',
|
||||||
checked: false,
|
checked: false,
|
||||||
onChange: jest.fn(),
|
onChange: jest.fn(),
|
||||||
@ -28,6 +28,39 @@ const mockField = {
|
|||||||
name: 'url'
|
name: 'url'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mockFieldIpfs = {
|
||||||
|
value: 'bafkreicxccbk4blsx5qtovqfgsuutxjxom47dvyzyz3asi2ggjg5ipwlc4',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'ipfs'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockFieldArwave = {
|
||||||
|
value: 'T6NL8Zc0LCbT3bF9HacAGQC4W0_hW7b3tXbm8OtWtlA',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'arweave'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockFieldGraphQL = {
|
||||||
|
value:
|
||||||
|
'https://v4.subgraph.mumbai.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'graphql'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockFieldSM = {
|
||||||
|
value: '0x564955E9d25B49afE5Abd66966Ab4Bc9Ad55Fedb',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'smartcontract'
|
||||||
|
}
|
||||||
|
|
||||||
const mockHelpers = {
|
const mockHelpers = {
|
||||||
setValue: jest.fn(),
|
setValue: jest.fn(),
|
||||||
setTouched: jest.fn()
|
setTouched: jest.fn()
|
||||||
@ -35,7 +68,13 @@ const mockHelpers = {
|
|||||||
|
|
||||||
const mockForm = {
|
const mockForm = {
|
||||||
values: {
|
values: {
|
||||||
services: [{ providerUrl: 'https://provider.url' }]
|
services: [
|
||||||
|
{
|
||||||
|
providerUrl: {
|
||||||
|
url: 'https://v4.provider.mainnet.oceanprotocol.com'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
errors: {},
|
errors: {},
|
||||||
touched: {},
|
touched: {},
|
||||||
@ -46,8 +85,12 @@ const mockForm = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('@shared/FormInput/InputElement/FilesInput', () => {
|
describe('@shared/FormInput/InputElement/FilesInput', () => {
|
||||||
it('renders without crashing', async () => {
|
it('renders URL without crashing', async () => {
|
||||||
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta, mockHelpers])
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
mockFieldUrl,
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
;(checkValidProvider as jest.Mock).mockReturnValue(true)
|
;(checkValidProvider as jest.Mock).mockReturnValue(true)
|
||||||
;(getFileInfo as jest.Mock).mockReturnValue([
|
;(getFileInfo as jest.Mock).mockReturnValue([
|
||||||
{
|
{
|
||||||
@ -59,7 +102,7 @@ describe('@shared/FormInput/InputElement/FilesInput', () => {
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
render(<FilesInput form={mockForm} field={mockField} {...props} />)
|
render(<FilesInput form={mockForm} field={mockFieldUrl} {...props} />)
|
||||||
expect(screen.getByText('Validate')).toBeInTheDocument()
|
expect(screen.getByText('Validate')).toBeInTheDocument()
|
||||||
fireEvent.click(screen.getByText('Validate'))
|
fireEvent.click(screen.getByText('Validate'))
|
||||||
|
|
||||||
@ -85,7 +128,7 @@ describe('@shared/FormInput/InputElement/FilesInput', () => {
|
|||||||
mockMeta,
|
mockMeta,
|
||||||
mockHelpers
|
mockHelpers
|
||||||
])
|
])
|
||||||
render(<FilesInput {...props} field={mockField} />)
|
render(<FilesInput {...props} field={mockFieldUrl} />)
|
||||||
expect(screen.getByText('https://hello.com')).toBeInTheDocument()
|
expect(screen.getByText('https://hello.com')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -106,7 +149,7 @@ describe('@shared/FormInput/InputElement/FilesInput', () => {
|
|||||||
mockMeta,
|
mockMeta,
|
||||||
mockHelpers
|
mockHelpers
|
||||||
])
|
])
|
||||||
render(<FilesInput {...props} field={mockField} />)
|
render(<FilesInput {...props} field={mockFieldIpfs} />)
|
||||||
expect(screen.getByText('✓ File confirmed')).toBeInTheDocument()
|
expect(screen.getByText('✓ File confirmed')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -127,28 +170,10 @@ describe('@shared/FormInput/InputElement/FilesInput', () => {
|
|||||||
mockMeta,
|
mockMeta,
|
||||||
mockHelpers
|
mockHelpers
|
||||||
])
|
])
|
||||||
render(<FilesInput {...props} field={mockField} />)
|
render(<FilesInput {...props} field={mockFieldArwave} />)
|
||||||
expect(screen.getByText('✓ File confirmed')).toBeInTheDocument()
|
expect(screen.getByText('✓ File confirmed')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('renders fileinfo without contentType', () => {
|
|
||||||
;(useField as jest.Mock).mockReturnValue([
|
|
||||||
{
|
|
||||||
value: [
|
|
||||||
{
|
|
||||||
valid: true,
|
|
||||||
url: 'https://hello.com',
|
|
||||||
type: 'url',
|
|
||||||
contentLength: 100
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
mockMeta,
|
|
||||||
mockHelpers
|
|
||||||
])
|
|
||||||
render(<FilesInput {...props} field={mockField} />)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('renders fileinfo placeholder when hideUrl is passed', () => {
|
it('renders fileinfo placeholder when hideUrl is passed', () => {
|
||||||
;(useField as jest.Mock).mockReturnValue([
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
{
|
{
|
||||||
@ -163,9 +188,60 @@ describe('@shared/FormInput/InputElement/FilesInput', () => {
|
|||||||
mockMeta,
|
mockMeta,
|
||||||
mockHelpers
|
mockHelpers
|
||||||
])
|
])
|
||||||
render(<FilesInput {...props} field={mockField} />)
|
render(<FilesInput {...props} field={mockFieldUrl} />)
|
||||||
expect(
|
expect(
|
||||||
screen.getByText('https://oceanprotocol/placeholder')
|
screen.getByText('https://oceanprotocol/placeholder')
|
||||||
).toBeInTheDocument()
|
).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('renders fileinfo when graphql is valid', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'graphql',
|
||||||
|
valid: true,
|
||||||
|
url: 'https://v4.subgraph.mumbai.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph',
|
||||||
|
query:
|
||||||
|
'query{\n nfts(orderBy: createdTimestamp,orderDirection:desc){\n id\n symbol\n createdTimestamp\n }\n }',
|
||||||
|
checksum: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
|
render(<FilesInput {...props} field={mockFieldGraphQL} />)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders fileinfo when smart contract is valid', () => {
|
||||||
|
;(useField as jest.Mock).mockReturnValue([
|
||||||
|
{
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
chainId: 80001,
|
||||||
|
type: 'smartcontract',
|
||||||
|
address: '0x564955E9d25B49afE5Abd66966Ab4Bc9Ad55Fedb',
|
||||||
|
abi: {
|
||||||
|
inputs: [],
|
||||||
|
name: 'swapOceanFee',
|
||||||
|
outputs: [
|
||||||
|
{
|
||||||
|
internalType: 'uint256',
|
||||||
|
name: '',
|
||||||
|
type: 'uint256'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
stateMutability: 'view',
|
||||||
|
type: 'function'
|
||||||
|
},
|
||||||
|
valid: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
mockMeta,
|
||||||
|
mockHelpers
|
||||||
|
])
|
||||||
|
render(<FilesInput {...props} field={mockFieldSM} />)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,23 +1,37 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import { useField } from 'formik'
|
import { Field, useField } from 'formik'
|
||||||
import FileInfo from './Info'
|
import FileInfoDetails from './Info'
|
||||||
import UrlInput from '../URLInput'
|
import UrlInput from '../URLInput'
|
||||||
import { InputProps } from '@shared/FormInput'
|
import Input, { InputProps } from '@shared/FormInput'
|
||||||
import { getFileInfo, checkValidProvider } from '@utils/provider'
|
import { getFileInfo, checkValidProvider } from '@utils/provider'
|
||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
import { LoggerInstance, FileInfo } from '@oceanprotocol/lib'
|
||||||
import { useAsset } from '@context/Asset'
|
import { useAsset } from '@context/Asset'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
import InputHeaders from '../Headers'
|
||||||
|
import Button from '@shared/atoms/Button'
|
||||||
|
import Loader from '@shared/atoms/Loader'
|
||||||
|
import { checkJson } from '@utils/codemirror'
|
||||||
import { isGoogleUrl } from '@utils/url/index'
|
import { isGoogleUrl } from '@utils/url/index'
|
||||||
|
import isUrl from 'is-url-superb'
|
||||||
|
import MethodInput from '../MethodInput'
|
||||||
|
|
||||||
export default function FilesInput(props: InputProps): ReactElement {
|
export default function FilesInput(props: InputProps): ReactElement {
|
||||||
const [field, meta, helpers] = useField(props.name)
|
const [field, meta, helpers] = useField(props.name)
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
|
const [disabledButton, setDisabledButton] = useState(true)
|
||||||
const { asset } = useAsset()
|
const { asset } = useAsset()
|
||||||
|
const { chainId } = useWeb3()
|
||||||
|
|
||||||
const providerUrl = props.form?.values?.services
|
const providerUrl = props.form?.values?.services
|
||||||
? props.form?.values?.services[0].providerUrl.url
|
? props.form?.values?.services[0].providerUrl.url
|
||||||
: asset.services[0].serviceEndpoint
|
: asset.services[0].serviceEndpoint
|
||||||
|
|
||||||
const storageType = field.value[0].type
|
const storageType = field.value[0].type
|
||||||
|
const query = field.value[0].query || undefined
|
||||||
|
const abi = field.value[0].abi || undefined
|
||||||
|
const headers = field.value[0].headers || undefined
|
||||||
|
const method = field.value[0].method || undefined
|
||||||
|
|
||||||
async function handleValidation(e: React.SyntheticEvent, url: string) {
|
async function handleValidation(e: React.SyntheticEvent, url: string) {
|
||||||
// File example 'https://oceanprotocol.com/tech-whitepaper.pdf'
|
// File example 'https://oceanprotocol.com/tech-whitepaper.pdf'
|
||||||
@ -26,8 +40,7 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
try {
|
try {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
|
|
||||||
// TODO: handled on provider
|
if (isUrl(url) && isGoogleUrl(url)) {
|
||||||
if (isGoogleUrl(url)) {
|
|
||||||
throw Error(
|
throw Error(
|
||||||
'Google Drive is not a supported hosting service. Please use an alternative.'
|
'Google Drive is not a supported hosting service. Please use an alternative.'
|
||||||
)
|
)
|
||||||
@ -40,17 +53,39 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
'✗ Provider cannot be reached, please check status.oceanprotocol.com and try again later.'
|
'✗ Provider cannot be reached, please check status.oceanprotocol.com and try again later.'
|
||||||
)
|
)
|
||||||
|
|
||||||
const checkedFile = await getFileInfo(url, providerUrl, storageType)
|
const checkedFile = await getFileInfo(
|
||||||
|
url,
|
||||||
|
providerUrl,
|
||||||
|
storageType,
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
abi,
|
||||||
|
chainId,
|
||||||
|
method
|
||||||
|
)
|
||||||
|
|
||||||
// error if something's not right from response
|
// error if something's not right from response
|
||||||
if (!checkedFile)
|
if (!checkedFile)
|
||||||
throw Error('Could not fetch file info. Is your network down?')
|
throw Error('Could not fetch file info. Is your network down?')
|
||||||
|
|
||||||
if (checkedFile[0].valid === false)
|
if (checkedFile[0].valid === false)
|
||||||
throw Error('✗ No valid file detected. Check your URL and try again.')
|
throw Error(
|
||||||
|
`✗ No valid file detected. Check your ${props.label} and details, and try again.`
|
||||||
|
)
|
||||||
|
|
||||||
// if all good, add file to formik state
|
// if all good, add file to formik state
|
||||||
helpers.setValue([{ url, type: storageType, ...checkedFile[0] }])
|
helpers.setValue([
|
||||||
|
{
|
||||||
|
url,
|
||||||
|
providerUrl,
|
||||||
|
type: storageType,
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
abi,
|
||||||
|
chainId,
|
||||||
|
...checkedFile[0]
|
||||||
|
}
|
||||||
|
])
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
props.form.setFieldError(`${field.name}[0].url`, error.message)
|
props.form.setFieldError(`${field.name}[0].url`, error.message)
|
||||||
LoggerInstance.error(error.message)
|
LoggerInstance.error(error.message)
|
||||||
@ -59,6 +94,10 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleMethod(method: string) {
|
||||||
|
helpers.setValue([{ ...props.value[0], method }])
|
||||||
|
}
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
helpers.setTouched(false)
|
helpers.setTouched(false)
|
||||||
helpers.setValue([
|
helpers.setValue([
|
||||||
@ -66,21 +105,95 @@ export default function FilesInput(props: InputProps): ReactElement {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
storageType === 'graphql' && setDisabledButton(!providerUrl || !query)
|
||||||
|
|
||||||
|
storageType === 'smartcontract' &&
|
||||||
|
setDisabledButton(!providerUrl || !abi || !checkJson(abi))
|
||||||
|
|
||||||
|
storageType === 'url' && setDisabledButton(!providerUrl)
|
||||||
|
|
||||||
|
if (meta.error?.length > 0) {
|
||||||
|
const { url } = meta.error[0] as unknown as FileInfo
|
||||||
|
url && setDisabledButton(true)
|
||||||
|
}
|
||||||
|
}, [storageType, providerUrl, headers, query, abi, meta])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{field?.value?.[0]?.valid === true ||
|
{field?.value?.[0]?.valid === true ||
|
||||||
field?.value?.[0]?.type === 'hidden' ? (
|
field?.value?.[0]?.type === 'hidden' ? (
|
||||||
<FileInfo file={field.value[0]} handleClose={handleClose} />
|
<FileInfoDetails file={field.value[0]} handleClose={handleClose} />
|
||||||
) : (
|
) : (
|
||||||
<UrlInput
|
<>
|
||||||
submitText="Validate"
|
{props.methods && storageType === 'url' ? (
|
||||||
{...props}
|
<MethodInput
|
||||||
name={`${field.name}[0].url`}
|
{...props}
|
||||||
isLoading={isLoading}
|
name={`${field.name}[0].url`}
|
||||||
checkUrl={true}
|
isLoading={isLoading}
|
||||||
handleButtonClick={handleValidation}
|
checkUrl={true}
|
||||||
storageType={storageType}
|
handleButtonClick={handleMethod}
|
||||||
/>
|
storageType={storageType}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<UrlInput
|
||||||
|
submitText="Validate"
|
||||||
|
{...props}
|
||||||
|
name={`${field.name}[0].url`}
|
||||||
|
isLoading={isLoading}
|
||||||
|
hideButton={
|
||||||
|
storageType === 'graphql' || storageType === 'smartcontract'
|
||||||
|
}
|
||||||
|
checkUrl={true}
|
||||||
|
handleButtonClick={handleValidation}
|
||||||
|
storageType={storageType}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{props.innerFields && (
|
||||||
|
<>
|
||||||
|
<div className={`${styles.textblock}`}>
|
||||||
|
{props.innerFields &&
|
||||||
|
props.innerFields.map((innerField: any, i: number) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Field
|
||||||
|
key={i}
|
||||||
|
component={
|
||||||
|
innerField.type === 'headers' ? InputHeaders : Input
|
||||||
|
}
|
||||||
|
{...innerField}
|
||||||
|
name={`${field.name}[0].${innerField.value}`}
|
||||||
|
value={field.value[0][innerField.value]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
onClick={(e: React.SyntheticEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
handleValidation(e, field.value[0].url)
|
||||||
|
}}
|
||||||
|
disabled={disabledButton}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<Loader />
|
||||||
|
) : (
|
||||||
|
`submit ${
|
||||||
|
storageType === 'graphql'
|
||||||
|
? 'query'
|
||||||
|
: storageType === 'smartcontract'
|
||||||
|
? 'abi'
|
||||||
|
: 'url'
|
||||||
|
}`
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
.headersContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0 calc(var(--spacer) / 4);
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headersAddedContainer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0 calc(var(--spacer) / 4);
|
||||||
|
margin-bottom: var(--spacer);
|
||||||
|
}
|
135
src/components/@shared/FormInput/InputElement/Headers/index.tsx
Normal file
135
src/components/@shared/FormInput/InputElement/Headers/index.tsx
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import React, { ChangeEvent, ReactElement, useEffect, useState } from 'react'
|
||||||
|
import InputElement from '../../InputElement'
|
||||||
|
import Label from '../../Label'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import Tooltip from '@shared/atoms/Tooltip'
|
||||||
|
import Markdown from '@shared/Markdown'
|
||||||
|
import Button from '@shared/atoms/Button'
|
||||||
|
import { InputProps } from '@shared/FormInput'
|
||||||
|
|
||||||
|
export interface QueryHeader {
|
||||||
|
key: string
|
||||||
|
value: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function InputHeaders(props: InputProps): ReactElement {
|
||||||
|
const { label, help, prominentHelp, form, field } = props
|
||||||
|
|
||||||
|
const [currentKey, setCurrentKey] = useState('')
|
||||||
|
const [currentValue, setCurrentValue] = useState('')
|
||||||
|
const [disabledButton, setDisabledButton] = useState(true)
|
||||||
|
|
||||||
|
const [headers, setHeaders] = useState([] as QueryHeader[])
|
||||||
|
|
||||||
|
const addHeader = () => {
|
||||||
|
setHeaders((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
key: currentKey,
|
||||||
|
value: currentValue
|
||||||
|
}
|
||||||
|
])
|
||||||
|
setCurrentKey('')
|
||||||
|
setCurrentValue('')
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeHeader = (i: number) => {
|
||||||
|
const newHeaders = headers.filter((header, index) => index !== i)
|
||||||
|
setHeaders(newHeaders)
|
||||||
|
setCurrentKey('')
|
||||||
|
setCurrentValue('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(e: ChangeEvent<HTMLInputElement>) {
|
||||||
|
const checkType = e.target.name.search('key')
|
||||||
|
checkType > 0
|
||||||
|
? setCurrentKey(e.target.value)
|
||||||
|
: setCurrentValue(e.target.value)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
form.setFieldValue(`${field.name}`, headers)
|
||||||
|
}, [headers])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDisabledButton(!currentKey || !currentValue)
|
||||||
|
}, [currentKey, currentValue])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Label htmlFor={props.name}>
|
||||||
|
{label}
|
||||||
|
{props.required && (
|
||||||
|
<span title="Required" className={styles.required}>
|
||||||
|
*
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{help && !prominentHelp && (
|
||||||
|
<Tooltip content={<Markdown text={help} />} />
|
||||||
|
)}
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<div className={styles.headersContainer}>
|
||||||
|
<InputElement
|
||||||
|
name={`${field.name}.key`}
|
||||||
|
placeholder={'key'}
|
||||||
|
value={`${currentKey}`}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InputElement
|
||||||
|
className={`${styles.input}`}
|
||||||
|
name={`${field.name}.value`}
|
||||||
|
placeholder={'value'}
|
||||||
|
value={`${currentValue}`}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={(e: React.SyntheticEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
addHeader()
|
||||||
|
}}
|
||||||
|
disabled={disabledButton}
|
||||||
|
>
|
||||||
|
add
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{headers.length > 0 &&
|
||||||
|
headers.map((header, i) => {
|
||||||
|
return (
|
||||||
|
<div className={styles.headersAddedContainer} key={`header_${i}`}>
|
||||||
|
<InputElement
|
||||||
|
name={`header[${i}].key`}
|
||||||
|
value={`${header.key}`}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InputElement
|
||||||
|
name={`header[${i}].key`}
|
||||||
|
value={`${header.value}`}
|
||||||
|
disabled
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style="primary"
|
||||||
|
size="small"
|
||||||
|
onClick={(e: React.SyntheticEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
removeHeader(i)
|
||||||
|
}}
|
||||||
|
disabled={false}
|
||||||
|
>
|
||||||
|
remove
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
.input {
|
||||||
|
composes: input from '@shared/FormInput/InputElement/index.module.css';
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputMethod {
|
||||||
|
composes: input from '@shared/FormInput/InputElement/index.module.css';
|
||||||
|
cursor: pointer;
|
||||||
|
outline: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 7rem;
|
||||||
|
padding: calc(var(--spacer) / 3) var(--spacer);
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
font-family: var(--font-family-base);
|
||||||
|
font-weight: var(--font-weight-bold);
|
||||||
|
text-transform: uppercase;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
transition: 0.2s ease-out;
|
||||||
|
color: var(--brand-white);
|
||||||
|
box-shadow: 0 9px 18px 0 rgb(0 0 0 / 10%);
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
text-align: center;
|
||||||
|
border-top-right-radius: var(--border-radius);
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: -1px;
|
||||||
|
width: -webkit-fit-content;
|
||||||
|
width: -moz-fit-content;
|
||||||
|
width: fit-content;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: var(--brand-gradient);
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hasError {
|
||||||
|
color: var(--brand-alert-red);
|
||||||
|
border-color: var(--brand-alert-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
composes: error from '@shared/FormInput/index.module.css';
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import React from 'react'
|
||||||
|
import MethodInput, { MethodInputProps } from './index'
|
||||||
|
import { useField } from 'formik'
|
||||||
|
|
||||||
|
jest.mock('formik')
|
||||||
|
|
||||||
|
const props: MethodInputProps = {
|
||||||
|
handleButtonClick: jest.fn(),
|
||||||
|
isLoading: false,
|
||||||
|
name: 'Hello Name'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockMeta = {
|
||||||
|
touched: false,
|
||||||
|
error: '',
|
||||||
|
initialError: '',
|
||||||
|
initialTouched: false,
|
||||||
|
initialValue: '',
|
||||||
|
value: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('@shared/FormInput/InputElement/MethodInput', () => {
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const mockField = {
|
||||||
|
value: '',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'url',
|
||||||
|
method: 'get'
|
||||||
|
}
|
||||||
|
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta])
|
||||||
|
|
||||||
|
render(<MethodInput {...props} />)
|
||||||
|
expect(screen.getByRole('textbox')).toBeInTheDocument()
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByRole('textbox'), {
|
||||||
|
target: { value: 'https://google.com' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('renders button enabled with value', () => {
|
||||||
|
const mockField = {
|
||||||
|
value: 'https://google.com',
|
||||||
|
checked: false,
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onBlur: jest.fn(),
|
||||||
|
name: 'url',
|
||||||
|
method: 'get'
|
||||||
|
}
|
||||||
|
;(useField as jest.Mock).mockReturnValue([mockField, mockMeta])
|
||||||
|
|
||||||
|
render(<MethodInput {...props} />)
|
||||||
|
expect(screen.getByRole('textbox')).toBeInTheDocument()
|
||||||
|
fireEvent.click(screen.getByRole('textbox'))
|
||||||
|
})
|
||||||
|
})
|
@ -0,0 +1,69 @@
|
|||||||
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
|
import Button from '@shared/atoms/Button'
|
||||||
|
import { ErrorMessage, useField } from 'formik'
|
||||||
|
import Loader from '@shared/atoms/Loader'
|
||||||
|
import styles from './index.module.css'
|
||||||
|
import InputGroup from '@shared/FormInput/InputGroup'
|
||||||
|
import InputElement from '@shared/FormInput/InputElement'
|
||||||
|
import isUrl from 'is-url-superb'
|
||||||
|
import { isCID } from '@utils/ipfs'
|
||||||
|
|
||||||
|
export interface MethodInputProps {
|
||||||
|
handleButtonClick(method: string): void
|
||||||
|
isLoading: boolean
|
||||||
|
name: string
|
||||||
|
checkUrl?: boolean
|
||||||
|
storageType?: string
|
||||||
|
hideButton?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MethodInput({
|
||||||
|
handleButtonClick,
|
||||||
|
isLoading,
|
||||||
|
name,
|
||||||
|
checkUrl,
|
||||||
|
storageType,
|
||||||
|
...props
|
||||||
|
}: MethodInputProps): ReactElement {
|
||||||
|
const [field, meta] = useField(name)
|
||||||
|
const [methodSelected, setMethod] = useState(field?.value[0]?.method || 'get')
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<InputGroup>
|
||||||
|
<InputElement
|
||||||
|
className={`${styles.input} ${
|
||||||
|
!isLoading && meta.error !== undefined && meta.touched
|
||||||
|
? styles.hasError
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
{...props}
|
||||||
|
{...field}
|
||||||
|
type="url"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<InputElement
|
||||||
|
className={`${styles.inputMethod} ${
|
||||||
|
!isLoading && meta.error !== undefined && meta.touched
|
||||||
|
? styles.hasError
|
||||||
|
: ''
|
||||||
|
}`}
|
||||||
|
name={`${field.name}[0].method`}
|
||||||
|
value={methodSelected}
|
||||||
|
onChange={(e) => {
|
||||||
|
setMethod(e.currentTarget.value)
|
||||||
|
handleButtonClick(e.currentTarget.value)
|
||||||
|
}}
|
||||||
|
type="select"
|
||||||
|
options={['get', 'post']}
|
||||||
|
/>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
|
{meta.touched && meta.error && (
|
||||||
|
<div className={styles.error}>
|
||||||
|
<ErrorMessage name={field.name} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -7,7 +7,7 @@ import InputGroup from '@shared/FormInput/InputGroup'
|
|||||||
import InputElement from '@shared/FormInput/InputElement'
|
import InputElement from '@shared/FormInput/InputElement'
|
||||||
import isUrl from 'is-url-superb'
|
import isUrl from 'is-url-superb'
|
||||||
import { isCID } from '@utils/ipfs'
|
import { isCID } from '@utils/ipfs'
|
||||||
|
import web3 from 'web3'
|
||||||
export interface URLInputProps {
|
export interface URLInputProps {
|
||||||
submitText: string
|
submitText: string
|
||||||
handleButtonClick(e: React.SyntheticEvent, data: string): void
|
handleButtonClick(e: React.SyntheticEvent, data: string): void
|
||||||
@ -15,6 +15,7 @@ export interface URLInputProps {
|
|||||||
name: string
|
name: string
|
||||||
checkUrl?: boolean
|
checkUrl?: boolean
|
||||||
storageType?: string
|
storageType?: string
|
||||||
|
hideButton?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function URLInput({
|
export default function URLInput({
|
||||||
@ -24,11 +25,12 @@ export default function URLInput({
|
|||||||
name,
|
name,
|
||||||
checkUrl,
|
checkUrl,
|
||||||
storageType,
|
storageType,
|
||||||
|
hideButton,
|
||||||
...props
|
...props
|
||||||
}: URLInputProps): ReactElement {
|
}: URLInputProps): ReactElement {
|
||||||
const [field, meta] = useField(name)
|
const [field, meta] = useField(name)
|
||||||
const [isButtonDisabled, setIsButtonDisabled] = useState(true)
|
const [isButtonDisabled, setIsButtonDisabled] = useState(true)
|
||||||
|
const inputValues = (props as any)?.value
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!field?.value) return
|
if (!field?.value) return
|
||||||
|
|
||||||
@ -37,10 +39,15 @@ export default function URLInput({
|
|||||||
field.value === '' ||
|
field.value === '' ||
|
||||||
(checkUrl && storageType === 'url' && !isUrl(field.value)) ||
|
(checkUrl && storageType === 'url' && !isUrl(field.value)) ||
|
||||||
(checkUrl && storageType === 'ipfs' && !isCID(field.value)) ||
|
(checkUrl && storageType === 'ipfs' && !isCID(field.value)) ||
|
||||||
|
(checkUrl &&
|
||||||
|
storageType === 'graphql' &&
|
||||||
|
!isCID(field.value) &&
|
||||||
|
!inputValues[0]?.query) ||
|
||||||
field.value.includes('javascript:') ||
|
field.value.includes('javascript:') ||
|
||||||
|
(storageType === 'smartcontract' && !inputValues[0]?.abi) ||
|
||||||
meta?.error
|
meta?.error
|
||||||
)
|
)
|
||||||
}, [field?.value, meta?.error])
|
}, [field?.value, meta?.error, inputValues])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -56,17 +63,19 @@ export default function URLInput({
|
|||||||
type="url"
|
type="url"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
{!hideButton && (
|
||||||
style="primary"
|
<Button
|
||||||
size="small"
|
style="primary"
|
||||||
onClick={(e: React.SyntheticEvent) => {
|
size="small"
|
||||||
e.preventDefault()
|
onClick={(e: React.SyntheticEvent) => {
|
||||||
handleButtonClick(e, field.value)
|
e.preventDefault()
|
||||||
}}
|
handleButtonClick(e, field.value)
|
||||||
disabled={isButtonDisabled}
|
}}
|
||||||
>
|
disabled={isButtonDisabled}
|
||||||
{isLoading ? <Loader /> : submitText}
|
>
|
||||||
</Button>
|
{isLoading ? <Loader /> : submitText}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</InputGroup>
|
</InputGroup>
|
||||||
|
|
||||||
{meta.touched && meta.error && (
|
{meta.touched && meta.error && (
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textarea {
|
.codemirror, .textarea {
|
||||||
composes: input;
|
composes: input;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { ReactElement, useCallback, useState } from 'react'
|
import React, { ReactElement } from 'react'
|
||||||
|
import CodeMirror from '@uiw/react-codemirror'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { InputProps } from '..'
|
import { InputProps } from '..'
|
||||||
import FilesInput from './FilesInput'
|
import FilesInput from './FilesInput'
|
||||||
@ -12,6 +13,9 @@ import InputRadio from './Radio'
|
|||||||
import ContainerInput from '@shared/FormInput/InputElement/ContainerInput'
|
import ContainerInput from '@shared/FormInput/InputElement/ContainerInput'
|
||||||
import TagsAutoComplete from './TagsAutoComplete'
|
import TagsAutoComplete from './TagsAutoComplete'
|
||||||
import TabsFile from '@shared/atoms/TabsFile'
|
import TabsFile from '@shared/atoms/TabsFile'
|
||||||
|
import useDarkMode from '@oceanprotocol/use-dark-mode'
|
||||||
|
import appConfig from '../../../../../app.config'
|
||||||
|
import { extensions, oceanTheme } from '@utils/codemirror'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
const cx = classNames.bind(styles)
|
||||||
|
|
||||||
@ -56,6 +60,7 @@ export default function InputElement({
|
|||||||
...props
|
...props
|
||||||
}: InputProps): ReactElement {
|
}: InputProps): ReactElement {
|
||||||
const styleClasses = cx({ select: true, [size]: size })
|
const styleClasses = cx({ select: true, [size]: size })
|
||||||
|
const darkMode = useDarkMode(false, appConfig?.darkModeConfig)
|
||||||
|
|
||||||
switch (props.type) {
|
switch (props.type) {
|
||||||
case 'select': {
|
case 'select': {
|
||||||
@ -100,8 +105,31 @@ export default function InputElement({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return <TabsFile items={tabs} className={styles.pricing} />
|
return (
|
||||||
|
<TabsFile
|
||||||
|
items={tabs}
|
||||||
|
key={`tabFile_${props.name}`}
|
||||||
|
className={styles.pricing}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'codeeditor':
|
||||||
|
return (
|
||||||
|
<CodeMirror
|
||||||
|
id={props.name}
|
||||||
|
className={styles.codemirror}
|
||||||
|
value={`${props.value ? props.value : ''}`}
|
||||||
|
height="200px"
|
||||||
|
placeholder={props.placeholder}
|
||||||
|
theme={oceanTheme(darkMode ? 'dark' : 'light', props)}
|
||||||
|
extensions={[extensions]}
|
||||||
|
onChange={(value) => {
|
||||||
|
form.setFieldValue(`${props.name}`, value)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
case 'textarea':
|
case 'textarea':
|
||||||
return <textarea id={props.name} className={styles.textarea} {...props} />
|
return <textarea id={props.name} className={styles.textarea} {...props} />
|
||||||
|
|
||||||
|
@ -33,6 +33,8 @@ export interface InputProps {
|
|||||||
options?: string[] | AssetSelectionAsset[] | BoxSelectionOption[]
|
options?: string[] | AssetSelectionAsset[] | BoxSelectionOption[]
|
||||||
sortOptions?: boolean
|
sortOptions?: boolean
|
||||||
fields?: FieldInputProps<any>[]
|
fields?: FieldInputProps<any>[]
|
||||||
|
methods?: boolean
|
||||||
|
innerFields?: any
|
||||||
additionalComponent?: ReactElement
|
additionalComponent?: ReactElement
|
||||||
value?: string | number
|
value?: string | number
|
||||||
onChange?(
|
onChange?(
|
||||||
@ -77,8 +79,9 @@ function checkError(
|
|||||||
if (
|
if (
|
||||||
(form?.touched?.[parsedFieldName[0]]?.[parsedFieldName[1]] &&
|
(form?.touched?.[parsedFieldName[0]]?.[parsedFieldName[1]] &&
|
||||||
form?.errors?.[parsedFieldName[0]]?.[parsedFieldName[1]]) ||
|
form?.errors?.[parsedFieldName[0]]?.[parsedFieldName[1]]) ||
|
||||||
(form?.touched[field.name] &&
|
(form?.touched[field?.name] &&
|
||||||
form?.errors[field.name] &&
|
form?.errors[field?.name] &&
|
||||||
|
field.name !== 'files' &&
|
||||||
field.name !== 'links')
|
field.name !== 'links')
|
||||||
) {
|
) {
|
||||||
return true
|
return true
|
||||||
|
@ -65,13 +65,22 @@
|
|||||||
border-top: 0;
|
border-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabLabel {
|
.tabPanel {
|
||||||
color: var(--font-color-text);
|
color: var(--font-color-text);
|
||||||
font-size: var(--font-size-small);
|
font-size: var(--font-size-base);
|
||||||
font-family: var(--font-family-heading);
|
font-family: var(--font-family-heading);
|
||||||
line-height: 1.2;
|
line-height: 1.2;
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: calc(var(--spacer) / 2);
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabPanel label {
|
||||||
|
font-size: var(--font-size-small) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabLabel {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: calc(var(--spacer) / 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 40rem) {
|
@media (min-width: 40rem) {
|
||||||
|
@ -25,7 +25,18 @@ export default function TabsFile({
|
|||||||
className
|
className
|
||||||
}: TabsProps): ReactElement {
|
}: TabsProps): ReactElement {
|
||||||
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
const { values, setFieldValue } = useFormikContext<FormPublishData>()
|
||||||
const [tabIndex, setTabIndex] = useState(0)
|
const initialState = () => {
|
||||||
|
const index = items.findIndex((tab: any) => {
|
||||||
|
// fallback for edit mode (starts at index 0 with hidden element)
|
||||||
|
if (!values?.services) return 0
|
||||||
|
|
||||||
|
return tab.field.value === values.services[0].files[0].type
|
||||||
|
})
|
||||||
|
|
||||||
|
return index < 0 ? 0 : index
|
||||||
|
}
|
||||||
|
|
||||||
|
const [tabIndex, setTabIndex] = useState(initialState)
|
||||||
// hide tabs if are hidden
|
// hide tabs if are hidden
|
||||||
const isHidden = items[tabIndex].props.value[0].type === 'hidden'
|
const isHidden = items[tabIndex].props.value[0].type === 'hidden'
|
||||||
|
|
||||||
@ -76,7 +87,10 @@ export default function TabsFile({
|
|||||||
{items.map((item, index) => {
|
{items.map((item, index) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TabPanel key={`tabpanel_${items[tabIndex].props.name}_${index}`}>
|
<TabPanel
|
||||||
|
key={`tabpanel_${items[tabIndex].props.name}_${index}`}
|
||||||
|
className={styles.tabPanel}
|
||||||
|
>
|
||||||
{!isHidden && (
|
{!isHidden && (
|
||||||
<label className={styles.tabLabel}>
|
<label className={styles.tabLabel}>
|
||||||
{item.field.label}
|
{item.field.label}
|
||||||
|
@ -22,7 +22,7 @@ export default function AssetActions({
|
|||||||
}: {
|
}: {
|
||||||
asset: AssetExtended
|
asset: AssetExtended
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { accountId, balance, web3 } = useWeb3()
|
const { accountId, balance, web3, chainId } = useWeb3()
|
||||||
const { isAssetNetwork } = useAsset()
|
const { isAssetNetwork } = useAsset()
|
||||||
const newCancelToken = useCancelToken()
|
const newCancelToken = useCancelToken()
|
||||||
const isMounted = useIsMounted()
|
const isMounted = useIsMounted()
|
||||||
@ -56,13 +56,25 @@ export default function AssetActions({
|
|||||||
? formikState?.values?.services[0].files[0].type
|
? formikState?.values?.services[0].files[0].type
|
||||||
: null
|
: null
|
||||||
|
|
||||||
|
// TODO: replace 'any' with correct typing
|
||||||
|
const file = formikState?.values?.services[0].files[0] as any
|
||||||
|
const query = file?.query || undefined
|
||||||
|
const abi = file?.abi || undefined
|
||||||
|
const headers = file?.headers || undefined
|
||||||
|
const method = file?.method || undefined
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileInfoResponse = formikState?.values?.services?.[0].files?.[0]
|
const fileInfoResponse = formikState?.values?.services?.[0].files?.[0]
|
||||||
.url
|
.url
|
||||||
? await getFileInfo(
|
? await getFileInfo(
|
||||||
formikState?.values?.services?.[0].files?.[0].url,
|
formikState?.values?.services?.[0].files?.[0].url,
|
||||||
providerUrl,
|
providerUrl,
|
||||||
storageType
|
storageType,
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
abi,
|
||||||
|
chainId,
|
||||||
|
method
|
||||||
)
|
)
|
||||||
: await getFileDidInfo(asset?.id, asset?.services[0]?.id, providerUrl)
|
: await getFileDidInfo(asset?.id, asset?.services[0]?.id, providerUrl)
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import DebugOutput from '@shared/DebugOutput'
|
|||||||
import { useCancelToken } from '@hooks/useCancelToken'
|
import { useCancelToken } from '@hooks/useCancelToken'
|
||||||
import { transformComputeFormToServiceComputeOptions } from '@utils/compute'
|
import { transformComputeFormToServiceComputeOptions } from '@utils/compute'
|
||||||
import { ComputeEditForm } from './_types'
|
import { ComputeEditForm } from './_types'
|
||||||
|
import { previewDebugPatch } from '@utils/ddo'
|
||||||
|
|
||||||
export default function DebugEditCompute({
|
export default function DebugEditCompute({
|
||||||
values,
|
values,
|
||||||
@ -12,6 +13,7 @@ export default function DebugEditCompute({
|
|||||||
values: ComputeEditForm
|
values: ComputeEditForm
|
||||||
asset: Asset
|
asset: Asset
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const [valuePreview, setValuePreview] = useState({})
|
||||||
const [formTransformed, setFormTransformed] =
|
const [formTransformed, setFormTransformed] =
|
||||||
useState<ServiceComputeOptions>()
|
useState<ServiceComputeOptions>()
|
||||||
const newCancelToken = useCancelToken()
|
const newCancelToken = useCancelToken()
|
||||||
@ -27,11 +29,12 @@ export default function DebugEditCompute({
|
|||||||
setFormTransformed(privacy)
|
setFormTransformed(privacy)
|
||||||
}
|
}
|
||||||
transformValues()
|
transformValues()
|
||||||
|
setValuePreview(previewDebugPatch(values, asset.chainId))
|
||||||
}, [values, asset])
|
}, [values, asset])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DebugOutput title="Collected Form Values" output={values} />
|
<DebugOutput title="Collected Form Values" output={valuePreview} />
|
||||||
<DebugOutput title="Transformed Form Values" output={formTransformed} />
|
<DebugOutput title="Transformed Form Values" output={formTransformed} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Asset, Metadata, Service } from '@oceanprotocol/lib'
|
import { Asset, Metadata, Service } from '@oceanprotocol/lib'
|
||||||
import React, { ReactElement } from 'react'
|
import React, { ReactElement, useEffect, useState } from 'react'
|
||||||
import DebugOutput from '@shared/DebugOutput'
|
import DebugOutput from '@shared/DebugOutput'
|
||||||
import { MetadataEditForm } from './_types'
|
import { MetadataEditForm } from './_types'
|
||||||
import { mapTimeoutStringToSeconds } from '@utils/ddo'
|
import { mapTimeoutStringToSeconds, previewDebugPatch } from '@utils/ddo'
|
||||||
import { sanitizeUrl } from '@utils/url'
|
import { sanitizeUrl } from '@utils/url'
|
||||||
|
|
||||||
export default function DebugEditMetadata({
|
export default function DebugEditMetadata({
|
||||||
@ -12,6 +12,7 @@ export default function DebugEditMetadata({
|
|||||||
values: Partial<MetadataEditForm>
|
values: Partial<MetadataEditForm>
|
||||||
asset: Asset
|
asset: Asset
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
|
const [valuePreview, setValuePreview] = useState({})
|
||||||
const linksTransformed = values.links?.length &&
|
const linksTransformed = values.links?.length &&
|
||||||
values.links[0].valid && [sanitizeUrl(values.links[0].url)]
|
values.links[0].valid && [sanitizeUrl(values.links[0].url)]
|
||||||
|
|
||||||
@ -32,9 +33,13 @@ export default function DebugEditMetadata({
|
|||||||
services: [updatedService]
|
services: [updatedService]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setValuePreview(previewDebugPatch(values, asset.chainId))
|
||||||
|
}, [asset.chainId, values])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DebugOutput title="Collected Form Values" output={values} />
|
<DebugOutput title="Collected Form Values" output={valuePreview} />
|
||||||
<DebugOutput title="Transformed Asset Values" output={updatedAsset} />
|
<DebugOutput title="Transformed Asset Values" output={updatedAsset} />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -16,7 +16,7 @@ import { useWeb3 } from '@context/Web3'
|
|||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
import Web3Feedback from '@shared/Web3Feedback'
|
import Web3Feedback from '@shared/Web3Feedback'
|
||||||
import FormEditMetadata from './FormEditMetadata'
|
import FormEditMetadata from './FormEditMetadata'
|
||||||
import { mapTimeoutStringToSeconds } from '@utils/ddo'
|
import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import content from '../../../../content/pages/editMetadata.json'
|
import content from '../../../../content/pages/editMetadata.json'
|
||||||
import { useAbortController } from '@hooks/useAbortController'
|
import { useAbortController } from '@hooks/useAbortController'
|
||||||
@ -36,7 +36,7 @@ export default function Edit({
|
|||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { debug } = useUserPreferences()
|
const { debug } = useUserPreferences()
|
||||||
const { fetchAsset, isAssetNetwork, assetState } = useAsset()
|
const { fetchAsset, isAssetNetwork, assetState } = useAsset()
|
||||||
const { accountId, web3 } = useWeb3()
|
const { accountId, web3, chainId } = useWeb3()
|
||||||
const newAbortController = useAbortController()
|
const newAbortController = useAbortController()
|
||||||
const [success, setSuccess] = useState<string>()
|
const [success, setSuccess] = useState<string>()
|
||||||
const [paymentCollector, setPaymentCollector] = useState<string>()
|
const [paymentCollector, setPaymentCollector] = useState<string>()
|
||||||
@ -115,19 +115,9 @@ export default function Edit({
|
|||||||
const file = {
|
const file = {
|
||||||
nftAddress: asset.nftAddress,
|
nftAddress: asset.nftAddress,
|
||||||
datatokenAddress: asset.services[0].datatokenAddress,
|
datatokenAddress: asset.services[0].datatokenAddress,
|
||||||
files: [
|
files: [normalizeFile(values.files[0].type, values.files[0], chainId)]
|
||||||
{
|
|
||||||
type: values.files[0].type,
|
|
||||||
index: 0,
|
|
||||||
[values.files[0].type === 'ipfs'
|
|
||||||
? 'hash'
|
|
||||||
: values.files[0].type === 'arweave'
|
|
||||||
? 'transactionId'
|
|
||||||
: 'url']: values.files[0].url,
|
|
||||||
method: 'GET'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const filesEncrypted = await getEncryptedFiles(
|
const filesEncrypted = await getEncryptedFiles(
|
||||||
file,
|
file,
|
||||||
asset.services[0].serviceEndpoint
|
asset.services[0].serviceEndpoint
|
||||||
|
@ -5,14 +5,19 @@ import { useFormikContext } from 'formik'
|
|||||||
import { transformPublishFormToDdo } from '../_utils'
|
import { transformPublishFormToDdo } from '../_utils'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import { DDO } from '@oceanprotocol/lib'
|
import { DDO } from '@oceanprotocol/lib'
|
||||||
|
import { previewDebugPatch } from '@utils/ddo'
|
||||||
|
import { useWeb3 } from '@context/Web3'
|
||||||
|
|
||||||
export default function Debug(): ReactElement {
|
export default function Debug(): ReactElement {
|
||||||
const { values } = useFormikContext<FormPublishData>()
|
const { values } = useFormikContext<FormPublishData>()
|
||||||
|
const [valuePreview, setValuePreview] = useState({})
|
||||||
const [ddo, setDdo] = useState<DDO>()
|
const [ddo, setDdo] = useState<DDO>()
|
||||||
|
const { chainId } = useWeb3()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function makeDdo() {
|
async function makeDdo() {
|
||||||
const ddo = await transformPublishFormToDdo(values)
|
const ddo = await transformPublishFormToDdo(values)
|
||||||
|
setValuePreview(previewDebugPatch(values, chainId))
|
||||||
setDdo(ddo)
|
setDdo(ddo)
|
||||||
}
|
}
|
||||||
makeDdo()
|
makeDdo()
|
||||||
@ -20,7 +25,7 @@ export default function Debug(): ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.debug}>
|
<div className={styles.debug}>
|
||||||
<DebugOutput title="Collected Form Values" output={values} />
|
<DebugOutput title="Collected Form Values" output={valuePreview} />
|
||||||
<DebugOutput title="Transformed DDO Values" output={ddo} />
|
<DebugOutput title="Transformed DDO Values" output={ddo} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
Service,
|
Service,
|
||||||
ZERO_ADDRESS
|
ZERO_ADDRESS
|
||||||
} from '@oceanprotocol/lib'
|
} from '@oceanprotocol/lib'
|
||||||
import { mapTimeoutStringToSeconds } from '@utils/ddo'
|
import { mapTimeoutStringToSeconds, normalizeFile } from '@utils/ddo'
|
||||||
import { generateNftCreateData } from '@utils/nft'
|
import { generateNftCreateData } from '@utils/nft'
|
||||||
import { getEncryptedFiles } from '@utils/provider'
|
import { getEncryptedFiles } from '@utils/provider'
|
||||||
import slugify from 'slugify'
|
import slugify from 'slugify'
|
||||||
@ -138,23 +138,10 @@ export async function transformPublishFormToDdo(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is the default format hardcoded
|
|
||||||
|
|
||||||
const file = {
|
const file = {
|
||||||
nftAddress,
|
nftAddress,
|
||||||
datatokenAddress,
|
datatokenAddress,
|
||||||
files: [
|
files: [normalizeFile(files[0].type, files[0], chainId)]
|
||||||
{
|
|
||||||
type: files[0].type,
|
|
||||||
index: 0,
|
|
||||||
[files[0].type === 'ipfs'
|
|
||||||
? 'hash'
|
|
||||||
: files[0].type === 'arweave'
|
|
||||||
? 'transactionId'
|
|
||||||
: 'url']: files[0].url,
|
|
||||||
method: 'GET'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const filesEncrypted =
|
const filesEncrypted =
|
||||||
|
@ -33,8 +33,7 @@ const validationService = {
|
|||||||
files: Yup.array<FileInfo[]>()
|
files: Yup.array<FileInfo[]>()
|
||||||
.of(
|
.of(
|
||||||
Yup.object().shape({
|
Yup.object().shape({
|
||||||
url: testLinks().required('Required'),
|
url: testLinks(),
|
||||||
|
|
||||||
valid: Yup.boolean().isTrue().required('File must be valid.')
|
valid: Yup.boolean().isTrue().required('File must be valid.')
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -126,6 +126,12 @@ ul li {
|
|||||||
color: var(--background-body);
|
color: var(--background-body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* fix required for codemirror */
|
||||||
|
.cm-scroller ::selection {
|
||||||
|
color: var(--font-color-heading);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
form,
|
form,
|
||||||
fieldset {
|
fieldset {
|
||||||
border: 0;
|
border: 0;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".", // This must be specified if "paths" is.
|
"baseUrl": ".", // This must be specified if "paths" is.
|
||||||
"target": "es5",
|
"target": "es2015",
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
Loading…
Reference in New Issue
Block a user