Merge pull request #260 from fleekxyz/release/release-v0.0.7
Release v0.0.7 to main
This commit is contained in:
commit
ebffbaff0f
|
|
@ -113,7 +113,9 @@ contract FleekERC721 is
|
||||||
bool accessPointAutoApproval,
|
bool accessPointAutoApproval,
|
||||||
address verifier
|
address verifier
|
||||||
) public payable requirePayment(Billing.Mint) returns (uint256) {
|
) public payable requirePayment(Billing.Mint) returns (uint256) {
|
||||||
FleekENS.requireENSOwner(ens);
|
if (!hasCollectionRole(CollectionRoles.Verifier, verifier))
|
||||||
|
revert MustHaveCollectionRole(uint8(CollectionRoles.Verifier));
|
||||||
|
if (bytes(ens).length > 0) FleekENS.requireENSOwner(ens);
|
||||||
uint256 tokenId = _appIds;
|
uint256 tokenId = _appIds;
|
||||||
_mint(to, tokenId);
|
_mint(to, tokenId);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@
|
||||||
"colorthief": "^2.3.2",
|
"colorthief": "^2.3.2",
|
||||||
"dotenv": "^16.0.2",
|
"dotenv": "^16.0.2",
|
||||||
"ethers": "^5.7.2",
|
"ethers": "^5.7.2",
|
||||||
"hardhat": "^2.11.2",
|
"hardhat": "^2.14.0",
|
||||||
"hardhat-contract-sizer": "^2.6.1",
|
"hardhat-contract-sizer": "^2.6.1",
|
||||||
"hardhat-gas-reporter": "^1.0.9",
|
"hardhat-gas-reporter": "^1.0.9",
|
||||||
"lint-staged": "^13.0.4",
|
"lint-staged": "^13.0.4",
|
||||||
|
|
|
||||||
|
|
@ -103,4 +103,39 @@ contract Test_FleekERC721_Mint is Test_FleekERC721_Base {
|
||||||
assertEq(tokenId, 0);
|
assertEq(tokenId, 0);
|
||||||
assertEq(CuT.ownerOf(tokenId), to);
|
assertEq(CuT.ownerOf(tokenId), to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testFuzz_shouldNotAllowMintWithInvalidVerifier(address verifier) public {
|
||||||
|
vm.assume(!CuT.hasCollectionRole(FleekAccessControl.CollectionRoles.Verifier, verifier));
|
||||||
|
expectRevertWithCollectionRole(FleekAccessControl.CollectionRoles.Verifier);
|
||||||
|
CuT.mint(
|
||||||
|
deployer,
|
||||||
|
TestConstants.APP_NAME,
|
||||||
|
TestConstants.APP_DESCRIPTION,
|
||||||
|
TestConstants.APP_EXTERNAL_URL,
|
||||||
|
TestConstants.APP_ENS,
|
||||||
|
TestConstants.APP_COMMIT_HASH,
|
||||||
|
TestConstants.APP_GIT_REPOSITORY,
|
||||||
|
TestConstants.LOGO_0,
|
||||||
|
TestConstants.APP_COLOR,
|
||||||
|
false,
|
||||||
|
verifier
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_shouldAllowMintWithEmptyENS() public {
|
||||||
|
uint256 tokenId = CuT.mint(
|
||||||
|
deployer,
|
||||||
|
TestConstants.APP_NAME,
|
||||||
|
TestConstants.APP_DESCRIPTION,
|
||||||
|
TestConstants.APP_EXTERNAL_URL,
|
||||||
|
"",
|
||||||
|
TestConstants.APP_COMMIT_HASH,
|
||||||
|
TestConstants.APP_GIT_REPOSITORY,
|
||||||
|
TestConstants.LOGO_0,
|
||||||
|
TestConstants.APP_COLOR,
|
||||||
|
false,
|
||||||
|
deployer
|
||||||
|
);
|
||||||
|
assertEq(tokenId, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
|
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { TestConstants, Fixtures } from './helpers';
|
import { TestConstants, Fixtures, Errors } from './helpers';
|
||||||
import { ethers } from 'hardhat';
|
import { ethers } from 'hardhat';
|
||||||
|
|
||||||
const { MintParams } = TestConstants;
|
const { MintParams, CollectionRoles } = TestConstants;
|
||||||
|
|
||||||
describe('FleekERC721.Minting', () => {
|
describe('FleekERC721.Minting', () => {
|
||||||
it('should be able to mint a new token', async () => {
|
it('should be able to mint a new token', async () => {
|
||||||
|
|
@ -51,4 +51,49 @@ describe('FleekERC721.Minting', () => {
|
||||||
expect(await contract.ownerOf(tokenId)).to.equal(owner.address);
|
expect(await contract.ownerOf(tokenId)).to.equal(owner.address);
|
||||||
expect(await contract.ownerOf(tokenId)).not.to.equal(otherAccount.address);
|
expect(await contract.ownerOf(tokenId)).not.to.equal(otherAccount.address);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not allow minting with non verifier account param', async () => {
|
||||||
|
const { owner, otherAccount, contract } = await loadFixture(
|
||||||
|
Fixtures.default
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
contract.mint(
|
||||||
|
owner.address,
|
||||||
|
MintParams.name,
|
||||||
|
MintParams.description,
|
||||||
|
MintParams.externalUrl,
|
||||||
|
MintParams.ens,
|
||||||
|
MintParams.commitHash,
|
||||||
|
MintParams.gitRepository,
|
||||||
|
MintParams.logo,
|
||||||
|
MintParams.color,
|
||||||
|
MintParams.accessPointAutoApprovalSettings,
|
||||||
|
otherAccount.address
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.to.be.revertedWithCustomError(contract, Errors.MustHaveCollectionRole)
|
||||||
|
.withArgs(CollectionRoles.Verifier);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow minting with empty ens', async () => {
|
||||||
|
const { owner, contract } = await loadFixture(Fixtures.default);
|
||||||
|
|
||||||
|
const response = await contract.mint(
|
||||||
|
owner.address,
|
||||||
|
MintParams.name,
|
||||||
|
MintParams.description,
|
||||||
|
MintParams.externalUrl,
|
||||||
|
'',
|
||||||
|
MintParams.commitHash,
|
||||||
|
MintParams.gitRepository,
|
||||||
|
MintParams.logo,
|
||||||
|
MintParams.color,
|
||||||
|
MintParams.accessPointAutoApprovalSettings,
|
||||||
|
owner.address
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(response.value).to.be.instanceOf(ethers.BigNumber);
|
||||||
|
expect(response.value.toNumber()).to.equal(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -104,6 +104,6 @@ describe('Deploy', () => {
|
||||||
TestConstants.MintParams.accessPointAutoApprovalSettings,
|
TestConstants.MintParams.accessPointAutoApprovalSettings,
|
||||||
owner.address
|
owner.address
|
||||||
)
|
)
|
||||||
).to.be.revertedWithCustomError(implementation, Errors.ContractIsPaused);
|
).to.be.reverted;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,42 @@
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@chainsafe/as-sha256@^0.3.1":
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz#3639df0e1435cab03f4d9870cc3ac079e57a6fc9"
|
||||||
|
integrity sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==
|
||||||
|
|
||||||
|
"@chainsafe/persistent-merkle-tree@^0.4.2":
|
||||||
|
version "0.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz#4c9ee80cc57cd3be7208d98c40014ad38f36f7ff"
|
||||||
|
integrity sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==
|
||||||
|
dependencies:
|
||||||
|
"@chainsafe/as-sha256" "^0.3.1"
|
||||||
|
|
||||||
|
"@chainsafe/persistent-merkle-tree@^0.5.0":
|
||||||
|
version "0.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz#2b4a62c9489a5739dedd197250d8d2f5427e9f63"
|
||||||
|
integrity sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==
|
||||||
|
dependencies:
|
||||||
|
"@chainsafe/as-sha256" "^0.3.1"
|
||||||
|
|
||||||
|
"@chainsafe/ssz@^0.10.0":
|
||||||
|
version "0.10.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.10.2.tgz#c782929e1bb25fec66ba72e75934b31fd087579e"
|
||||||
|
integrity sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==
|
||||||
|
dependencies:
|
||||||
|
"@chainsafe/as-sha256" "^0.3.1"
|
||||||
|
"@chainsafe/persistent-merkle-tree" "^0.5.0"
|
||||||
|
|
||||||
|
"@chainsafe/ssz@^0.9.2":
|
||||||
|
version "0.9.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@chainsafe/ssz/-/ssz-0.9.4.tgz#696a8db46d6975b600f8309ad3a12f7c0e310497"
|
||||||
|
integrity sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==
|
||||||
|
dependencies:
|
||||||
|
"@chainsafe/as-sha256" "^0.3.1"
|
||||||
|
"@chainsafe/persistent-merkle-tree" "^0.4.2"
|
||||||
|
case "^1.6.3"
|
||||||
|
|
||||||
"@colors/colors@1.5.0":
|
"@colors/colors@1.5.0":
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
|
||||||
|
|
@ -256,7 +292,7 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ethersproject/logger" "^5.7.0"
|
"@ethersproject/logger" "^5.7.0"
|
||||||
|
|
||||||
"@ethersproject/providers@5.7.2":
|
"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.7.1", "@ethersproject/providers@^5.7.2":
|
||||||
version "5.7.2"
|
version "5.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
|
resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb"
|
||||||
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
|
integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==
|
||||||
|
|
@ -477,29 +513,31 @@
|
||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-block@^4.0.0":
|
"@nomicfoundation/ethereumjs-block@5.0.1":
|
||||||
version "4.0.0"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz#fdd5c045e7baa5169abeed0e1202bf94e4481c49"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.1.tgz#6f89664f55febbd723195b6d0974773d29ee133d"
|
||||||
integrity sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==
|
integrity sha512-u1Yioemi6Ckj3xspygu/SfFvm8vZEO8/Yx5a1QLzi6nVU0jz3Pg2OmHKJ5w+D9Ogk1vhwRiqEBAqcb0GVhCyHw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-common" "^3.0.0"
|
"@nomicfoundation/ethereumjs-common" "4.0.1"
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-trie" "^5.0.0"
|
"@nomicfoundation/ethereumjs-trie" "6.0.1"
|
||||||
"@nomicfoundation/ethereumjs-tx" "^4.0.0"
|
"@nomicfoundation/ethereumjs-tx" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
|
ethers "^5.7.1"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-blockchain@^6.0.0":
|
"@nomicfoundation/ethereumjs-blockchain@7.0.1":
|
||||||
version "6.0.0"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz#1a8c243a46d4d3691631f139bfb3a4a157187b0c"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.1.tgz#80e0bd3535bfeb9baa29836b6f25123dab06a726"
|
||||||
integrity sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==
|
integrity sha512-NhzndlGg829XXbqJEYrF1VeZhAwSPgsK/OB7TVrdzft3y918hW5KNd7gIZ85sn6peDZOdjBsAXIpXZ38oBYE5A==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-block" "^4.0.0"
|
"@nomicfoundation/ethereumjs-block" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-common" "^3.0.0"
|
"@nomicfoundation/ethereumjs-common" "4.0.1"
|
||||||
"@nomicfoundation/ethereumjs-ethash" "^2.0.0"
|
"@nomicfoundation/ethereumjs-ethash" "3.0.1"
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-trie" "^5.0.0"
|
"@nomicfoundation/ethereumjs-trie" "6.0.1"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-tx" "5.0.1"
|
||||||
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
abstract-level "^1.0.3"
|
abstract-level "^1.0.3"
|
||||||
debug "^4.3.3"
|
debug "^4.3.3"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
|
|
@ -507,105 +545,105 @@
|
||||||
lru-cache "^5.1.1"
|
lru-cache "^5.1.1"
|
||||||
memory-level "^1.0.0"
|
memory-level "^1.0.0"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-common@^3.0.0":
|
"@nomicfoundation/ethereumjs-common@4.0.1":
|
||||||
version "3.0.0"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz#f6bcc7753994555e49ab3aa517fc8bcf89c280b9"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.1.tgz#4702d82df35b07b5407583b54a45bf728e46a2f0"
|
||||||
integrity sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==
|
integrity sha512-OBErlkfp54GpeiE06brBW/TTbtbuBJV5YI5Nz/aB2evTDo+KawyEzPjBlSr84z/8MFfj8wS2wxzQX1o32cev5g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
crc-32 "^1.2.0"
|
crc-32 "^1.2.0"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-ethash@^2.0.0":
|
"@nomicfoundation/ethereumjs-ethash@3.0.1":
|
||||||
version "2.0.0"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz#11539c32fe0990e1122ff987d1b84cfa34774e81"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.1.tgz#65ca494d53e71e8415c9a49ef48bc921c538fc41"
|
||||||
integrity sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==
|
integrity sha512-KDjGIB5igzWOp8Ik5I6QiRH5DH+XgILlplsHR7TEuWANZA759G6krQ6o8bvj+tRUz08YygMQu/sGd9mJ1DYT8w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-block" "^4.0.0"
|
"@nomicfoundation/ethereumjs-block" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
abstract-level "^1.0.3"
|
abstract-level "^1.0.3"
|
||||||
bigint-crypto-utils "^3.0.23"
|
bigint-crypto-utils "^3.0.23"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-evm@^1.0.0":
|
"@nomicfoundation/ethereumjs-evm@2.0.1":
|
||||||
version "1.0.0"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz#99cd173c03b59107c156a69c5e215409098a370b"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.1.tgz#f35681e203363f69ce2b3d3bf9f44d4e883ca1f1"
|
||||||
integrity sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==
|
integrity sha512-oL8vJcnk0Bx/onl+TgQOQ1t/534GKFaEG17fZmwtPFeH8S5soiBYPCLUrvANOl4sCp9elYxIMzIiTtMtNNN8EQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-common" "^3.0.0"
|
"@ethersproject/providers" "^5.7.1"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-common" "4.0.1"
|
||||||
"@types/async-eventemitter" "^0.2.1"
|
"@nomicfoundation/ethereumjs-tx" "5.0.1"
|
||||||
async-eventemitter "^0.2.4"
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
debug "^4.3.3"
|
debug "^4.3.3"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
mcl-wasm "^0.7.1"
|
mcl-wasm "^0.7.1"
|
||||||
rustbn.js "~0.2.0"
|
rustbn.js "~0.2.0"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-rlp@^4.0.0", "@nomicfoundation/ethereumjs-rlp@^4.0.0-beta.2":
|
"@nomicfoundation/ethereumjs-rlp@5.0.1":
|
||||||
version "4.0.0"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz#d9a9c5f0f10310c8849b6525101de455a53e771d"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.1.tgz#0b30c1cf77d125d390408e391c4bb5291ef43c28"
|
||||||
integrity sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==
|
integrity sha512-xtxrMGa8kP4zF5ApBQBtjlSbN5E2HI8m8FYgVSYAnO6ssUoY5pVPGy2H8+xdf/bmMa22Ce8nWMH3aEW8CcqMeQ==
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-statemanager@^1.0.0":
|
"@nomicfoundation/ethereumjs-statemanager@2.0.1":
|
||||||
version "1.0.0"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz#14a9d4e1c828230368f7ab520c144c34d8721e4b"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.1.tgz#8824a97938db4471911e2d2f140f79195def5935"
|
||||||
integrity sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==
|
integrity sha512-B5ApMOnlruVOR7gisBaYwFX+L/AP7i/2oAahatssjPIBVDF6wTX1K7Qpa39E/nzsH8iYuL3krkYeUFIdO3EMUQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-common" "^3.0.0"
|
"@nomicfoundation/ethereumjs-common" "4.0.1"
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-trie" "^5.0.0"
|
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
|
||||||
debug "^4.3.3"
|
debug "^4.3.3"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
functional-red-black-tree "^1.0.1"
|
ethers "^5.7.1"
|
||||||
|
js-sdsl "^4.1.4"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-trie@^5.0.0":
|
"@nomicfoundation/ethereumjs-trie@6.0.1":
|
||||||
version "5.0.0"
|
version "6.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz#dcfbe3be53a94bc061c9767a396c16702bc2f5b7"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.1.tgz#662c55f6b50659fd4b22ea9f806a7401cafb7717"
|
||||||
integrity sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==
|
integrity sha512-A64It/IMpDVODzCgxDgAAla8jNjNtsoQZIzZUfIV5AY6Coi4nvn7+VReBn5itlxMiL2yaTlQr9TRWp3CSI6VoA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
|
"@types/readable-stream" "^2.3.13"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
readable-stream "^3.6.0"
|
readable-stream "^3.6.0"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-tx@^4.0.0":
|
"@nomicfoundation/ethereumjs-tx@5.0.1":
|
||||||
version "4.0.0"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz#59dc7452b0862b30342966f7052ab9a1f7802f52"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.1.tgz#7629dc2036b4a33c34e9f0a592b43227ef4f0c7d"
|
||||||
integrity sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==
|
integrity sha512-0HwxUF2u2hrsIM1fsasjXvlbDOq1ZHFV2dd1yGq8CA+MEYhaxZr8OTScpVkkxqMwBcc5y83FyPl0J9MZn3kY0w==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-common" "^3.0.0"
|
"@chainsafe/ssz" "^0.9.2"
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@ethersproject/providers" "^5.7.2"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-common" "4.0.1"
|
||||||
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-util@^8.0.0":
|
"@nomicfoundation/ethereumjs-util@9.0.1":
|
||||||
version "8.0.0"
|
version "9.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz#deb2b15d2c308a731e82977aefc4e61ca0ece6c5"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.1.tgz#530cda8bae33f8b5020a8f199ed1d0a2ce48ec89"
|
||||||
integrity sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==
|
integrity sha512-TwbhOWQ8QoSCFhV/DDfSmyfFIHjPjFBj957219+V3jTZYZ2rf9PmDtNOeZWAE3p3vlp8xb02XGpd0v6nTUPbsA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0-beta.2"
|
"@chainsafe/ssz" "^0.10.0"
|
||||||
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
|
|
||||||
"@nomicfoundation/ethereumjs-vm@^6.0.0":
|
"@nomicfoundation/ethereumjs-vm@7.0.1":
|
||||||
version "6.0.0"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz#2bb50d332bf41790b01a3767ffec3987585d1de6"
|
resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.1.tgz#7d035e0993bcad10716c8b36e61dfb87fa3ca05f"
|
||||||
integrity sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==
|
integrity sha512-rArhyn0jPsS/D+ApFsz3yVJMQ29+pVzNZ0VJgkzAZ+7FqXSRtThl1C1prhmlVr3YNUlfpZ69Ak+RUT4g7VoOuQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nomicfoundation/ethereumjs-block" "^4.0.0"
|
"@nomicfoundation/ethereumjs-block" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-blockchain" "^6.0.0"
|
"@nomicfoundation/ethereumjs-blockchain" "7.0.1"
|
||||||
"@nomicfoundation/ethereumjs-common" "^3.0.0"
|
"@nomicfoundation/ethereumjs-common" "4.0.1"
|
||||||
"@nomicfoundation/ethereumjs-evm" "^1.0.0"
|
"@nomicfoundation/ethereumjs-evm" "2.0.1"
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-statemanager" "^1.0.0"
|
"@nomicfoundation/ethereumjs-statemanager" "2.0.1"
|
||||||
"@nomicfoundation/ethereumjs-trie" "^5.0.0"
|
"@nomicfoundation/ethereumjs-trie" "6.0.1"
|
||||||
"@nomicfoundation/ethereumjs-tx" "^4.0.0"
|
"@nomicfoundation/ethereumjs-tx" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
"@types/async-eventemitter" "^0.2.1"
|
|
||||||
async-eventemitter "^0.2.4"
|
|
||||||
debug "^4.3.3"
|
debug "^4.3.3"
|
||||||
ethereum-cryptography "0.1.3"
|
ethereum-cryptography "0.1.3"
|
||||||
functional-red-black-tree "^1.0.1"
|
|
||||||
mcl-wasm "^0.7.1"
|
mcl-wasm "^0.7.1"
|
||||||
rustbn.js "~0.2.0"
|
rustbn.js "~0.2.0"
|
||||||
|
|
||||||
|
|
@ -901,11 +939,6 @@
|
||||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
|
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e"
|
||||||
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
|
integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==
|
||||||
|
|
||||||
"@types/async-eventemitter@^0.2.1":
|
|
||||||
version "0.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz#f8e6280e87e8c60b2b938624b0a3530fb3e24712"
|
|
||||||
integrity sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==
|
|
||||||
|
|
||||||
"@types/bignumber.js@^5.0.0":
|
"@types/bignumber.js@^5.0.0":
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/bignumber.js/-/bignumber.js-5.0.0.tgz#d9f1a378509f3010a3255e9cc822ad0eeb4ab969"
|
resolved "https://registry.yarnpkg.com/@types/bignumber.js/-/bignumber.js-5.0.0.tgz#d9f1a378509f3010a3255e9cc822ad0eeb4ab969"
|
||||||
|
|
@ -1030,6 +1063,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
|
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
|
||||||
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
|
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
|
||||||
|
|
||||||
|
"@types/readable-stream@^2.3.13":
|
||||||
|
version "2.3.15"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-2.3.15.tgz#3d79c9ceb1b6a57d5f6e6976f489b9b5384321ae"
|
||||||
|
integrity sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
safe-buffer "~5.1.1"
|
||||||
|
|
||||||
"@types/responselike@^1.0.0":
|
"@types/responselike@^1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
||||||
|
|
@ -1296,13 +1337,6 @@ astral-regex@^2.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
|
resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
|
||||||
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
|
integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
|
||||||
|
|
||||||
async-eventemitter@^0.2.4:
|
|
||||||
version "0.2.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca"
|
|
||||||
integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==
|
|
||||||
dependencies:
|
|
||||||
async "^2.4.0"
|
|
||||||
|
|
||||||
async-limiter@~1.0.0:
|
async-limiter@~1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||||
|
|
@ -1313,13 +1347,6 @@ async@1.x:
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
|
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
|
||||||
integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==
|
integrity sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==
|
||||||
|
|
||||||
async@^2.4.0:
|
|
||||||
version "2.6.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
|
|
||||||
integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==
|
|
||||||
dependencies:
|
|
||||||
lodash "^4.17.14"
|
|
||||||
|
|
||||||
asynckit@^0.4.0:
|
asynckit@^0.4.0:
|
||||||
version "0.4.0"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
|
|
@ -1604,6 +1631,11 @@ camelcase@^6.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
|
||||||
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
|
||||||
|
|
||||||
|
case@^1.6.3:
|
||||||
|
version "1.6.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/case/-/case-1.6.3.tgz#0a4386e3e9825351ca2e6216c60467ff5f1ea1c9"
|
||||||
|
integrity sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==
|
||||||
|
|
||||||
caseless@^0.12.0, caseless@~0.12.0:
|
caseless@^0.12.0, caseless@~0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||||
|
|
@ -2564,7 +2596,7 @@ ethers@^4.0.40:
|
||||||
uuid "2.0.1"
|
uuid "2.0.1"
|
||||||
xmlhttprequest "1.8.0"
|
xmlhttprequest "1.8.0"
|
||||||
|
|
||||||
ethers@^5.7.2:
|
ethers@^5.7.1, ethers@^5.7.2:
|
||||||
version "5.7.2"
|
version "5.7.2"
|
||||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
|
resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e"
|
||||||
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
|
integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==
|
||||||
|
|
@ -3230,23 +3262,23 @@ hardhat-gas-reporter@^1.0.9:
|
||||||
eth-gas-reporter "^0.2.25"
|
eth-gas-reporter "^0.2.25"
|
||||||
sha1 "^1.1.1"
|
sha1 "^1.1.1"
|
||||||
|
|
||||||
hardhat@^2.11.2:
|
hardhat@^2.14.0:
|
||||||
version "2.12.6"
|
version "2.14.0"
|
||||||
resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.12.6.tgz#ea3c058bbd81850867389d10f76037cfa52a0019"
|
resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.14.0.tgz#b60c74861494aeb1b50803cf04cc47865a42b87a"
|
||||||
integrity sha512-0Ent1O5DsPgvaVb5sxEgsQ3bJRt/Ex92tsoO+xjoNH2Qc4bFmhI5/CHVlFikulalxOPjNmw5XQ2vJFuVQFESAA==
|
integrity sha512-73jsInY4zZahMSVFurSK+5TNCJTXMv+vemvGia0Ac34Mm19fYp6vEPVGF3sucbumszsYxiTT2TbS8Ii2dsDSoQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@ethersproject/abi" "^5.1.2"
|
"@ethersproject/abi" "^5.1.2"
|
||||||
"@metamask/eth-sig-util" "^4.0.0"
|
"@metamask/eth-sig-util" "^4.0.0"
|
||||||
"@nomicfoundation/ethereumjs-block" "^4.0.0"
|
"@nomicfoundation/ethereumjs-block" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-blockchain" "^6.0.0"
|
"@nomicfoundation/ethereumjs-blockchain" "7.0.1"
|
||||||
"@nomicfoundation/ethereumjs-common" "^3.0.0"
|
"@nomicfoundation/ethereumjs-common" "4.0.1"
|
||||||
"@nomicfoundation/ethereumjs-evm" "^1.0.0"
|
"@nomicfoundation/ethereumjs-evm" "2.0.1"
|
||||||
"@nomicfoundation/ethereumjs-rlp" "^4.0.0"
|
"@nomicfoundation/ethereumjs-rlp" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-statemanager" "^1.0.0"
|
"@nomicfoundation/ethereumjs-statemanager" "2.0.1"
|
||||||
"@nomicfoundation/ethereumjs-trie" "^5.0.0"
|
"@nomicfoundation/ethereumjs-trie" "6.0.1"
|
||||||
"@nomicfoundation/ethereumjs-tx" "^4.0.0"
|
"@nomicfoundation/ethereumjs-tx" "5.0.1"
|
||||||
"@nomicfoundation/ethereumjs-util" "^8.0.0"
|
"@nomicfoundation/ethereumjs-util" "9.0.1"
|
||||||
"@nomicfoundation/ethereumjs-vm" "^6.0.0"
|
"@nomicfoundation/ethereumjs-vm" "7.0.1"
|
||||||
"@nomicfoundation/solidity-analyzer" "^0.1.0"
|
"@nomicfoundation/solidity-analyzer" "^0.1.0"
|
||||||
"@sentry/node" "^5.18.1"
|
"@sentry/node" "^5.18.1"
|
||||||
"@types/bn.js" "^5.1.0"
|
"@types/bn.js" "^5.1.0"
|
||||||
|
|
@ -3766,6 +3798,11 @@ jpeg-js@^0.4.1:
|
||||||
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
|
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.4.tgz#a9f1c6f1f9f0fa80cdb3484ed9635054d28936aa"
|
||||||
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
|
integrity sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==
|
||||||
|
|
||||||
|
js-sdsl@^4.1.4:
|
||||||
|
version "4.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430"
|
||||||
|
integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==
|
||||||
|
|
||||||
js-sha3@0.5.7, js-sha3@^0.5.7:
|
js-sha3@0.5.7, js-sha3@^0.5.7:
|
||||||
version "0.5.7"
|
version "0.5.7"
|
||||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
|
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ jspm_packages
|
||||||
# Serverless directories
|
# Serverless directories
|
||||||
.serverless
|
.serverless
|
||||||
|
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
# esbuild directories
|
# esbuild directories
|
||||||
.esbuild
|
.esbuild
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,3 +37,11 @@ functions:
|
||||||
path: build
|
path: build
|
||||||
method: post
|
method: post
|
||||||
cors: true
|
cors: true
|
||||||
|
|
||||||
|
submitMintInfo:
|
||||||
|
handler: dist/functions/mints/handler.submitMintInfo
|
||||||
|
events:
|
||||||
|
- http:
|
||||||
|
path: mint
|
||||||
|
method: post
|
||||||
|
cors: true
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
import {
|
||||||
|
APIGatewayProxyResult,
|
||||||
|
APIGatewayEvent,
|
||||||
|
///APIGatewayEventRequestContext,
|
||||||
|
} from 'aws-lambda';
|
||||||
|
import { formatJSONResponse } from '@libs/api-gateway';
|
||||||
|
|
||||||
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
|
export const submitMintInfo = async (
|
||||||
|
event: APIGatewayEvent,
|
||||||
|
///context: APIGatewayEventRequestContext
|
||||||
|
): Promise<APIGatewayProxyResult> => {
|
||||||
|
try {
|
||||||
|
const id = v4();
|
||||||
|
|
||||||
|
/**if (!verifyAlchemySig(event.headers.xalchemywork)) {
|
||||||
|
throw new Error('Invalid sig');
|
||||||
|
}**/
|
||||||
|
|
||||||
|
if (event.body == undefined) {
|
||||||
|
throw new Error('Undefined data');
|
||||||
|
}
|
||||||
|
|
||||||
|
const mintInfo = {
|
||||||
|
buildId: id,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
body: JSON.parse(event.body),
|
||||||
|
};
|
||||||
|
|
||||||
|
// check if we have it in mongo
|
||||||
|
// if so, trigger verification call
|
||||||
|
// if not, add to mongo
|
||||||
|
|
||||||
|
return formatJSONResponse({
|
||||||
|
mintInfo,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return formatJSONResponse({
|
||||||
|
status: 500,
|
||||||
|
message: e,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { handlerPath } from '@libs/handler-resolver';
|
||||||
|
|
||||||
|
export const newMint = {
|
||||||
|
handler: `${handlerPath(__dirname)}/handler.newMint`,
|
||||||
|
events: [
|
||||||
|
{
|
||||||
|
http: {
|
||||||
|
method: 'post',
|
||||||
|
path: 'mint',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
@ -41,7 +41,15 @@ query getLatestNFAs {
|
||||||
query getNFA($id: ID!) {
|
query getNFA($id: ID!) {
|
||||||
token(id: $id) {
|
token(id: $id) {
|
||||||
tokenId
|
tokenId
|
||||||
|
owner {
|
||||||
|
id
|
||||||
|
}
|
||||||
name
|
name
|
||||||
|
description
|
||||||
|
ENS
|
||||||
|
externalURL
|
||||||
|
logo
|
||||||
|
color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ import { HashRouter, Navigate, Route, Routes } from 'react-router-dom';
|
||||||
import { themeGlobals } from '@/theme/globals';
|
import { themeGlobals } from '@/theme/globals';
|
||||||
|
|
||||||
import { AppPage, ToastProvider } from './components';
|
import { AppPage, ToastProvider } from './components';
|
||||||
import { ComponentsTest, CreateAP, Explore, Home, Mint } from './views';
|
import {
|
||||||
|
ComponentsTest,
|
||||||
|
CreateAP,
|
||||||
|
ExploreView,
|
||||||
|
IndexedNFAView,
|
||||||
|
Mint,
|
||||||
|
} from './views';
|
||||||
|
|
||||||
export const App: React.FC = () => {
|
export const App: React.FC = () => {
|
||||||
themeGlobals();
|
themeGlobals();
|
||||||
|
|
@ -13,10 +19,10 @@ export const App: React.FC = () => {
|
||||||
<ToastProvider />
|
<ToastProvider />
|
||||||
<AppPage>
|
<AppPage>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Explore />} />
|
<Route path="/" element={<ExploreView />} />
|
||||||
<Route path="/mint" element={<Mint />} />
|
<Route path="/mint" element={<Mint />} />
|
||||||
<Route path="/create-ap" element={<CreateAP />} />
|
|
||||||
<Route path="/create-ap/:id" element={<CreateAP />} />
|
<Route path="/create-ap/:id" element={<CreateAP />} />
|
||||||
|
<Route path="/nfa/:id" element={<IndexedNFAView />} />
|
||||||
{/** TODO remove for release */}
|
{/** TODO remove for release */}
|
||||||
<Route path="/components-test" element={<ComponentsTest />} />
|
<Route path="/components-test" element={<ComponentsTest />} />
|
||||||
<Route path="*" element={<Navigate to="/" />} />
|
<Route path="*" element={<Navigate to="/" />} />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { styled } from '@/theme';
|
||||||
|
|
||||||
|
export const CardTag = styled('span', {
|
||||||
|
fontSize: '$sm',
|
||||||
|
backgroundColor: '$slate4',
|
||||||
|
color: '$slate11',
|
||||||
|
p: '$1 $3h',
|
||||||
|
height: '$7',
|
||||||
|
borderRadius: '$md',
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './card-tag';
|
||||||
|
|
@ -96,7 +96,7 @@ const DropdownButton: React.FC<DropdownButtonProps> = ({
|
||||||
{selectedValue && selectedValue.label ? selectedValue.label : 'Select'}
|
{selectedValue && selectedValue.label ? selectedValue.label : 'Select'}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={`pointer-events-none absolute inset-y-0 right-0 flex items-center pr-4 ${textColorCss}`}
|
className={`pointer-events-none absolute top-1 bottom-0 right-0 flex items-center pr-4 ${textColorCss}`}
|
||||||
>
|
>
|
||||||
<Icon name="chevron-down" />
|
<Icon name="chevron-down" />
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -172,7 +172,7 @@ export const Dropdown: React.FC<DropdownProps> = ({
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<Listbox.Options
|
<Listbox.Options
|
||||||
className={`absolute mt-1 max-h-32 ${width} right-0 z-10 overflow-auto rounded-xl bg-black px-3 pt-2 border-solid border-slate6 border text-base focus:outline-none sm:text-sm`}
|
className={`absolute mt-1 max-h-36 ${width} right-0 z-10 overflow-auto rounded-xl bg-black px-3 pt-2 border-solid border-slate6 border text-base focus:outline-none sm:text-sm`}
|
||||||
>
|
>
|
||||||
{items.map((option: DropdownItem) => (
|
{items.map((option: DropdownItem) => (
|
||||||
<DropdownOption key={option.value} option={option} />
|
<DropdownOption key={option.value} option={option} />
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,15 @@ import { AiOutlineTwitter } from '@react-icons/all-files/ai/AiOutlineTwitter';
|
||||||
import { BiGitBranch } from '@react-icons/all-files/bi/BiGitBranch';
|
import { BiGitBranch } from '@react-icons/all-files/bi/BiGitBranch';
|
||||||
import { BiSearch } from '@react-icons/all-files/bi/BiSearch';
|
import { BiSearch } from '@react-icons/all-files/bi/BiSearch';
|
||||||
import { BsFillSquareFill } from '@react-icons/all-files/bs/BsFillSquareFill';
|
import { BsFillSquareFill } from '@react-icons/all-files/bs/BsFillSquareFill';
|
||||||
|
import { FaChevronRight } from '@react-icons/all-files/fa/FaChevronRight';
|
||||||
|
import { FaExternalLinkAlt } from '@react-icons/all-files/fa/FaExternalLinkAlt';
|
||||||
import { IoArrowBackCircleSharp } from '@react-icons/all-files/io5/IoArrowBackCircleSharp';
|
import { IoArrowBackCircleSharp } from '@react-icons/all-files/io5/IoArrowBackCircleSharp';
|
||||||
import { IoCheckmarkCircleSharp } from '@react-icons/all-files/io5/IoCheckmarkCircleSharp';
|
import { IoCheckmarkCircleSharp } from '@react-icons/all-files/io5/IoCheckmarkCircleSharp';
|
||||||
import { IoClose } from '@react-icons/all-files/io5/IoClose';
|
import { IoClose } from '@react-icons/all-files/io5/IoClose';
|
||||||
import { IoCloudUploadSharp } from '@react-icons/all-files/io5/IoCloudUploadSharp';
|
import { IoCloudUploadSharp } from '@react-icons/all-files/io5/IoCloudUploadSharp';
|
||||||
import { IoInformationCircleSharp } from '@react-icons/all-files/io5/IoInformationCircleSharp';
|
import { IoInformationCircleSharp } from '@react-icons/all-files/io5/IoInformationCircleSharp';
|
||||||
import { IoLogoGithub } from '@react-icons/all-files/io5/IoLogoGithub';
|
import { IoLogoGithub } from '@react-icons/all-files/io5/IoLogoGithub';
|
||||||
|
import { MdVerifiedUser } from '@react-icons/all-files/md/MdVerifiedUser';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BetaTag,
|
BetaTag,
|
||||||
|
|
@ -28,19 +31,22 @@ export const IconLibrary = Object.freeze({
|
||||||
check: AiOutlineCheck,
|
check: AiOutlineCheck,
|
||||||
'check-circle': IoCheckmarkCircleSharp,
|
'check-circle': IoCheckmarkCircleSharp,
|
||||||
'chevron-down': ChevronDownIcon,
|
'chevron-down': ChevronDownIcon,
|
||||||
|
'chevron-right': FaChevronRight,
|
||||||
close: IoClose,
|
close: IoClose,
|
||||||
error: ErrorIcon,
|
error: ErrorIcon,
|
||||||
ethereum: EthereumIcon,
|
ethereum: EthereumIcon,
|
||||||
|
'external-link': FaExternalLinkAlt,
|
||||||
fleekLogo: FleekLogo,
|
fleekLogo: FleekLogo,
|
||||||
fleekName: FleekName,
|
fleekName: FleekName,
|
||||||
github: IoLogoGithub,
|
github: IoLogoGithub,
|
||||||
info: IoInformationCircleSharp,
|
info: IoInformationCircleSharp,
|
||||||
upload: IoCloudUploadSharp,
|
|
||||||
metamask: MetamaskIcon, //remove if not used
|
metamask: MetamaskIcon, //remove if not used
|
||||||
search: BiSearch,
|
search: BiSearch,
|
||||||
square: BsFillSquareFill,
|
square: BsFillSquareFill,
|
||||||
success: AiFillCheckCircle,
|
success: AiFillCheckCircle,
|
||||||
twitter: AiOutlineTwitter,
|
twitter: AiOutlineTwitter,
|
||||||
|
upload: IoCloudUploadSharp,
|
||||||
|
verified: MdVerifiedUser,
|
||||||
});
|
});
|
||||||
|
|
||||||
export type IconName = keyof typeof IconLibrary;
|
export type IconName = keyof typeof IconLibrary;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,5 @@ export abstract class InputFileStyles {
|
||||||
'&[aria-invalid=true], &[data-invalid]': {
|
'&[aria-invalid=true], &[data-invalid]': {
|
||||||
borderColor: '$red9',
|
borderColor: '$red9',
|
||||||
},
|
},
|
||||||
//TODO add error state
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,5 @@ export * from './toast';
|
||||||
export * from './step';
|
export * from './step';
|
||||||
export * from './nfa-card';
|
export * from './nfa-card';
|
||||||
export * from './nfa-preview';
|
export * from './nfa-preview';
|
||||||
|
export * from './card-tag';
|
||||||
export * from './resolved-address';
|
export * from './resolved-address';
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,29 @@
|
||||||
import { Avatar, ConnectKitButton } from 'connectkit';
|
import { Avatar, ConnectKitButton } from 'connectkit';
|
||||||
|
|
||||||
import { Button, Flex } from '@/components';
|
import { Button, Flex } from '@/components';
|
||||||
|
import { ENSActions, useAppDispatch, useENSStore } from '@/store';
|
||||||
|
|
||||||
export const ConnectWalletButton: React.FC = () => {
|
export const ConnectWalletButton: React.FC = () => {
|
||||||
|
const { addressMap } = useENSStore();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const setEnsNameStore = (ensName: string, address: string): void => {
|
||||||
|
const stored = addressMap[address] || {};
|
||||||
|
if (typeof stored.state !== 'undefined') return;
|
||||||
|
|
||||||
|
dispatch(
|
||||||
|
ENSActions.setAddress({
|
||||||
|
key: address,
|
||||||
|
value: { state: 'success', value: ensName },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConnectKitButton.Custom>
|
<ConnectKitButton.Custom>
|
||||||
{({ isConnected, show, truncatedAddress, address, ensName }) => {
|
{({ isConnected, show, truncatedAddress, address, ensName }) => {
|
||||||
|
if (ensName && address) setEnsNameStore(ensName, address);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button onClick={show}>
|
<Button onClick={show}>
|
||||||
{isConnected && !!address && !!truncatedAddress ? (
|
{isConnected && !!address && !!truncatedAddress ? (
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export abstract class NavBarStyles {
|
||||||
backgroundColor: '$black',
|
backgroundColor: '$black',
|
||||||
zIndex: '$sticky',
|
zIndex: '$sticky',
|
||||||
height: '$22',
|
height: '$22',
|
||||||
|
overflow: 'hidden', // TODO: this must be worked on for responsive layout
|
||||||
});
|
});
|
||||||
|
|
||||||
static readonly Content = styled('div', {
|
static readonly Content = styled('div', {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,8 @@ export abstract class PageStyles {
|
||||||
public static readonly Content = styled('div', {
|
public static readonly Content = styled('div', {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
minHeight: '85vh',
|
minHeight: '85vh',
|
||||||
maxWidth: '$7xl',
|
maxWidth: '$6xl',
|
||||||
|
padding: '0 $6',
|
||||||
margin: '0 auto',
|
margin: '0 auto',
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export const NFACardStyles = {
|
||||||
Container: styled(Link, {
|
Container: styled(Link, {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
width: '14.6875rem',
|
minWidth: '12.5rem',
|
||||||
padding: 0,
|
padding: 0,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
|
|
@ -60,6 +60,11 @@ export const NFACardStyles = {
|
||||||
fontSize: '$xl',
|
fontSize: '$xl',
|
||||||
fontWeight: '$medium',
|
fontWeight: '$medium',
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
|
|
||||||
|
maxWidth: 'auto',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
}),
|
}),
|
||||||
Content: styled('span', {
|
Content: styled('span', {
|
||||||
all: 'unset',
|
all: 'unset',
|
||||||
|
|
@ -79,7 +84,7 @@ export const NFACardStyles = {
|
||||||
Skeleton: {
|
Skeleton: {
|
||||||
Preview: styled(Skeleton, {
|
Preview: styled(Skeleton, {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: 'calc(14.6875rem - 2px)',
|
aspectRatio: 1,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Title: styled(Skeleton, {
|
Title: styled(Skeleton, {
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,7 @@ export type NFACardProps = Omit<
|
||||||
export const NFACard: React.FC<NFACardProps> = forwardStyledRef<
|
export const NFACard: React.FC<NFACardProps> = forwardStyledRef<
|
||||||
HTMLAnchorElement,
|
HTMLAnchorElement,
|
||||||
NFACardProps
|
NFACardProps
|
||||||
// TODO: Set default path to NFA page
|
>(({ data, to = `/nfa/${data.tokenId}`, ...props }, ref) => {
|
||||||
>(({ data, to = `/create-ap/${data.tokenId}`, ...props }, ref) => {
|
|
||||||
const { name, color, ENS, logo, accessPoints } = data;
|
const { name, color, ENS, logo, accessPoints } = data;
|
||||||
|
|
||||||
const apCounter = useMemo(() => accessPoints?.length ?? 0, [accessPoints]);
|
const apCounter = useMemo(() => accessPoints?.length ?? 0, [accessPoints]);
|
||||||
|
|
@ -61,8 +60,7 @@ export const NFACard: React.FC<NFACardProps> = forwardStyledRef<
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* TODO: treat names bigger than space in layout when designs are done */}
|
<S.Title title={data.name}>{data.name}</S.Title>
|
||||||
<S.Title>{data.name}</S.Title>
|
|
||||||
{/* TODO: set correct value when it gets available on contract side */}
|
{/* TODO: set correct value when it gets available on contract side */}
|
||||||
<Badge verified={Math.random() > 0.5} />
|
<Badge verified={Math.random() > 0.5} />
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,20 @@ export type ResolvedAddressProps = React.ComponentPropsWithRef<
|
||||||
typeof RAS.Container
|
typeof RAS.Container
|
||||||
> & {
|
> & {
|
||||||
children: string;
|
children: string;
|
||||||
|
truncated?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ResolvedAddress = forwardStyledRef<
|
export const ResolvedAddress = forwardStyledRef<
|
||||||
HTMLSpanElement,
|
HTMLSpanElement,
|
||||||
ResolvedAddressProps
|
ResolvedAddressProps
|
||||||
>(({ children, ...props }, ref) => {
|
>(({ children, truncated = false, ...props }, ref) => {
|
||||||
const [resolvedAddress, loading] = useResolvedAddress(children);
|
const [resolvedAddress, loading] = useResolvedAddress(children);
|
||||||
|
|
||||||
const text = useMemo(() => {
|
const text = useMemo(() => {
|
||||||
if (!resolvedAddress.endsWith('.eth'))
|
if (!resolvedAddress.endsWith('.eth') && truncated)
|
||||||
return `${resolvedAddress.slice(0, 6)}...${resolvedAddress.slice(-4)}`;
|
return `${resolvedAddress.slice(0, 6)}...${resolvedAddress.slice(-4)}`;
|
||||||
return resolvedAddress;
|
return resolvedAddress;
|
||||||
}, [resolvedAddress]);
|
}, [resolvedAddress, truncated]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RAS.Container {...props} ref={ref} data-loading={loading}>
|
<RAS.Container {...props} ref={ref} data-loading={loading}>
|
||||||
|
|
|
||||||
|
|
@ -1 +1,2 @@
|
||||||
export * from './spinner';
|
export * from './spinner';
|
||||||
|
export * from './spinner-dot';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { SpinnerStyles } from './spinner.styles';
|
||||||
|
|
||||||
|
export const SpinnerDot: React.FC<SpinnerStyles.ContainerProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<SpinnerStyles.Container
|
||||||
|
{...props}
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<circle cx="12" cy="2.5" r="1" opacity=".14" fill="#FFFFFF" />
|
||||||
|
<circle cx="16.75" cy="3.77" r="1" opacity=".29" fill="#FFFFFF" />
|
||||||
|
<circle cx="20.23" cy="7.25" r="1" opacity=".43" fill="#FFFFFF" />
|
||||||
|
<circle cx="21.50" cy="12.00" r="1" opacity=".57" fill="#FFFFFF" />
|
||||||
|
<circle cx="20.23" cy="16.75" r="1" opacity=".71" fill="#FFFFFF" />
|
||||||
|
<circle cx="16.75" cy="20.23" r="1" opacity=".86" fill="#FFFFFF" />
|
||||||
|
<circle cx="12" cy="21.5" r="1" fill="#FFFFFF" />
|
||||||
|
</g>
|
||||||
|
</SpinnerStyles.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,12 +1,57 @@
|
||||||
import { styled } from '@/theme';
|
import { keyframes, styled } from '@/theme';
|
||||||
|
|
||||||
export abstract class SpinnerStyles {
|
const DotSpinner = keyframes({
|
||||||
static readonly Container = styled('svg', {
|
'8.3%': {
|
||||||
|
transform: 'rotate(30deg)',
|
||||||
|
},
|
||||||
|
'16.6%': {
|
||||||
|
transform: 'rotate(60deg)',
|
||||||
|
},
|
||||||
|
'25%': {
|
||||||
|
transform: 'rotate(90deg)',
|
||||||
|
},
|
||||||
|
'33.3%': {
|
||||||
|
transform: 'rotate(120deg)',
|
||||||
|
},
|
||||||
|
'41.6%': {
|
||||||
|
transform: 'rotate(150deg)',
|
||||||
|
},
|
||||||
|
'50%': {
|
||||||
|
transform: 'rotate(180deg)',
|
||||||
|
},
|
||||||
|
'58.3%': {
|
||||||
|
transform: 'rotate(210deg)',
|
||||||
|
},
|
||||||
|
'66.6%': {
|
||||||
|
transform: 'rotate(240deg)',
|
||||||
|
},
|
||||||
|
'75%': {
|
||||||
|
transform: 'rotate(270deg)',
|
||||||
|
},
|
||||||
|
'83.3%': {
|
||||||
|
transform: 'rotate(300deg)',
|
||||||
|
},
|
||||||
|
'91.6%': {
|
||||||
|
transform: 'rotate(330deg)',
|
||||||
|
},
|
||||||
|
'100%': {
|
||||||
|
transform: 'rotate(360deg)',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SpinnerStyles = {
|
||||||
|
KeyFrames: {},
|
||||||
|
Container: styled('svg', {
|
||||||
fontSize: '1.5rem',
|
fontSize: '1.5rem',
|
||||||
width: '1em',
|
width: '1em',
|
||||||
height: '1em',
|
height: '1em',
|
||||||
});
|
|
||||||
}
|
g: {
|
||||||
|
transformOrigin: 'center',
|
||||||
|
animation: `${DotSpinner} 0.75s step-end infinite`,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
export namespace SpinnerStyles {
|
export namespace SpinnerStyles {
|
||||||
export type ContainerProps = React.ComponentProps<
|
export type ContainerProps = React.ComponentProps<
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
export const createBunnyCDNMock = async (
|
||||||
|
domain: string,
|
||||||
|
targetDomain: string
|
||||||
|
): Promise<{ bunnyURL: string }> => {
|
||||||
|
return new Promise((resolved, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolved({
|
||||||
|
bunnyURL: '8c12c649402442d88b5f.b-cdn.net',
|
||||||
|
});
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const verifyBunnyCDNMock = async (domain: string): Promise<boolean> => {
|
||||||
|
return new Promise((resolved, reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolved(true);
|
||||||
|
}, 3000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
export * from './mint-site';
|
export * from './mint-site';
|
||||||
export * from './detail';
|
export * from './detail';
|
||||||
export * from './list';
|
export * from './list';
|
||||||
|
export * from './bunny-cdn';
|
||||||
|
export * from './nfa';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
export const NFAMock = {
|
||||||
|
id: '6',
|
||||||
|
tokenId: '6',
|
||||||
|
name: 'Polygon',
|
||||||
|
description:
|
||||||
|
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sit amet velit dolor. Praesent dapibus euismod molestie. Duis maximus porttitor odio. Duis quis lorem id lacus cursus commodo vel vehicula mauris.',
|
||||||
|
externalURL: 'https://polygon.com',
|
||||||
|
ENS: 'polygon.eth',
|
||||||
|
logo: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAzOC40IDMzLjUiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDM4LjQgMzMuNTsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOiM4MjQ3RTU7fQo8L3N0eWxlPgo8Zz4KCTxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yOSwxMC4yYy0wLjctMC40LTEuNi0wLjQtMi40LDBMMjEsMTMuNWwtMy44LDIuMWwtNS41LDMuM2MtMC43LDAuNC0xLjYsMC40LTIuNCwwTDUsMTYuMwoJCWMtMC43LTAuNC0xLjItMS4yLTEuMi0yLjF2LTVjMC0wLjgsMC40LTEuNiwxLjItMi4xbDQuMy0yLjVjMC43LTAuNCwxLjYtMC40LDIuNCwwTDE2LDcuMmMwLjcsMC40LDEuMiwxLjIsMS4yLDIuMXYzLjNsMy44LTIuMlY3CgkJYzAtMC44LTAuNC0xLjYtMS4yLTIuMWwtOC00LjdjLTAuNy0wLjQtMS42LTAuNC0yLjQsMEwxLjIsNUMwLjQsNS40LDAsNi4yLDAsN3Y5LjRjMCwwLjgsMC40LDEuNiwxLjIsMi4xbDguMSw0LjcKCQljMC43LDAuNCwxLjYsMC40LDIuNCwwbDUuNS0zLjJsMy44LTIuMmw1LjUtMy4yYzAuNy0wLjQsMS42LTAuNCwyLjQsMGw0LjMsMi41YzAuNywwLjQsMS4yLDEuMiwxLjIsMi4xdjVjMCwwLjgtMC40LDEuNi0xLjIsMi4xCgkJTDI5LDI4LjhjLTAuNywwLjQtMS42LDAuNC0yLjQsMGwtNC4zLTIuNWMtMC43LTAuNC0xLjItMS4yLTEuMi0yLjFWMjFsLTMuOCwyLjJ2My4zYzAsMC44LDAuNCwxLjYsMS4yLDIuMWw4LjEsNC43CgkJYzAuNywwLjQsMS42LDAuNCwyLjQsMGw4LjEtNC43YzAuNy0wLjQsMS4yLTEuMiwxLjItMi4xVjE3YzAtMC44LTAuNC0xLjYtMS4yLTIuMUwyOSwxMC4yeiIvPgo8L2c+Cjwvc3ZnPgo=',
|
||||||
|
color: 8668388,
|
||||||
|
accessPointAutoApproval: true,
|
||||||
|
owner: {
|
||||||
|
id: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049',
|
||||||
|
collection: true,
|
||||||
|
},
|
||||||
|
mintedBy: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049',
|
||||||
|
controllers: [],
|
||||||
|
gitRepository: {
|
||||||
|
id: '',
|
||||||
|
},
|
||||||
|
commitHash: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049',
|
||||||
|
accessPoints: [],
|
||||||
|
verifier: {
|
||||||
|
id: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import { createBunnyCDNMock } from '@/mocks';
|
||||||
|
import { RootState } from '@/store';
|
||||||
|
import { AppLog } from '@/utils';
|
||||||
|
|
||||||
|
import { bunnyCDNActions } from '../bunny-cdn-slice';
|
||||||
|
|
||||||
|
type CNAMERecord = {
|
||||||
|
domain: string;
|
||||||
|
targetDomain: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createBunnyCDN = createAsyncThunk<void, CNAMERecord>(
|
||||||
|
'BunnyCDN/CreateCDN',
|
||||||
|
async ({ domain, targetDomain }, { dispatch, getState }) => {
|
||||||
|
const { state } = (getState() as RootState).bunnyCDN;
|
||||||
|
|
||||||
|
if (state === 'loading') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch(bunnyCDNActions.setState('loading'));
|
||||||
|
|
||||||
|
const CDNRecord = await createBunnyCDNMock(domain, targetDomain);
|
||||||
|
|
||||||
|
dispatch(bunnyCDNActions.setCDNRecordData(CDNRecord.bunnyURL));
|
||||||
|
} catch (error) {
|
||||||
|
AppLog.errorToast(
|
||||||
|
'Failed to create the CDN record. Please, try again',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
dispatch(bunnyCDNActions.setState('failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './create-cdn';
|
||||||
|
export * from './verify-pullzone';
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { createAsyncThunk } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import { verifyBunnyCDNMock } from '@/mocks';
|
||||||
|
import { RootState } from '@/store';
|
||||||
|
import { AppLog } from '@/utils';
|
||||||
|
|
||||||
|
import { bunnyCDNActions } from '../bunny-cdn-slice';
|
||||||
|
|
||||||
|
export const verifyBunnyPullzone = createAsyncThunk<void, string>(
|
||||||
|
'BunnyCDN/VerifyPullzone',
|
||||||
|
async (domain, { dispatch, getState }): Promise<void> => {
|
||||||
|
const { state } = (getState() as RootState).bunnyCDN;
|
||||||
|
|
||||||
|
if (state === 'loading') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch(bunnyCDNActions.setState('loading'));
|
||||||
|
|
||||||
|
const verifyAPState = await verifyBunnyCDNMock(domain);
|
||||||
|
|
||||||
|
if (verifyAPState) dispatch(bunnyCDNActions.setState('success'));
|
||||||
|
else throw new Error('Invalid AP state');
|
||||||
|
} catch (error) {
|
||||||
|
AppLog.errorToast(
|
||||||
|
'There was an error trying to verify the domain. Please, try again',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
dispatch(bunnyCDNActions.setState('failed'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import { RootState } from '@/store';
|
||||||
|
import { useAppSelector } from '@/store/hooks';
|
||||||
|
|
||||||
|
import * as asyncThunk from './async-thunk';
|
||||||
|
|
||||||
|
export namespace BunnyCDNState {
|
||||||
|
export type CreateCDNState =
|
||||||
|
| undefined
|
||||||
|
| 'loading'
|
||||||
|
| 'unferified'
|
||||||
|
| 'failed'
|
||||||
|
| 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BunnyCDNState {
|
||||||
|
state: BunnyCDNState.CreateCDNState;
|
||||||
|
bunnyURL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: BunnyCDNState = {
|
||||||
|
state: undefined,
|
||||||
|
bunnyURL: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const bunnyCDNSlice = createSlice({
|
||||||
|
name: 'BunnyCDNSlice',
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setState: (state, action: PayloadAction<BunnyCDNState.CreateCDNState>) => {
|
||||||
|
state.state = action.payload;
|
||||||
|
},
|
||||||
|
setCDNRecordData: (state, action: PayloadAction<string>) => {
|
||||||
|
state.bunnyURL = action.payload;
|
||||||
|
state.state = 'success';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const bunnyCDNActions = {
|
||||||
|
...bunnyCDNSlice.actions,
|
||||||
|
...asyncThunk,
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectENSState = (state: RootState): BunnyCDNState => state.bunnyCDN;
|
||||||
|
|
||||||
|
export const useBunnyCDNStore = (): BunnyCDNState =>
|
||||||
|
useAppSelector(selectENSState);
|
||||||
|
|
||||||
|
export default bunnyCDNSlice.reducer;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './bunny-cdn-slice';
|
||||||
|
|
@ -2,3 +2,4 @@ export * from './fleek-erc721';
|
||||||
export * from './github';
|
export * from './github';
|
||||||
export * from './toasts';
|
export * from './toasts';
|
||||||
export * from './ens';
|
export * from './ens';
|
||||||
|
export * from './bunny-cdn';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
import bunnyCDNReducer from './features/bunny-cdn/bunny-cdn-slice';
|
||||||
import ENSReducer from './features/ens/ens-slice';
|
import ENSReducer from './features/ens/ens-slice';
|
||||||
import fleekERC721Reducer from './features/fleek-erc721/fleek-erc721-slice';
|
import fleekERC721Reducer from './features/fleek-erc721/fleek-erc721-slice';
|
||||||
import githubReducer from './features/github/github-slice';
|
import githubReducer from './features/github/github-slice';
|
||||||
|
|
@ -7,10 +8,11 @@ import toastsReducer from './features/toasts/toasts-slice';
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
|
bunnyCDN: bunnyCDNReducer,
|
||||||
|
ENS: ENSReducer,
|
||||||
fleekERC721: fleekERC721Reducer,
|
fleekERC721: fleekERC721Reducer,
|
||||||
github: githubReducer,
|
github: githubReducer,
|
||||||
toasts: toastsReducer,
|
toasts: toastsReducer,
|
||||||
ENS: ENSReducer,
|
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) =>
|
middleware: (getDefaultMiddleware) =>
|
||||||
getDefaultMiddleware({
|
getDefaultMiddleware({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* Converts a hex color string to a number.
|
||||||
|
*/
|
||||||
|
export const parseColorToNumber = (color: string): number => {
|
||||||
|
const hexColor = color.replace('#', '');
|
||||||
|
return parseInt(hexColor, 16);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts string number to hex color string.
|
||||||
|
*/
|
||||||
|
export const parseNumberToHexColor = (color: number): string => {
|
||||||
|
const hexColor = color.toString(16);
|
||||||
|
return hexColor;
|
||||||
|
};
|
||||||
|
|
@ -54,12 +54,23 @@ const hasSpecialCharacters: StringValidator = {
|
||||||
message: 'This field has special characters',
|
message: 'This field has special characters',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isValidDomain: StringValidator = {
|
||||||
|
name: 'isValidDomain',
|
||||||
|
validate: (value = '') => {
|
||||||
|
const regex =
|
||||||
|
/(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]/;
|
||||||
|
return regex.test(value);
|
||||||
|
},
|
||||||
|
message: 'This field is not a valid domain',
|
||||||
|
};
|
||||||
|
|
||||||
export const StringValidators = {
|
export const StringValidators = {
|
||||||
required,
|
required,
|
||||||
maxLength,
|
maxLength,
|
||||||
isUrl,
|
isUrl,
|
||||||
maxFileSize,
|
maxFileSize,
|
||||||
hasSpecialCharacters,
|
hasSpecialCharacters,
|
||||||
|
isValidDomain,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const hasValidator = <
|
export const hasValidator = <
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,156 @@
|
||||||
|
import { useQuery } from '@apollo/client';
|
||||||
|
import { ethers } from 'ethers';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { useAccount } from 'wagmi';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
CardTag,
|
||||||
|
Flex,
|
||||||
|
Form,
|
||||||
|
Spinner,
|
||||||
|
Stepper,
|
||||||
|
Text,
|
||||||
|
} from '@/components';
|
||||||
|
import { getNFADocument } from '@/graphclient';
|
||||||
|
import { useAppDispatch } from '@/store';
|
||||||
|
import { bunnyCDNActions, useBunnyCDNStore } from '@/store/features/bunny-cdn';
|
||||||
|
import { AppLog } from '@/utils';
|
||||||
|
|
||||||
|
import { CreateAccessPoint } from '../create-ap.context';
|
||||||
|
import { NFAIconFragment } from '../nfa-icon';
|
||||||
|
import { useAccessPointFormContext } from './create-ap.form.context';
|
||||||
|
|
||||||
|
export const SelectedNFA: React.FC = () => {
|
||||||
|
const { nfa } = CreateAccessPoint.useContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
css={{
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Flex css={{ alignItems: 'center', maxWidth: '65%' }}>
|
||||||
|
<NFAIconFragment image={nfa.logo} color={nfa.color} />
|
||||||
|
<Text
|
||||||
|
css={{
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{nfa.name}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
<CardTag css={{ minWidth: '$28' }}>Selected NFA</CardTag>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CreateAccessPointFormBody: React.FC = () => {
|
||||||
|
const { id } = useParams();
|
||||||
|
const { address } = useAccount();
|
||||||
|
const { nextStep } = Stepper.useContext();
|
||||||
|
const { nfa, setNfa, billing } = CreateAccessPoint.useContext();
|
||||||
|
const { setArgs } = CreateAccessPoint.useTransactionContext();
|
||||||
|
const { state } = useBunnyCDNStore();
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
domain: {
|
||||||
|
value: [domain],
|
||||||
|
},
|
||||||
|
isValid: [isValid],
|
||||||
|
},
|
||||||
|
} = useAccessPointFormContext();
|
||||||
|
|
||||||
|
const {
|
||||||
|
form: { domain: domainContext },
|
||||||
|
} = useAccessPointFormContext();
|
||||||
|
|
||||||
|
const { loading: nfaLoading } = useQuery(getNFADocument, {
|
||||||
|
skip: id === undefined,
|
||||||
|
variables: {
|
||||||
|
id: ethers.utils.hexlify(Number(id)),
|
||||||
|
},
|
||||||
|
onCompleted(data) {
|
||||||
|
if (data.token && id) {
|
||||||
|
const { name, tokenId, logo, color, externalURL: domain } = data.token;
|
||||||
|
setNfa({ name, tokenId, logo, color, domain });
|
||||||
|
} else {
|
||||||
|
AppLog.errorToast("We couldn't find the NFA you are looking for");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
AppLog.errorToast('Error fetching NFA', error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state === 'success') {
|
||||||
|
nextStep();
|
||||||
|
dispatch(bunnyCDNActions.setState(undefined));
|
||||||
|
}
|
||||||
|
}, [state, nextStep, dispatch]);
|
||||||
|
|
||||||
|
if (nfaLoading) {
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
css={{
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '$48',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Spinner />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleContinueClick = (): void => {
|
||||||
|
if (!address) {
|
||||||
|
AppLog.errorToast('No address found. Please connect your wallet.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nfa && domain) {
|
||||||
|
try {
|
||||||
|
setArgs([Number(nfa.tokenId), domain, { value: billing }]);
|
||||||
|
dispatch(
|
||||||
|
bunnyCDNActions.createBunnyCDN({
|
||||||
|
domain: 'domain',
|
||||||
|
targetDomain: domain,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
AppLog.errorToast('Error setting transaction arguments');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex css={{ flexDirection: 'column', gap: '$6' }}>
|
||||||
|
<SelectedNFA />
|
||||||
|
<Text css={{ fontSize: '$sm', color: '$slate11' }}>
|
||||||
|
Enter the domain you want to host the NFA. You will need access to the
|
||||||
|
DNS settings in the next step.
|
||||||
|
</Text>
|
||||||
|
<Form.Field context={domainContext}>
|
||||||
|
<Form.Label>Domain</Form.Label>
|
||||||
|
<Form.Input placeholder="mydomain.com" />
|
||||||
|
<Form.Overline />
|
||||||
|
</Form.Field>
|
||||||
|
<Button
|
||||||
|
disabled={!isValid || nfa.tokenId === ''}
|
||||||
|
isLoading={state === 'loading'}
|
||||||
|
colorScheme="blue"
|
||||||
|
variant="solid"
|
||||||
|
onClick={handleContinueClick}
|
||||||
|
>
|
||||||
|
Continue
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Card, Grid, Icon, IconButton, Stepper } from '@/components';
|
import { Card, Flex, Icon, IconButton, Stepper } from '@/components';
|
||||||
|
|
||||||
import { CreateAccessPointFormBody } from './create-ap.form-body';
|
import { CreateAccessPointFormBody } from './create-ap-form-body';
|
||||||
|
|
||||||
export const CreateAccessPointForm: React.FC = () => {
|
export const CreateAccessPointForm: React.FC = () => {
|
||||||
const { prevStep } = Stepper.useContext();
|
const { prevStep } = Stepper.useContext();
|
||||||
|
|
@ -8,7 +8,7 @@ export const CreateAccessPointForm: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Card.Container css={{ width: '$107h' }}>
|
<Card.Container css={{ width: '$107h' }}>
|
||||||
<Card.Heading
|
<Card.Heading
|
||||||
title="Create Access Point"
|
title="Enter Domain"
|
||||||
leftIcon={
|
leftIcon={
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="Add"
|
aria-label="Add"
|
||||||
|
|
@ -29,13 +29,14 @@ export const CreateAccessPointForm: React.FC = () => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Grid
|
<Flex
|
||||||
css={{
|
css={{
|
||||||
rowGap: '$6',
|
flexDirection: 'column',
|
||||||
|
gap: '$6',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<CreateAccessPointFormBody />
|
<CreateAccessPointFormBody />
|
||||||
</Grid>
|
</Flex>
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card.Container>
|
</Card.Container>
|
||||||
);
|
);
|
||||||
|
|
@ -5,7 +5,7 @@ import { createContext, StringValidators } from '@/utils';
|
||||||
|
|
||||||
export type CreateAccessPointFormContext = {
|
export type CreateAccessPointFormContext = {
|
||||||
form: {
|
form: {
|
||||||
appName: FormField;
|
domain: FormField;
|
||||||
isValid: ReactState<boolean>;
|
isValid: ReactState<boolean>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -20,9 +20,9 @@ export const [CreateAccessPointFormProvider, useAccessPointFormContext] =
|
||||||
export const useAccessPointFormContextInit =
|
export const useAccessPointFormContextInit =
|
||||||
(): CreateAccessPointFormContext => ({
|
(): CreateAccessPointFormContext => ({
|
||||||
form: {
|
form: {
|
||||||
appName: useFormField('appName', [
|
domain: useFormField('domain', [
|
||||||
StringValidators.required,
|
StringValidators.required,
|
||||||
StringValidators.maxLength(50),
|
StringValidators.isValidDomain,
|
||||||
]),
|
]),
|
||||||
isValid: useState(false),
|
isValid: useState(false),
|
||||||
},
|
},
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './create-ap.form.context';
|
||||||
|
export * from './create-ap-form';
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
|
import { Button, Card, Grid, SpinnerDot, Stepper, Text } from '@/components';
|
||||||
|
import { bunnyCDNActions, useAppDispatch, useBunnyCDNStore } from '@/store';
|
||||||
|
|
||||||
|
import { useAccessPointFormContext } from '../ap-form-step';
|
||||||
|
import { CreateAccessPoint } from '../create-ap.context';
|
||||||
|
import { DisplayText } from '../display-text';
|
||||||
|
import { isSubdomain } from './record-step.utils';
|
||||||
|
|
||||||
|
export const APRecordCardBody: React.FC = () => {
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
const { bunnyURL, state } = useBunnyCDNStore();
|
||||||
|
const {
|
||||||
|
nfa: { domain: nfaDomain },
|
||||||
|
} = CreateAccessPoint.useContext();
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
domain: {
|
||||||
|
value: [accesPointDomain],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} = useAccessPointFormContext();
|
||||||
|
const { nextStep } = Stepper.useContext();
|
||||||
|
|
||||||
|
const isSudomain = useMemo(
|
||||||
|
() => isSubdomain(accesPointDomain),
|
||||||
|
[accesPointDomain]
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state === 'success') {
|
||||||
|
dispatch(bunnyCDNActions.setState(undefined));
|
||||||
|
nextStep();
|
||||||
|
}
|
||||||
|
}, [state, nextStep, dispatch]);
|
||||||
|
|
||||||
|
const handleContinueClick = (): void => {
|
||||||
|
dispatch(bunnyCDNActions.verifyBunnyPullzone(nfaDomain));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card.Body>
|
||||||
|
{state === 'loading' ? (
|
||||||
|
<Card.Text css={{ p: '$12 $10', gap: '$7' }}>
|
||||||
|
<SpinnerDot css={{ fontSize: '$7xl' }} />
|
||||||
|
<Text css={{ fontSize: '$md' }}>
|
||||||
|
Waiting for DNS propagation, allow a few minutes.
|
||||||
|
</Text>
|
||||||
|
</Card.Text>
|
||||||
|
) : (
|
||||||
|
<Grid
|
||||||
|
css={{
|
||||||
|
rowGap: '$6',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text>
|
||||||
|
{`Create a ${
|
||||||
|
isSudomain ? 'CNAME' : 'ANAME'
|
||||||
|
} record in your DNS provider pointing to our CDN
|
||||||
|
endpoint.`}
|
||||||
|
</Text>
|
||||||
|
<DisplayText
|
||||||
|
label="Record Type"
|
||||||
|
value={isSudomain ? 'CNAME' : 'ANAME'}
|
||||||
|
/>
|
||||||
|
<DisplayText label="Host" value={isSudomain ? 'App' : '@'} />
|
||||||
|
<DisplayText label="Data (Points to)" value={bunnyURL} />
|
||||||
|
<Button
|
||||||
|
colorScheme="blue"
|
||||||
|
variant="solid"
|
||||||
|
onClick={handleContinueClick}
|
||||||
|
>
|
||||||
|
I added the record
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
|
</Card.Body>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { Card, Icon, IconButton, Stepper } from '@/components';
|
||||||
|
|
||||||
|
export const APRecordCardHeader: React.FC = () => {
|
||||||
|
const { prevStep } = Stepper.useContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card.Heading
|
||||||
|
title="Create Record"
|
||||||
|
leftIcon={
|
||||||
|
<IconButton
|
||||||
|
aria-label="Add"
|
||||||
|
colorScheme="gray"
|
||||||
|
variant="link"
|
||||||
|
icon={<Icon name="back" />}
|
||||||
|
css={{ mr: '$2' }}
|
||||||
|
onClick={prevStep}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={
|
||||||
|
<IconButton
|
||||||
|
aria-label="Add"
|
||||||
|
colorScheme="gray"
|
||||||
|
variant="link"
|
||||||
|
icon={<Icon name="info" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Card } from '@/components';
|
||||||
|
|
||||||
|
import { APRecordCardBody } from './ap-record-body';
|
||||||
|
import { APRecordCardHeader } from './ap-record-header';
|
||||||
|
|
||||||
|
export const APRecordStep: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Card.Container css={{ width: '$107h' }}>
|
||||||
|
<APRecordCardHeader />
|
||||||
|
<APRecordCardBody />
|
||||||
|
</Card.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './ap-record-step';
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
export const isSubdomain = (url: string): boolean => {
|
||||||
|
const urlParts = url.split('.');
|
||||||
|
|
||||||
|
return urlParts.length > 2;
|
||||||
|
};
|
||||||
|
|
@ -1,36 +1,71 @@
|
||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import { useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
|
import { useAccount } from 'wagmi';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Flex,
|
Flex,
|
||||||
Grid,
|
|
||||||
Icon,
|
Icon,
|
||||||
IconButton,
|
IconButton,
|
||||||
|
ResolvedAddress,
|
||||||
Stepper,
|
Stepper,
|
||||||
|
Text,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { useTransactionCost } from '@/hooks';
|
import { useTransactionCost } from '@/hooks';
|
||||||
import { FleekERC721 } from '@/integrations';
|
import { FleekERC721 } from '@/integrations';
|
||||||
|
import { AppLog } from '@/utils';
|
||||||
|
|
||||||
|
import { useAccessPointFormContext } from './ap-form-step/create-ap.form.context';
|
||||||
|
import { SelectedNFA } from './ap-form-step/create-ap-form-body';
|
||||||
import { CreateAccessPoint } from './create-ap.context';
|
import { CreateAccessPoint } from './create-ap.context';
|
||||||
import { useAccessPointFormContext } from './create-ap.form.context';
|
import { DisplayText } from './display-text';
|
||||||
|
|
||||||
|
export const AccessPointDataFragment: React.FC = () => {
|
||||||
|
const { address, status } = useAccount();
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
domain: {
|
||||||
|
value: [domain],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} = useAccessPointFormContext();
|
||||||
|
|
||||||
|
if (status === 'connecting') return <div>Loading...</div>; //TODO replace with spinner
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SelectedNFA />
|
||||||
|
<DisplayText
|
||||||
|
label="Owner"
|
||||||
|
value={
|
||||||
|
address ? (
|
||||||
|
<ResolvedAddress truncated={false}>{address || ''}</ResolvedAddress>
|
||||||
|
) : (
|
||||||
|
'Please connect to wallet'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<DisplayText label="Frontend URL" value={domain} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const CreateAccessPointPreview: React.FC = () => {
|
export const CreateAccessPointPreview: React.FC = () => {
|
||||||
const { prevStep } = Stepper.useContext();
|
const { prevStep } = Stepper.useContext();
|
||||||
|
const { address } = useAccount();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
prepare: { status: prepareStatus, data: prepareData, error: prepareError },
|
prepare: { status: prepareStatus, data: prepareData, error: prepareError },
|
||||||
write: { status: writeStatus, write },
|
write: { status: writeStatus, write },
|
||||||
transaction: { status: transactionStatus },
|
transaction: { status: transactionStatus },
|
||||||
} = CreateAccessPoint.useTransactionContext();
|
} = CreateAccessPoint.useTransactionContext();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
form: {
|
form: {
|
||||||
appName: {
|
isValid: [isValid],
|
||||||
value: [appName],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} = useAccessPointFormContext();
|
} = useAccessPointFormContext();
|
||||||
const { nfa } = CreateAccessPoint.useContext();
|
|
||||||
|
|
||||||
const [cost, currency, isCostLoading] = useTransactionCost(
|
const [cost, currency, isCostLoading] = useTransactionCost(
|
||||||
prepareData?.request.value,
|
prepareData?.request.value,
|
||||||
|
|
@ -66,10 +101,19 @@ export const CreateAccessPointPreview: React.FC = () => {
|
||||||
[prepareStatus, writeStatus, transactionStatus]
|
[prepareStatus, writeStatus, transactionStatus]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const error = [writeStatus, transactionStatus].some(
|
||||||
|
(status) => status === 'error'
|
||||||
|
);
|
||||||
|
if (error) {
|
||||||
|
AppLog.errorToast('An error occurred while minting the NFA');
|
||||||
|
}
|
||||||
|
}, [writeStatus, transactionStatus]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card.Container css={{ width: '$107h' }}>
|
<Card.Container css={{ width: '$107h' }}>
|
||||||
<Card.Heading
|
<Card.Heading
|
||||||
title={`Create Access Point ${nfa.label || ''}`}
|
title="Review Details"
|
||||||
leftIcon={
|
leftIcon={
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="Add"
|
aria-label="Add"
|
||||||
|
|
@ -90,26 +134,19 @@ export const CreateAccessPointPreview: React.FC = () => {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Card.Body>
|
<Card.Body>
|
||||||
<Grid
|
<Flex css={{ flexDirection: 'column', gap: '$6' }}>
|
||||||
css={{
|
<AccessPointDataFragment />
|
||||||
rowGap: '$6',
|
<Text>{message}</Text>
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex css={{ flexDirection: 'column' }}>
|
|
||||||
<span>NFA: {nfa.value}</span>
|
|
||||||
<span>{appName}</span>
|
|
||||||
<span className="text-slate11 text-sm">{message}</span>
|
|
||||||
</Flex>
|
|
||||||
<Button
|
<Button
|
||||||
disabled={!!prepareError || !nfa}
|
isLoading={isLoading}
|
||||||
|
isDisabled={isLoading || !isValid || !address}
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
variant="solid"
|
variant="solid"
|
||||||
onClick={write}
|
onClick={write}
|
||||||
isLoading={isLoading}
|
|
||||||
>
|
>
|
||||||
Create
|
Create
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Flex>
|
||||||
</Card.Body>
|
</Card.Body>
|
||||||
</Card.Container>
|
</Card.Container>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { Button, Card, Flex, Icon, IconButton, Text } from '@/components';
|
||||||
|
|
||||||
|
import { CreateAccessPoint } from './create-ap.context';
|
||||||
|
import { AccessPointDataFragment } from './create-ap-preview';
|
||||||
|
|
||||||
|
export const CreateAccessPointSuccess: React.FC = () => {
|
||||||
|
const { nfa } = CreateAccessPoint.useContext();
|
||||||
|
return (
|
||||||
|
<Card.Container css={{ width: '$107h' }}>
|
||||||
|
<Card.Heading
|
||||||
|
title="Hosting Successful"
|
||||||
|
leftIcon={
|
||||||
|
<Icon
|
||||||
|
name="check-circle"
|
||||||
|
css={{ color: '$green11', fontSize: '$xl', mr: '$2' }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
rightIcon={
|
||||||
|
<IconButton
|
||||||
|
aria-label="Add"
|
||||||
|
colorScheme="gray"
|
||||||
|
variant="link"
|
||||||
|
icon={<Icon name="info" />}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Card.Body>
|
||||||
|
<Flex css={{ flexDirection: 'column', gap: '$6' }}>
|
||||||
|
<Text css={{ fontSize: '$sm', color: '$slate11' }}>
|
||||||
|
{`You have successfully hosted a ${nfa.name} frontend on your own domain.`}
|
||||||
|
</Text>
|
||||||
|
<AccessPointDataFragment />
|
||||||
|
<Flex css={{ flexDirection: 'column', gap: '$4' }}>
|
||||||
|
<Button
|
||||||
|
colorScheme="blue"
|
||||||
|
variant="solid"
|
||||||
|
leftIcon={<Icon name="twitter" />}
|
||||||
|
>
|
||||||
|
Tweet about your frontend!
|
||||||
|
</Button>
|
||||||
|
<Button>Manage Frontend</Button>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
</Card.Body>
|
||||||
|
</Card.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,16 +1,23 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { Token } from '@/graphclient';
|
|
||||||
import { EthereumHooks } from '@/integrations';
|
import { EthereumHooks } from '@/integrations';
|
||||||
import { useFleekERC721Billing } from '@/store';
|
import { useFleekERC721Billing } from '@/store';
|
||||||
import { AppLog, createContext, pushToast } from '@/utils';
|
import { AppLog, createContext } from '@/utils';
|
||||||
|
|
||||||
type NFA = Pick<Token, 'id' | 'name'>;
|
export type NFA = {
|
||||||
|
tokenId: string;
|
||||||
|
name: string;
|
||||||
|
logo: string;
|
||||||
|
color: number;
|
||||||
|
domain: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type AccessPointContext = {
|
export type AccessPointContext = {
|
||||||
billing: string | undefined;
|
billing: string | undefined;
|
||||||
nfa: NFA | undefined;
|
nfa: NFA;
|
||||||
setNfa: ReactState<NFA | undefined>[1];
|
setNfa: (nfa: NFA) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [CreateAPProvider, useContext] = createContext<AccessPointContext>({
|
const [CreateAPProvider, useContext] = createContext<AccessPointContext>({
|
||||||
|
|
@ -31,7 +38,13 @@ export abstract class CreateAccessPoint {
|
||||||
children,
|
children,
|
||||||
}) => {
|
}) => {
|
||||||
const [billing] = useFleekERC721Billing('AddAccessPoint');
|
const [billing] = useFleekERC721Billing('AddAccessPoint');
|
||||||
const [nfa, setNfa] = useState<NFA>();
|
const [nfa, setNfa] = useState<NFA>({
|
||||||
|
tokenId: '',
|
||||||
|
name: '',
|
||||||
|
logo: '',
|
||||||
|
color: 0,
|
||||||
|
domain: '',
|
||||||
|
});
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
billing,
|
billing,
|
||||||
|
|
@ -44,12 +57,10 @@ export abstract class CreateAccessPoint {
|
||||||
<TransactionProvider
|
<TransactionProvider
|
||||||
config={{
|
config={{
|
||||||
transaction: {
|
transaction: {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data: any) => {
|
||||||
AppLog.info('Transaction:', data);
|
AppLog.info('Transaction:', data);
|
||||||
pushToast('success', 'Your transaction was successful!');
|
|
||||||
},
|
},
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
onError: (error: any) => {
|
||||||
onError: (error) => {
|
|
||||||
AppLog.errorToast(
|
AppLog.errorToast(
|
||||||
'There was an error trying to create the Access Point. Please try again'
|
'There was an error trying to create the Access Point. Please try again'
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
import { useQuery } from '@apollo/client';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { Button, Flex, Form, Spinner, Stepper } from '@/components';
|
|
||||||
import { getNFADocument } from '@/graphclient';
|
|
||||||
import { AppLog } from '@/utils';
|
|
||||||
|
|
||||||
import { CreateAccessPoint } from './create-ap.context';
|
|
||||||
import { useAccessPointFormContext } from './create-ap.form.context';
|
|
||||||
import { NfaPicker } from './nfa-picker';
|
|
||||||
|
|
||||||
export const CreateAccessPointFormBody: React.FC = () => {
|
|
||||||
const { id } = useParams();
|
|
||||||
const { nextStep } = Stepper.useContext();
|
|
||||||
const { nfa, setNfa, billing } = CreateAccessPoint.useContext();
|
|
||||||
const { setArgs } = CreateAccessPoint.useTransactionContext();
|
|
||||||
|
|
||||||
const {
|
|
||||||
form: {
|
|
||||||
appName: {
|
|
||||||
value: [appName],
|
|
||||||
},
|
|
||||||
isValid: [isValid],
|
|
||||||
},
|
|
||||||
} = useAccessPointFormContext();
|
|
||||||
|
|
||||||
const {
|
|
||||||
form: { appName: appNameContext },
|
|
||||||
} = useAccessPointFormContext();
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: nfaData,
|
|
||||||
error: nfaError,
|
|
||||||
loading: nfaLoading,
|
|
||||||
} = useQuery(getNFADocument, {
|
|
||||||
skip: id === undefined,
|
|
||||||
variables: {
|
|
||||||
id: ethers.utils.hexlify(Number(id)),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (nfaError) {
|
|
||||||
AppLog.errorToast('Error fetching NFA');
|
|
||||||
}
|
|
||||||
}, [nfaError]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (nfaData) {
|
|
||||||
if (nfaData.token && id) {
|
|
||||||
const { name } = nfaData.token;
|
|
||||||
setNfa({ value: id, label: name });
|
|
||||||
} else {
|
|
||||||
AppLog.errorToast("We couldn't find the NFA you are looking for");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [nfaData, id, setNfa]);
|
|
||||||
|
|
||||||
if (nfaLoading) {
|
|
||||||
return (
|
|
||||||
<Flex
|
|
||||||
css={{
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '$48',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Spinner />
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleContinueClick = (): void => {
|
|
||||||
if (nfa && appName) {
|
|
||||||
setArgs([Number(nfa.value), appName, { value: billing }]);
|
|
||||||
nextStep();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* TODO will have to do some changes on the Form.Combobox if we use this component for the NFA picker */}
|
|
||||||
{id === undefined && <NfaPicker />}
|
|
||||||
<Form.Field context={appNameContext}>
|
|
||||||
<Form.Label>App Name</Form.Label>
|
|
||||||
<Form.Input />
|
|
||||||
</Form.Field>
|
|
||||||
<Button
|
|
||||||
disabled={!isValid}
|
|
||||||
colorScheme="blue"
|
|
||||||
variant="solid"
|
|
||||||
onClick={handleContinueClick}
|
|
||||||
>
|
|
||||||
Continue
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -1,16 +1,29 @@
|
||||||
import { Form, Step, Stepper } from '@/components';
|
import { Form, Step, Stepper } from '@/components';
|
||||||
|
|
||||||
import { WalletStep } from '../mint/wallet-step';
|
import { WalletStep } from '../mint/wallet-step';
|
||||||
import { useAccessPointFormContext } from './create-ap.form.context';
|
import { useAccessPointFormContext } from './ap-form-step/create-ap.form.context';
|
||||||
import { CreateAccessPointForm } from './create-ap-form';
|
import { CreateAccessPointForm } from './ap-form-step/create-ap-form';
|
||||||
|
import { APRecordStep } from './ap-record-step/ap-record-step';
|
||||||
|
import { isSubdomain } from './ap-record-step/record-step.utils';
|
||||||
|
import { CreateAccessPoint } from './create-ap.context';
|
||||||
import { CreateAccessPointPreview } from './create-ap-preview';
|
import { CreateAccessPointPreview } from './create-ap-preview';
|
||||||
|
import { CreateAccessPointSuccess } from './create-ap-success';
|
||||||
|
|
||||||
export const CreateApStepper: React.FC = () => {
|
export const CreateApStepper: React.FC = () => {
|
||||||
|
const {
|
||||||
|
transaction: { isSuccess },
|
||||||
|
} = CreateAccessPoint.useTransactionContext();
|
||||||
const {
|
const {
|
||||||
form: {
|
form: {
|
||||||
|
domain: {
|
||||||
|
value: [accesPointDomain],
|
||||||
|
},
|
||||||
isValid: [, setIsValid],
|
isValid: [, setIsValid],
|
||||||
},
|
},
|
||||||
} = useAccessPointFormContext();
|
} = useAccessPointFormContext();
|
||||||
|
|
||||||
|
if (isSuccess) return <CreateAccessPointSuccess />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stepper.Root initialStep={1}>
|
<Stepper.Root initialStep={1}>
|
||||||
<Form.Root onValidationChange={setIsValid}>
|
<Form.Root onValidationChange={setIsValid}>
|
||||||
|
|
@ -22,13 +35,23 @@ export const CreateApStepper: React.FC = () => {
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
|
|
||||||
<Stepper.Step>
|
<Stepper.Step>
|
||||||
<Step header="Set Access Point">
|
<Step header="Enter the domain you want to host the NFA">
|
||||||
<CreateAccessPointForm />
|
<CreateAccessPointForm />
|
||||||
</Step>
|
</Step>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
|
|
||||||
<Stepper.Step>
|
<Stepper.Step>
|
||||||
<Step header="Create Access Point">
|
<Step
|
||||||
|
header={`Add a ${
|
||||||
|
isSubdomain(accesPointDomain) ? 'CNAME' : 'ANAME'
|
||||||
|
} record to your DNS provider`}
|
||||||
|
>
|
||||||
|
<APRecordStep />
|
||||||
|
</Step>
|
||||||
|
</Stepper.Step>
|
||||||
|
|
||||||
|
<Stepper.Step>
|
||||||
|
<Step header="Review your hosted frontend and confirm">
|
||||||
<CreateAccessPointPreview />
|
<CreateAccessPointPreview />
|
||||||
</Step>
|
</Step>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { Flex } from '@/components';
|
import { Flex } from '@/components';
|
||||||
|
|
||||||
import { CreateAccessPoint } from './create-ap.context';
|
|
||||||
import {
|
import {
|
||||||
CreateAccessPointFormProvider,
|
CreateAccessPointFormProvider,
|
||||||
useAccessPointFormContextInit,
|
useAccessPointFormContextInit,
|
||||||
} from './create-ap.form.context';
|
} from './ap-form-step/create-ap.form.context';
|
||||||
|
import { CreateAccessPoint } from './create-ap.context';
|
||||||
import { CreateApStepper } from './create-ap.stepper';
|
import { CreateApStepper } from './create-ap.stepper';
|
||||||
|
|
||||||
export const CreateAP: React.FC = () => {
|
export const CreateAP: React.FC = () => {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { styled } from '@/theme';
|
||||||
|
|
||||||
|
import { Flex } from '../../../components/layout';
|
||||||
|
|
||||||
|
export const DisplayTextStyles = {
|
||||||
|
Container: styled(Flex, {
|
||||||
|
flexDirection: 'column',
|
||||||
|
}),
|
||||||
|
Label: styled('label', {
|
||||||
|
color: '$slate11',
|
||||||
|
mb: '$1h',
|
||||||
|
|
||||||
|
fontSize: '$xs',
|
||||||
|
//TODO add variants
|
||||||
|
}),
|
||||||
|
Input: styled('span', {
|
||||||
|
backgroundColor: '$slate1',
|
||||||
|
borderColor: '$slate1',
|
||||||
|
color: '$slate12',
|
||||||
|
borderRadius: '$lg',
|
||||||
|
fontSize: '$sm',
|
||||||
|
height: '$11',
|
||||||
|
p: '$3 $3h',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { DisplayTextStyles as S } from './display-text.styles';
|
||||||
|
type DisplayTextProps = {
|
||||||
|
label: string;
|
||||||
|
value: string | React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DisplayText: React.FC<DisplayTextProps> = ({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
}: DisplayTextProps) => {
|
||||||
|
return (
|
||||||
|
<S.Container>
|
||||||
|
<S.Label>{label}</S.Label>
|
||||||
|
<S.Input>{value}</S.Input>
|
||||||
|
</S.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './display-text';
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './nfa-icon';
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { styled } from '@/theme';
|
||||||
|
|
||||||
|
export const NFAIconStyles = {
|
||||||
|
Container: styled('span', {
|
||||||
|
p: '$1h',
|
||||||
|
borderRadius: '$full',
|
||||||
|
width: '$7',
|
||||||
|
height: '$7',
|
||||||
|
mr: '$2',
|
||||||
|
}),
|
||||||
|
Image: styled('img', {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { parseNumberToHexColor } from '@/utils/color';
|
||||||
|
|
||||||
|
import { NFAIconStyles as NS } from './nfa-icon.styles';
|
||||||
|
|
||||||
|
type NFAIconProps = {
|
||||||
|
image: string;
|
||||||
|
color: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const NFAIconFragment: React.FC<NFAIconProps> = ({
|
||||||
|
image,
|
||||||
|
color,
|
||||||
|
}: NFAIconProps) => {
|
||||||
|
return (
|
||||||
|
<NS.Container
|
||||||
|
css={{ backgroundColor: `#${parseNumberToHexColor(color)}57` }}
|
||||||
|
>
|
||||||
|
<NS.Image src={image} />
|
||||||
|
</NS.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
import { useQuery } from '@apollo/client';
|
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
import { Combobox } from '@/components';
|
|
||||||
import { getLatestNFAsDocument } from '@/graphclient';
|
|
||||||
import { AppLog } from '@/utils';
|
|
||||||
|
|
||||||
import { CreateAccessPoint } from './create-ap.context';
|
|
||||||
|
|
||||||
export const NfaPicker: React.FC = () => {
|
|
||||||
const { nfa, setNfa } = CreateAccessPoint.useContext();
|
|
||||||
const { data, loading, error } = useQuery(getLatestNFAsDocument);
|
|
||||||
|
|
||||||
const items = useMemo(() => data?.tokens || [], [data]);
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
AppLog.errorToast('Error loading NFA list', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Combobox
|
|
||||||
isLoading={loading}
|
|
||||||
items={items}
|
|
||||||
selected={[nfa, setNfa]}
|
|
||||||
queryKey={['name', 'id']}
|
|
||||||
>
|
|
||||||
{({ Field, Options }) => (
|
|
||||||
<>
|
|
||||||
<Field>{(selected) => selected?.name || 'Select NFA'}</Field>
|
|
||||||
|
|
||||||
<Options>{(item) => item.name}</Options>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Combobox>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -2,13 +2,15 @@ import { Flex, ResolvedAddress } from '@/components';
|
||||||
|
|
||||||
import { ColorPickerTest } from './color-picker';
|
import { ColorPickerTest } from './color-picker';
|
||||||
import { ComboboxTest } from './combobox-test';
|
import { ComboboxTest } from './combobox-test';
|
||||||
|
import { SpinnerTest } from './spinner-test';
|
||||||
import { ToastTest } from './toast-test';
|
import { ToastTest } from './toast-test';
|
||||||
|
|
||||||
export const ComponentsTest: React.FC = () => {
|
export const ComponentsTest: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<Flex css={{ flexDirection: 'column' }}>
|
<Flex css={{ flexDirection: 'column' }}>
|
||||||
|
<SpinnerTest />
|
||||||
<ResolvedAddress css={{ alignSelf: 'center' }}>
|
<ResolvedAddress css={{ alignSelf: 'center' }}>
|
||||||
{'0x7ed735b7095c05d78df169f991f2b7f1a1f1a049'}
|
{'0x7ed735b7095c05d78df169f991f2b7f1a1f1a049a'}
|
||||||
</ResolvedAddress>
|
</ResolvedAddress>
|
||||||
<ComboboxTest />
|
<ComboboxTest />
|
||||||
<ColorPickerTest />
|
<ColorPickerTest />
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Flex, Spinner, SpinnerDot } from '@/components';
|
||||||
|
|
||||||
|
export const SpinnerTest: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Flex css={{ alignItems: 'center' }}>
|
||||||
|
<SpinnerDot css={{ fontSize: '$6xl' }} />
|
||||||
|
<SpinnerDot css={{ fontSize: '$4xl' }} />
|
||||||
|
<SpinnerDot css={{ fontSize: '$lg' }} />
|
||||||
|
</Flex>
|
||||||
|
<Flex css={{ alignItems: 'center' }}>
|
||||||
|
<Spinner css={{ fontSize: '$6xl' }} />
|
||||||
|
<Spinner css={{ fontSize: '$4xl' }} />
|
||||||
|
<Spinner css={{ fontSize: '$lg' }} />
|
||||||
|
</Flex>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -4,7 +4,7 @@ import { Button } from '@/components';
|
||||||
|
|
||||||
import { ExploreHeaderStyles as S } from './explore-header.styles';
|
import { ExploreHeaderStyles as S } from './explore-header.styles';
|
||||||
|
|
||||||
export const ExploreHeader: React.FC = () => (
|
export const ExploreHeaderFragment: React.FC = () => (
|
||||||
<S.Container>
|
<S.Container>
|
||||||
<S.Text>
|
<S.Text>
|
||||||
<S.GrayText>
|
<S.GrayText>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './explore-header.fragment';
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { Flex } from '@/components';
|
|
||||||
|
|
||||||
import { Explore } from '../explore.context';
|
|
||||||
import { NFAListFragment } from './nfa-list';
|
|
||||||
import { NFASearchFragment } from './nfa-search';
|
|
||||||
|
|
||||||
export const ExploreListContainer: React.FC = () => {
|
|
||||||
return (
|
|
||||||
<Flex css={{ flexDirection: 'column' }}>
|
|
||||||
<Explore.Provider>
|
|
||||||
<NFASearchFragment />
|
|
||||||
<NFAListFragment />
|
|
||||||
</Explore.Provider>
|
|
||||||
</Flex>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Explore } from '../explore.context';
|
||||||
|
import { NFAListFragment } from './nfa-list.fragment';
|
||||||
|
import { NFASearchFragment } from './nfa-search.fragment';
|
||||||
|
|
||||||
|
export const ExploreListFragment: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<Explore.Provider>
|
||||||
|
<NFASearchFragment />
|
||||||
|
<NFAListFragment />
|
||||||
|
</Explore.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1 +1 @@
|
||||||
export * from './explore-list-container';
|
export * from './explore-list.fragment';
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from '@apollo/client';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
import { Flex, NFACard, NFACardSkeleton } from '@/components';
|
import { NFACard, NFACardSkeleton } from '@/components';
|
||||||
import { lastNFAsPaginatedDocument } from '@/graphclient';
|
import { lastNFAsPaginatedDocument } from '@/graphclient';
|
||||||
import { useWindowScrollEnd } from '@/hooks';
|
import { useWindowScrollEnd } from '@/hooks';
|
||||||
|
|
||||||
import { Explore } from '../../explore.context';
|
import { Explore } from '../explore.context';
|
||||||
|
import { NFAListFragmentStyles as S } from './nfa-list.styles';
|
||||||
|
|
||||||
const pageSize = 10; //Set this size to test pagination
|
const pageSize = 10; //Set this size to test pagination
|
||||||
|
|
||||||
|
|
@ -15,6 +16,8 @@ const LoadingSkeletons: React.FC = () => (
|
||||||
<NFACardSkeleton />
|
<NFACardSkeleton />
|
||||||
<NFACardSkeleton />
|
<NFACardSkeleton />
|
||||||
<NFACardSkeleton />
|
<NFACardSkeleton />
|
||||||
|
<NFACardSkeleton />
|
||||||
|
<NFACardSkeleton />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -61,29 +64,16 @@ export const NFAListFragment: React.FC = () => {
|
||||||
if (queryError) return <div>Error</div>; //TODO handle error
|
if (queryError) return <div>Error</div>; //TODO handle error
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex
|
<S.Container>
|
||||||
css={{
|
{tokens.map((token) => (
|
||||||
flexDirection: 'column',
|
<NFACard data={token} key={token.id} />
|
||||||
gap: '$2',
|
))}
|
||||||
my: '$6',
|
|
||||||
minHeight: '50vh',
|
{isLoading && <LoadingSkeletons />}
|
||||||
marginBottom: '30vh', // TODO: remove this if we add page footer
|
|
||||||
}}
|
{!isLoading && tokens.length === 0 && (
|
||||||
>
|
<S.EmptyMessage>Nothing found.</S.EmptyMessage>
|
||||||
<Flex css={{ gap: '$6', flexWrap: 'wrap' }}>
|
)}
|
||||||
{tokens.map((token) => (
|
</S.Container>
|
||||||
<NFACard data={token} key={token.id} />
|
|
||||||
))}
|
|
||||||
{isLoading && <LoadingSkeletons />}
|
|
||||||
{!isLoading && tokens.length === 0 && (
|
|
||||||
// TODO: update this after designs are done
|
|
||||||
<div
|
|
||||||
className={`relative cursor-default select-none pt-2 px-3.5 pb-4 text-slate11 text-center`}
|
|
||||||
>
|
|
||||||
Nothing found.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { styled } from '@/theme';
|
||||||
|
|
||||||
|
export const NFAListFragmentStyles = {
|
||||||
|
Container: styled('div', {
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: 'repeat(auto-fill, minmax(12.5rem, 1fr))',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: '$6',
|
||||||
|
my: '$6',
|
||||||
|
minHeight: '50vh',
|
||||||
|
marginBottom: '30vh', // TODO: remove this if we add page footer
|
||||||
|
|
||||||
|
'@media (min-width: 1080px)': {
|
||||||
|
gridTemplateColumns: 'repeat(auto-fill, minmax(15rem, 1fr))',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
EmptyMessage: styled('span', {
|
||||||
|
padding: '$2 $3 $4 $3',
|
||||||
|
textAlign: 'center',
|
||||||
|
color: '$slate11',
|
||||||
|
width: '100%',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
export * from './nfa-list';
|
|
||||||
|
|
@ -1,16 +1,16 @@
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
import { Dropdown, DropdownItem, Flex, Input } from '@/components';
|
import { Dropdown, DropdownItem, Input } from '@/components';
|
||||||
import { useDebounce } from '@/hooks';
|
import { useDebounce } from '@/hooks';
|
||||||
|
|
||||||
import { Explore } from '../explore.context';
|
import { Explore } from '../explore.context';
|
||||||
import { ResultsContainer, ResultsNumber, ResultsText } from './results.styles';
|
import { NFASearchFragmentStyles as S } from './nfa-search.styles';
|
||||||
|
|
||||||
const orderResults: DropdownItem[] = [
|
const orderResults: DropdownItem[] = [
|
||||||
{ value: 'newest', label: 'Newest' },
|
{ value: 'newest', label: 'Newest' },
|
||||||
{ value: 'oldest', label: 'Oldest' },
|
{ value: 'oldest', label: 'Oldest' },
|
||||||
{ value: 'a-z', label: 'Name A-Z' },
|
{ value: 'a-z', label: 'Sort A-Z' },
|
||||||
{ value: 'z-a', label: 'Name Z-A' },
|
{ value: 'z-a', label: 'Sort Z-A' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const NFASearchFragment: React.FC = () => {
|
export const NFASearchFragment: React.FC = () => {
|
||||||
|
|
@ -62,17 +62,18 @@ export const NFASearchFragment: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex css={{ justifyContent: 'space-between' }}>
|
<S.Container>
|
||||||
<ResultsContainer>
|
<S.Data.Wrapper>
|
||||||
<ResultsText>All NFAs </ResultsText>
|
<S.Data.Text>All NFAs </S.Data.Text>
|
||||||
<ResultsNumber>(3,271)</ResultsNumber>
|
<S.Data.Number>(3,271)</S.Data.Number>
|
||||||
</ResultsContainer>
|
</S.Data.Wrapper>
|
||||||
<Flex css={{ gap: '$3' }}>
|
|
||||||
|
<S.Input.Wrapper>
|
||||||
<Input
|
<Input
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
leftIcon="search"
|
leftIcon="search"
|
||||||
css={{ width: '23rem' }}
|
|
||||||
onChange={handleSearchChange}
|
onChange={handleSearchChange}
|
||||||
|
wrapperClassName="flex-1"
|
||||||
/>
|
/>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
items={orderResults}
|
items={orderResults}
|
||||||
|
|
@ -82,7 +83,7 @@ export const NFASearchFragment: React.FC = () => {
|
||||||
textColor="slate11"
|
textColor="slate11"
|
||||||
optionsWidth="40"
|
optionsWidth="40"
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</S.Input.Wrapper>
|
||||||
</Flex>
|
</S.Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import { Flex } from '@/components';
|
||||||
|
import { styled } from '@/theme';
|
||||||
|
|
||||||
|
export const NFASearchFragmentStyles = {
|
||||||
|
Container: styled(Flex, {
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: '$3',
|
||||||
|
}),
|
||||||
|
|
||||||
|
Data: {
|
||||||
|
Wrapper: styled('div', {
|
||||||
|
fontSize: '$xl',
|
||||||
|
fontWeight: '$bold',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}),
|
||||||
|
|
||||||
|
Text: styled('span', {
|
||||||
|
color: '$slate12',
|
||||||
|
}),
|
||||||
|
|
||||||
|
Number: styled('span', {
|
||||||
|
color: '$slate11',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
Input: {
|
||||||
|
Wrapper: styled(Flex, {
|
||||||
|
gap: '$3',
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: '30rem',
|
||||||
|
justifySelf: 'center',
|
||||||
|
|
||||||
|
button: {
|
||||||
|
minWidth: '$28',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
import { styled } from '@/theme';
|
|
||||||
|
|
||||||
export const ResultsContainer = styled('div', {
|
|
||||||
fontSize: '$xl',
|
|
||||||
fontWeight: '$bold',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ResultsText = styled('span', {
|
|
||||||
color: '$slate12',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const ResultsNumber = styled('span', {
|
|
||||||
color: '$slate11',
|
|
||||||
});
|
|
||||||
|
|
@ -4,7 +4,5 @@ import { styled } from '@/theme';
|
||||||
export abstract class Explore {
|
export abstract class Explore {
|
||||||
static readonly Container = styled(Flex, {
|
static readonly Container = styled(Flex, {
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
width: '64.75rem', //TODO replace for max-width
|
|
||||||
margin: '0 auto',
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { Explore as ES } from './explore.styles';
|
import { Explore as ES } from './explore.styles';
|
||||||
import { ExploreHeader } from './explore-header';
|
import { ExploreHeaderFragment } from './explore-header';
|
||||||
import { ExploreListContainer } from './explore-list';
|
import { ExploreListFragment } from './explore-list';
|
||||||
|
|
||||||
export const Explore: React.FC = () => {
|
export const ExploreView: React.FC = () => {
|
||||||
return (
|
return (
|
||||||
<ES.Container>
|
<ES.Container>
|
||||||
<ExploreHeader />
|
<ExploreHeaderFragment />
|
||||||
<ExploreListContainer />
|
<ExploreListFragment />
|
||||||
</ES.Container>
|
</ES.Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -3,3 +3,4 @@ export * from './mint';
|
||||||
export * from './components-test';
|
export * from './components-test';
|
||||||
export * from './explore';
|
export * from './explore';
|
||||||
export * from './access-point';
|
export * from './access-point';
|
||||||
|
export * from './indexed-nfa';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { Button, Flex, Icon, NFAPreview } from '@/components';
|
||||||
|
|
||||||
|
import { IndexedNFA } from '../indexed-nfa.context';
|
||||||
|
import { IndexedNFAStyles as S } from '../indexed-nfa.styles';
|
||||||
|
|
||||||
|
const Preview: React.FC = () => {
|
||||||
|
const { nfa } = IndexedNFA.useContext();
|
||||||
|
|
||||||
|
const color = useMemo(
|
||||||
|
// TODO: replace with util function
|
||||||
|
() => `#${`000000${nfa.color.toString(16)}`.slice(-6)}`,
|
||||||
|
[nfa]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NFAPreview
|
||||||
|
color={color}
|
||||||
|
logo={nfa.logo}
|
||||||
|
ens={nfa.ENS}
|
||||||
|
name={nfa.name}
|
||||||
|
size="100%"
|
||||||
|
css={{
|
||||||
|
borderRadius: '$lg',
|
||||||
|
border: '1px solid $slate6',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CreateAccessPoint: React.FC = () => {
|
||||||
|
const { nfa } = IndexedNFA.useContext();
|
||||||
|
return (
|
||||||
|
<S.Aside.CreateAccessPoint.Container>
|
||||||
|
<S.Aside.CreateAccessPoint.Heading>
|
||||||
|
Host NFA Frontend
|
||||||
|
</S.Aside.CreateAccessPoint.Heading>
|
||||||
|
{/* TODO: replace with correct text */}
|
||||||
|
|
||||||
|
<S.Aside.CreateAccessPoint.Text>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce vitae
|
||||||
|
ante erat. Sed quis finibus diam.
|
||||||
|
</S.Aside.CreateAccessPoint.Text>
|
||||||
|
|
||||||
|
<Flex css={{ gap: '$3' }}>
|
||||||
|
<Button as={Link} to={`/create-ap/${nfa.tokenId}`} colorScheme="blue">
|
||||||
|
Host NFA Frontend
|
||||||
|
</Button>
|
||||||
|
<S.Aside.CreateAccessPoint.Extra href="">
|
||||||
|
{/* TODO: place correct href */}
|
||||||
|
Learn more
|
||||||
|
<Icon name="chevron-right" />
|
||||||
|
</S.Aside.CreateAccessPoint.Extra>
|
||||||
|
</Flex>
|
||||||
|
</S.Aside.CreateAccessPoint.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IndexedNFAAsideFragment: React.FC = () => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const [top, setTop] = useState<number>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTop(ref.current?.getBoundingClientRect().top);
|
||||||
|
}, [ref]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<S.Aside.Container ref={ref} css={{ top }}>
|
||||||
|
<Preview />
|
||||||
|
<CreateAccessPoint />
|
||||||
|
</S.Aside.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './aside.fragment';
|
||||||
|
export * from './main.fragment';
|
||||||
|
export * from './skeleton.fragment';
|
||||||
|
|
@ -0,0 +1,285 @@
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
|
||||||
|
import { Flex, Icon, IconName, ResolvedAddress, Text } from '@/components';
|
||||||
|
|
||||||
|
import { IndexedNFA } from '../indexed-nfa.context';
|
||||||
|
import { IndexedNFAStyles as S } from '../indexed-nfa.styles';
|
||||||
|
|
||||||
|
type HeaderDataProps = {
|
||||||
|
label: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const HeaderData: React.FC<HeaderDataProps> = ({
|
||||||
|
label,
|
||||||
|
children,
|
||||||
|
}: HeaderDataProps) => (
|
||||||
|
<Flex css={{ gap: '$2' }}>
|
||||||
|
<Text css={{ color: '$slate11' }}>{label}</Text>
|
||||||
|
<Text css={{ color: '$slate12' }}>{children}</Text>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Header: React.FC = () => {
|
||||||
|
const { nfa } = IndexedNFA.useContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<S.Main.Heading>{nfa.name}</S.Main.Heading>
|
||||||
|
<Flex css={{ justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<HeaderData label="Owner">
|
||||||
|
<ResolvedAddress>{nfa.owner.id}</ResolvedAddress>
|
||||||
|
</HeaderData>
|
||||||
|
|
||||||
|
<S.Main.Divider.Elipse />
|
||||||
|
|
||||||
|
<HeaderData label="Created">
|
||||||
|
{/* TODO: place correct data */}
|
||||||
|
12/12/22
|
||||||
|
</HeaderData>
|
||||||
|
|
||||||
|
<S.Main.Divider.Elipse />
|
||||||
|
|
||||||
|
<HeaderData label="Access Points">
|
||||||
|
{nfa.accessPoints?.length ?? 0}
|
||||||
|
</HeaderData>
|
||||||
|
</Flex>
|
||||||
|
<S.Main.Divider.Line />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Description: React.FC = () => {
|
||||||
|
const { nfa } = IndexedNFA.useContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<S.Main.SectionHeading css={{ marginTop: 0 }}>
|
||||||
|
Description
|
||||||
|
</S.Main.SectionHeading>
|
||||||
|
<S.Main.DataContainer as={S.Main.Paragraph}>
|
||||||
|
{nfa.description}
|
||||||
|
</S.Main.DataContainer>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type DataWrapperProps = React.PropsWithChildren<{
|
||||||
|
label: string | number;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
const DataWrapper: React.FC<DataWrapperProps> = ({
|
||||||
|
children,
|
||||||
|
label,
|
||||||
|
}: DataWrapperProps) => (
|
||||||
|
<S.Main.DataContainer key={label} css={{ flex: 1, minWidth: '45%' }}>
|
||||||
|
<Text css={{ color: '$slate12', fontWeight: 700 }}>{children || '-'}</Text>
|
||||||
|
<Text css={{ color: '$slate11' }}>{label}</Text>
|
||||||
|
</S.Main.DataContainer>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Traits: React.FC = () => {
|
||||||
|
const { nfa } = IndexedNFA.useContext();
|
||||||
|
|
||||||
|
// TODO: place correct data
|
||||||
|
const traitsToShow = useMemo(() => {
|
||||||
|
return [
|
||||||
|
[nfa.ENS, 'ENS'],
|
||||||
|
[nfa.gitRepository.id, 'Repository'],
|
||||||
|
[10, 'Version'],
|
||||||
|
[nfa.externalURL, 'Domain'],
|
||||||
|
[nfa.externalURL, 'Domain 2'],
|
||||||
|
];
|
||||||
|
}, [nfa]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<S.Main.SectionHeading>Traits</S.Main.SectionHeading>
|
||||||
|
<S.Main.DataList>
|
||||||
|
{traitsToShow.map(([value, label]) => (
|
||||||
|
<DataWrapper key={label} label={label}>
|
||||||
|
{value}
|
||||||
|
</DataWrapper>
|
||||||
|
))}
|
||||||
|
</S.Main.DataList>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
type VerificationBannerProps = {
|
||||||
|
verified: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const VerificationBanner: React.FC<VerificationBannerProps> = ({
|
||||||
|
verified,
|
||||||
|
}: VerificationBannerProps) => {
|
||||||
|
const [text, icon] = useMemo<[string, IconName]>(() => {
|
||||||
|
if (verified)
|
||||||
|
return ['This Non Fungible Application is Verified.', 'verified'];
|
||||||
|
return ['This Non Fungible Application is not Verified.', 'error'];
|
||||||
|
}, [verified]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<S.Main.VerificationBanner verified={verified}>
|
||||||
|
{text}
|
||||||
|
<Icon
|
||||||
|
name={icon}
|
||||||
|
css={{
|
||||||
|
fontSize: '3.5rem',
|
||||||
|
color: '$black',
|
||||||
|
position: 'absolute',
|
||||||
|
right: 'calc(8% - 1.75rem)',
|
||||||
|
zIndex: 1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</S.Main.VerificationBanner>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Verification: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<S.Main.SectionHeading>Verification</S.Main.SectionHeading>
|
||||||
|
{/* TODO: Get verified from context */}
|
||||||
|
<VerificationBanner verified={Math.random() > 0.5} />
|
||||||
|
<S.Main.DataList>
|
||||||
|
{/* TODO: place correct data */}
|
||||||
|
<DataWrapper label="Verifier">polygon.eth</DataWrapper>
|
||||||
|
<DataWrapper label="Repository">polygon/fe</DataWrapper>
|
||||||
|
</S.Main.DataList>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: replace mocks with fetched data
|
||||||
|
const apMocks = new Array(10).fill(0).map((_, index) => ({
|
||||||
|
approved: Math.random() > 0.5,
|
||||||
|
domain: `domain${index}.com`,
|
||||||
|
owner: '0x7ED735b7095C05d78dF169F991f2b7f1A1F1A049',
|
||||||
|
createdAt: `${Math.floor(Math.random() * 30)}m ago`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const AccessPoints: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<S.Main.SectionHeading>Frontends</S.Main.SectionHeading>
|
||||||
|
<S.Main.Table.Container>
|
||||||
|
<S.Main.Table.Root>
|
||||||
|
<colgroup>
|
||||||
|
<col span={1} style={{ width: '9.5%' }} />
|
||||||
|
<col span={1} style={{ width: '32.5%' }} />
|
||||||
|
<col span={1} style={{ width: '32.5%' }} />
|
||||||
|
<col span={1} style={{ width: '16%' }} />
|
||||||
|
<col span={1} style={{ width: '9.5%' }} />
|
||||||
|
</colgroup>
|
||||||
|
<S.Main.Table.Head>
|
||||||
|
<S.Main.Table.Row>
|
||||||
|
<S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Marker />
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>Domain</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>Owner</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>Created</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data />
|
||||||
|
</S.Main.Table.Row>
|
||||||
|
</S.Main.Table.Head>
|
||||||
|
<S.Main.Table.Body>
|
||||||
|
{apMocks.map((item) => (
|
||||||
|
<S.Main.Table.Row key={item.domain}>
|
||||||
|
<S.Main.Table.Data align="center">
|
||||||
|
<S.Main.Table.Marker
|
||||||
|
variant={item.approved ? 'active' : 'inactive'}
|
||||||
|
/>
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>{item.domain}</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>
|
||||||
|
<ResolvedAddress>{item.owner}</ResolvedAddress>
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>{item.createdAt}</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>
|
||||||
|
<Icon name="external-link" />
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
</S.Main.Table.Row>
|
||||||
|
))}
|
||||||
|
</S.Main.Table.Body>
|
||||||
|
</S.Main.Table.Root>
|
||||||
|
</S.Main.Table.Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: replace mocks with fetched data
|
||||||
|
const versionsMock = new Array(10).fill(0).map((_, index) => ({
|
||||||
|
live: index === 0,
|
||||||
|
commit: (Math.random() * 0xfffffffff).toString(16),
|
||||||
|
preview: `test: subgraph matchstick tests for access points and acl refactor (#150
|
||||||
|
)
|
||||||
|
|
||||||
|
* fix: errors from deprecated entities.`,
|
||||||
|
time: `${Math.floor(Math.random() * 30)}m ago`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const Versions: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<S.Main.SectionHeading>Versions</S.Main.SectionHeading>
|
||||||
|
<S.Main.Table.Container>
|
||||||
|
<S.Main.Table.Root>
|
||||||
|
<colgroup>
|
||||||
|
<col span={1} style={{ width: '9.5%' }} />
|
||||||
|
<col span={1} style={{ width: '15%' }} />
|
||||||
|
<col span={1} style={{ width: '50%' }} />
|
||||||
|
<col span={1} style={{ width: '16%' }} />
|
||||||
|
<col span={1} style={{ width: '9.5%' }} />
|
||||||
|
</colgroup>
|
||||||
|
<S.Main.Table.Head>
|
||||||
|
<S.Main.Table.Row>
|
||||||
|
<S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Marker />
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>Commit</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>Preview</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>Time</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data />
|
||||||
|
</S.Main.Table.Row>
|
||||||
|
</S.Main.Table.Head>
|
||||||
|
<S.Main.Table.Body>
|
||||||
|
{versionsMock.map((item) => (
|
||||||
|
<S.Main.Table.Row key={item.commit}>
|
||||||
|
<S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Marker
|
||||||
|
variant={item.live ? 'active' : 'inactive'}
|
||||||
|
text={item.live}
|
||||||
|
>
|
||||||
|
{item.live && 'Live'}
|
||||||
|
</S.Main.Table.Marker>
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>{item.commit.slice(0, 6)}</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data title={item.preview}>
|
||||||
|
{item.preview}
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>{item.time}</S.Main.Table.Data>
|
||||||
|
<S.Main.Table.Data>
|
||||||
|
<Icon name="external-link" />
|
||||||
|
</S.Main.Table.Data>
|
||||||
|
</S.Main.Table.Row>
|
||||||
|
))}
|
||||||
|
</S.Main.Table.Body>
|
||||||
|
</S.Main.Table.Root>
|
||||||
|
</S.Main.Table.Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IndexedNFAMainFragment: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<S.Main.Container>
|
||||||
|
<Header />
|
||||||
|
<Description />
|
||||||
|
<Traits />
|
||||||
|
<Verification />
|
||||||
|
<AccessPoints />
|
||||||
|
<Versions />
|
||||||
|
</S.Main.Container>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { IndexedNFAStyles as S } from '../indexed-nfa.styles';
|
||||||
|
|
||||||
|
export const IndexedNFASkeletonFragment: React.FC = () => (
|
||||||
|
<S.Grid>
|
||||||
|
<S.Aside.Container>
|
||||||
|
<S.Skeleton css={{ aspectRatio: 1, width: '100%' }} />
|
||||||
|
</S.Aside.Container>
|
||||||
|
<S.Main.Container css={{ justifyContent: 'stretch' }}>
|
||||||
|
<S.Skeleton css={{ height: '2.875rem' }} />
|
||||||
|
<S.Skeleton css={{ height: '1.5rem' }} />
|
||||||
|
<S.Main.Divider.Line />
|
||||||
|
<S.Skeleton css={{ height: '10rem' }} />
|
||||||
|
<S.Skeleton css={{ height: '15rem' }} />
|
||||||
|
</S.Main.Container>
|
||||||
|
</S.Grid>
|
||||||
|
);
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './indexed-nfa';
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { Owner, Token } from '@/graphclient';
|
||||||
|
import { createContext } from '@/utils';
|
||||||
|
|
||||||
|
const [Provider, useContext] = createContext<IndexedNFA.Context>({
|
||||||
|
name: 'IndexedNFA.Context',
|
||||||
|
hookName: 'IndexedNFA.useContext',
|
||||||
|
providerName: 'IndexedNFA.Provider',
|
||||||
|
});
|
||||||
|
|
||||||
|
export const IndexedNFA = {
|
||||||
|
useContext,
|
||||||
|
Provider: ({ children, nfa }: IndexedNFA.ProviderProps): JSX.Element => {
|
||||||
|
return <Provider value={{ nfa }}>{children}</Provider>;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export namespace IndexedNFA {
|
||||||
|
export type Context = {
|
||||||
|
nfa: Omit<Token, 'mintTransaction' | 'id' | 'owner'> & {
|
||||||
|
owner: Pick<Owner, 'id'>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ProviderProps = {
|
||||||
|
children: React.ReactNode | React.ReactNode[];
|
||||||
|
nfa: Context['nfa'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,241 @@
|
||||||
|
import { Skeleton } from '@/components';
|
||||||
|
import { styled } from '@/theme';
|
||||||
|
|
||||||
|
const Spacing = '$5';
|
||||||
|
|
||||||
|
export const IndexedNFAStyles = {
|
||||||
|
Grid: styled('div', {
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateAreas: '"aside main"',
|
||||||
|
gridTemplateColumns: '24.0625rem 1fr',
|
||||||
|
gridTemplateRows: 'fit-content',
|
||||||
|
gap: `calc(2 * ${Spacing})`,
|
||||||
|
padding: Spacing,
|
||||||
|
|
||||||
|
'@media (max-width: 1080px)': {
|
||||||
|
gridTemplateColumns: '20rem 1fr',
|
||||||
|
},
|
||||||
|
|
||||||
|
'@media (max-width: 580px)': {
|
||||||
|
gridTemplateAreas: '"aside" "main"',
|
||||||
|
gridTemplateColumns: '1fr',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
Aside: {
|
||||||
|
Container: styled('aside', {
|
||||||
|
gridArea: 'aside',
|
||||||
|
position: 'sticky',
|
||||||
|
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: Spacing,
|
||||||
|
height: 'fit-content',
|
||||||
|
|
||||||
|
'@media (max-width: 580px)': {
|
||||||
|
position: 'static',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
CreateAccessPoint: {
|
||||||
|
Container: styled('div', {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: Spacing,
|
||||||
|
padding: Spacing,
|
||||||
|
backgroundColor: '$blue1',
|
||||||
|
borderRadius: '$lg',
|
||||||
|
}),
|
||||||
|
Heading: styled('h2', {
|
||||||
|
fontSize: '$md',
|
||||||
|
color: '$slate12',
|
||||||
|
}),
|
||||||
|
Text: styled('p', {
|
||||||
|
fontSize: '$sm',
|
||||||
|
color: '$slate11',
|
||||||
|
}),
|
||||||
|
Extra: styled('a', {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
color: '$slate11',
|
||||||
|
fontSize: '$sm',
|
||||||
|
gap: '$2',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Main: {
|
||||||
|
Container: styled('main', {
|
||||||
|
gridArea: 'main',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: Spacing,
|
||||||
|
}),
|
||||||
|
Heading: styled('h1', {
|
||||||
|
fontSize: '2.125rem',
|
||||||
|
lineHeight: 1.35,
|
||||||
|
fontWeight: 700,
|
||||||
|
}),
|
||||||
|
SectionHeading: styled('h2', {
|
||||||
|
fontSize: '$xl',
|
||||||
|
lineHeight: 1.2,
|
||||||
|
fontWeight: 700,
|
||||||
|
marginTop: Spacing,
|
||||||
|
}),
|
||||||
|
Divider: {
|
||||||
|
Line: styled('span', {
|
||||||
|
width: '100%',
|
||||||
|
borderBottom: '1px solid $slate6',
|
||||||
|
}),
|
||||||
|
Elipse: styled('span', {
|
||||||
|
width: '0.375rem',
|
||||||
|
height: '0.375rem',
|
||||||
|
backgroundColor: '$slate4',
|
||||||
|
borderRadius: '100%',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
Paragraph: styled('p', {
|
||||||
|
color: '$slate11',
|
||||||
|
lineHeight: 1.43,
|
||||||
|
}),
|
||||||
|
DataContainer: styled('div', {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
border: '1px solid $slate6',
|
||||||
|
borderRadius: '$lg',
|
||||||
|
padding: Spacing,
|
||||||
|
gap: `$1`,
|
||||||
|
}),
|
||||||
|
DataList: styled('div', {
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: '$5',
|
||||||
|
}),
|
||||||
|
VerificationBanner: styled('div', {
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
border: '1px solid $slate6',
|
||||||
|
borderRadius: '$lg',
|
||||||
|
padding: '$8 $5',
|
||||||
|
fontWeight: 700,
|
||||||
|
overflow: 'hidden',
|
||||||
|
|
||||||
|
'&:after': {
|
||||||
|
content: '""',
|
||||||
|
position: 'absolute',
|
||||||
|
right: '-$5',
|
||||||
|
top: '-$10',
|
||||||
|
bottom: '-$10',
|
||||||
|
left: '84%',
|
||||||
|
borderRadius: '80% 0 0 80%',
|
||||||
|
},
|
||||||
|
|
||||||
|
variants: {
|
||||||
|
verified: {
|
||||||
|
true: {
|
||||||
|
borderColor: '$green11',
|
||||||
|
color: '$green11',
|
||||||
|
'&:after': {
|
||||||
|
backgroundColor: '$green11',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false: {
|
||||||
|
borderColor: '$red11',
|
||||||
|
color: '$red11',
|
||||||
|
'&:after': {
|
||||||
|
backgroundColor: '$red11',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
Table: {
|
||||||
|
Container: styled('div', {
|
||||||
|
border: '1px solid $slate6',
|
||||||
|
borderRadius: '10px',
|
||||||
|
padding: '0 $5',
|
||||||
|
|
||||||
|
maxHeight: '15.125rem',
|
||||||
|
overflow: 'auto',
|
||||||
|
}),
|
||||||
|
Root: styled('table', {
|
||||||
|
width: 'calc(100% + 2 * $space$5)',
|
||||||
|
margin: '0 -$5',
|
||||||
|
}),
|
||||||
|
Head: styled('thead', {
|
||||||
|
position: 'sticky',
|
||||||
|
top: 0,
|
||||||
|
backgroundColor: '$black',
|
||||||
|
|
||||||
|
'&:after': {
|
||||||
|
position: 'absolute',
|
||||||
|
content: '""',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
borderBottom: '1px solid $slate6',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Row: styled('tr'),
|
||||||
|
Data: styled('td', {
|
||||||
|
padding: '$3',
|
||||||
|
maxWidth: '10rem',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
}),
|
||||||
|
Body: styled('tbody', {
|
||||||
|
tr: {
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: '$slate6',
|
||||||
|
cursor: 'pointer',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
Marker: styled('span', {
|
||||||
|
display: 'block',
|
||||||
|
margin: 'auto',
|
||||||
|
width: '0.5625rem',
|
||||||
|
height: '0.5625rem',
|
||||||
|
borderRadius: '$full',
|
||||||
|
backgroundColor: '$slate6',
|
||||||
|
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
active: {
|
||||||
|
backgroundColor: '$green11',
|
||||||
|
},
|
||||||
|
inactive: {
|
||||||
|
backgroundColor: '$slate8',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
true: {
|
||||||
|
fontSize: '$xs',
|
||||||
|
padding: '0 $2',
|
||||||
|
width: 'fit-content',
|
||||||
|
height: 'fit-content',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
compoundVariants: [
|
||||||
|
{
|
||||||
|
variant: 'active',
|
||||||
|
text: true,
|
||||||
|
css: {
|
||||||
|
color: '$green11',
|
||||||
|
backgroundColor: '$green3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Skeleton: styled(Skeleton, {
|
||||||
|
borderRadius: '$lg',
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { useQuery } from '@apollo/client';
|
||||||
|
import { ethers } from 'ethers';
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { getNFADocument } from '@/graphclient';
|
||||||
|
import { NFAMock } from '@/mocks';
|
||||||
|
import { AppLog } from '@/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
IndexedNFAAsideFragment,
|
||||||
|
IndexedNFAMainFragment,
|
||||||
|
IndexedNFASkeletonFragment,
|
||||||
|
} from './fragments';
|
||||||
|
import { IndexedNFA } from './indexed-nfa.context';
|
||||||
|
import { IndexedNFAStyles as S } from './indexed-nfa.styles';
|
||||||
|
|
||||||
|
export const IndexedNFAView: React.FC = () => {
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleError = (error: unknown): void => {
|
||||||
|
AppLog.errorToast(
|
||||||
|
`It was not possible to find the NFA with id "${id}"`,
|
||||||
|
error
|
||||||
|
);
|
||||||
|
navigate('/', { replace: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
const { loading, data = { token: {} } } = useQuery(getNFADocument, {
|
||||||
|
skip: id === undefined,
|
||||||
|
variables: {
|
||||||
|
id: ethers.utils.hexlify(Number(id)),
|
||||||
|
},
|
||||||
|
onCompleted(data) {
|
||||||
|
if (!data.token) handleError(new Error('Token not found'));
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
handleError(error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return <IndexedNFASkeletonFragment />;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: replace NFAMock with real data from useQuery
|
||||||
|
return (
|
||||||
|
<IndexedNFA.Provider nfa={{ ...NFAMock, ...data.token }}>
|
||||||
|
<S.Grid>
|
||||||
|
<IndexedNFAAsideFragment />
|
||||||
|
<IndexedNFAMainFragment />
|
||||||
|
</S.Grid>
|
||||||
|
</IndexedNFA.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Button, Card, Flex, Stepper } from '@/components';
|
import { Button, Card, CardTag, Flex, Stepper } from '@/components';
|
||||||
import { Mint } from '@/views/mint/mint.context';
|
import { Mint } from '@/views/mint/mint.context';
|
||||||
import { useMintFormContext } from '@/views/mint/nfa-step/form-step';
|
import { useMintFormContext } from '@/views/mint/nfa-step/form-step';
|
||||||
|
|
||||||
|
|
@ -25,17 +25,8 @@ export const RepoConfigurationBody: React.FC = () => {
|
||||||
<Flex css={{ rowGap: '$6', flexDirection: 'column' }}>
|
<Flex css={{ rowGap: '$6', flexDirection: 'column' }}>
|
||||||
<RepoRow
|
<RepoRow
|
||||||
repo={repositoryName.name}
|
repo={repositoryName.name}
|
||||||
css={{ mb: '0' }}
|
css={{ mb: '0', cursor: 'default' }}
|
||||||
button={
|
button={<CardTag>Use for NFA</CardTag>}
|
||||||
<Button
|
|
||||||
colorScheme="gray"
|
|
||||||
disabled
|
|
||||||
variant="outline"
|
|
||||||
css={{ py: '$1', height: '$5', borderRadius: '$md' }}
|
|
||||||
>
|
|
||||||
Use for NFA
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
<RepoBranchCommitFields />
|
<RepoBranchCommitFields />
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ export const Repository: React.FC<RepositoryProps> = ({
|
||||||
<RepoRow
|
<RepoRow
|
||||||
onClick={handleSelectRepo}
|
onClick={handleSelectRepo}
|
||||||
repo={repository.name}
|
repo={repository.name}
|
||||||
|
css={{ cursor: 'pointer' }}
|
||||||
button={
|
button={
|
||||||
<Button
|
<Button
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ export const RepoRow = forwardRef<HTMLDivElement, RepoRowProps>(
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
my: '$4',
|
my: '$4',
|
||||||
...props.css,
|
...props.css,
|
||||||
cursor: 'pointer',
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex css={{ alignItems: 'center' }}>
|
<Flex css={{ alignItems: 'center' }}>
|
||||||
|
|
|
||||||
|
|
@ -18,39 +18,37 @@ export const MintStepper: React.FC = () => {
|
||||||
},
|
},
|
||||||
} = useMintFormContext();
|
} = useMintFormContext();
|
||||||
|
|
||||||
if (!isSuccess) {
|
if (isSuccess) return <NftMinted />;
|
||||||
return (
|
|
||||||
<Stepper.Root initialStep={1}>
|
|
||||||
<Form.Root onValidationChange={setIsValid}>
|
|
||||||
<Stepper.Container>
|
|
||||||
<Stepper.Step>
|
|
||||||
<Step header="Connect your Ethereum Wallet to mint an NFA">
|
|
||||||
<WalletStep />
|
|
||||||
</Step>
|
|
||||||
</Stepper.Step>
|
|
||||||
|
|
||||||
<Stepper.Step>
|
return (
|
||||||
<Step header="Connect GitHub and select repository">
|
<Stepper.Root initialStep={1}>
|
||||||
<GithubStep />
|
<Form.Root onValidationChange={setIsValid}>
|
||||||
</Step>
|
<Stepper.Container>
|
||||||
</Stepper.Step>
|
<Stepper.Step>
|
||||||
|
<Step header="Connect your Ethereum Wallet to mint an NFA">
|
||||||
|
<WalletStep />
|
||||||
|
</Step>
|
||||||
|
</Stepper.Step>
|
||||||
|
|
||||||
<Stepper.Step>
|
<Stepper.Step>
|
||||||
<Step header="Finalize a few key things for your NFA">
|
<Step header="Connect GitHub and select repository">
|
||||||
<NFAStep />
|
<GithubStep />
|
||||||
</Step>
|
</Step>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
|
|
||||||
<Stepper.Step>
|
<Stepper.Step>
|
||||||
<Step header="Review your NFA and mint it on Ethereum">
|
<Step header="Finalize a few key things for your NFA">
|
||||||
<MintPreview />
|
<NFAStep />
|
||||||
</Step>
|
</Step>
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
</Stepper.Container>
|
|
||||||
</Form.Root>
|
<Stepper.Step>
|
||||||
</Stepper.Root>
|
<Step header="Review your NFA and mint it on Ethereum">
|
||||||
);
|
<MintPreview />
|
||||||
} else {
|
</Step>
|
||||||
return <NftMinted />;
|
</Stepper.Step>
|
||||||
}
|
</Stepper.Container>
|
||||||
|
</Form.Root>
|
||||||
|
</Stepper.Root>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,3 @@ export const fileToBase64 = (file: File): Promise<string> =>
|
||||||
reader.onload = () => resolve(reader.result?.toString() || '');
|
reader.onload = () => resolve(reader.result?.toString() || '');
|
||||||
reader.onerror = reject;
|
reader.onerror = reject;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a hex color string to a number.
|
|
||||||
*/
|
|
||||||
export const parseColorToNumber = (color: string): number => {
|
|
||||||
const hexColor = color.replace('#', '');
|
|
||||||
return parseInt(hexColor, 16);
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { useAccount } from 'wagmi';
|
||||||
|
|
||||||
import { Button, Card, Grid, Stepper } from '@/components';
|
import { Button, Card, Grid, Stepper } from '@/components';
|
||||||
import { AppLog } from '@/utils';
|
import { AppLog } from '@/utils';
|
||||||
|
import { parseColorToNumber } from '@/utils/color';
|
||||||
|
|
||||||
import { Mint } from '../../mint.context';
|
import { Mint } from '../../mint.context';
|
||||||
import { MintCardHeader } from '../../mint-card';
|
import { MintCardHeader } from '../../mint-card';
|
||||||
|
|
@ -11,7 +12,6 @@ import {
|
||||||
EnsDomainField,
|
EnsDomainField,
|
||||||
LogoField,
|
LogoField,
|
||||||
} from './fields';
|
} from './fields';
|
||||||
import { parseColorToNumber } from './form.utils';
|
|
||||||
import { useMintFormContext } from './mint-form.context';
|
import { useMintFormContext } from './mint-form.context';
|
||||||
|
|
||||||
export const MintFormStep: React.FC = () => {
|
export const MintFormStep: React.FC = () => {
|
||||||
|
|
@ -54,7 +54,7 @@ export const MintFormStep: React.FC = () => {
|
||||||
AppLog.errorToast('No address found. Please connect your wallet.');
|
AppLog.errorToast('No address found. Please connect your wallet.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// setting the args otherwise mint may fail
|
|
||||||
setArgs([
|
setArgs([
|
||||||
address,
|
address,
|
||||||
appName,
|
appName,
|
||||||
|
|
@ -62,7 +62,7 @@ export const MintFormStep: React.FC = () => {
|
||||||
domainURL,
|
domainURL,
|
||||||
ens,
|
ens,
|
||||||
gitCommit,
|
gitCommit,
|
||||||
`${repositoryName.url}/tree/${gitBranch}`,
|
`${repositoryName?.url}/tree/${gitBranch}`,
|
||||||
appLogo,
|
appLogo,
|
||||||
parseColorToNumber(logoColor),
|
parseColorToNumber(logoColor),
|
||||||
verifyNFA,
|
verifyNFA,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue