Skip to content

Commit 7222936

Browse files
authored
Merge pull request #2 from codeSTACKr/netlify-functions
collection integration
2 parents 10001ec + df39fcc commit 7222936

File tree

10 files changed

+304
-70
lines changed

10 files changed

+304
-70
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
node_modules
2+
.env
3+
package-lock.json
4+
# Local Netlify folder
5+
.netlify

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,18 @@ This repo is a work-in-progress and pairs with my Mint 10k NFT project.
44

55
## Video Walkthrough
66

7+
### Getting Started & Integrate MetaMask
8+
9+
[![Thumbnail](https://img.youtube.com/vi/WZQSVv67NBc/maxresdefault.jpg)](https://youtu.be/WZQSVv67NBc)
10+
11+
Associated Source Code: [v1.0.0](https://github.com/codeSTACKr/nft-landing-page/releases/tag/v1.0.0)
12+
13+
### Collection Integration & Owner Check
14+
715
[![Thumbnail](https://img.youtube.com/vi/WZQSVv67NBc/maxresdefault.jpg)](https://youtu.be/WZQSVv67NBc)
816

17+
Associated Source Code: [v1.1.0](https://github.com/codeSTACKr/nft-landing-page/releases/tag/v1.1.0)
18+
919
## How to Create and Mint 10k NFTs
1020

1121
[![Thumbnail](https://img.youtube.com/vi/AaCgydeMu64/maxresdefault.jpg)](https://youtu.be/AaCgydeMu64)
@@ -24,4 +34,4 @@ This repo is a work-in-progress and pairs with my Mint 10k NFT project.
2434
- Fonts
2535
- Colors
2636

27-
## Watch the [video walkthrough](#video-walkthrough) above for more detailed instructions.
37+
## Watch the [video walkthroughs](#video-walkthrough) above for more detailed instructions.

app.js

Lines changed: 0 additions & 66 deletions
This file was deleted.

style.css renamed to css/style.css

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ body {
3131
}
3232

3333
.container {
34+
display: flex;
35+
flex-direction: column;
3436
max-width: 960px;
3537
margin: 0 auto;
3638
padding: 1rem 2rem;
@@ -43,7 +45,7 @@ header {
4345
}
4446

4547
header .container {
46-
display: flex;
48+
flex-direction: row;
4749
justify-content: space-between;
4850
align-items: center;
4951
}
@@ -153,6 +155,15 @@ section {
153155
text-align: center;
154156
}
155157

158+
/* OWNER STYLES */
159+
160+
.owner-status {
161+
font-size: 2rem;
162+
text-align: center;
163+
}
164+
165+
/* MEDIA QUERIES */
166+
156167
@media screen and (max-width: 768px) {
157168
.menu {
158169
gap: 1rem;

functions/isowner.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const fetch = require('node-fetch')
2+
3+
const CONTRACT = process.env.CONTRACT_ADDRESS;
4+
const AUTH = process.env.NFTPORT_AUTH;
5+
const chain = "polygon";
6+
const include = "metadata";
7+
8+
exports.handler = async (event, context) => {
9+
const wallet = event.queryStringParameters && event.queryStringParameters.wallet
10+
const page = event.queryStringParameters && event.queryStringParameters.page
11+
12+
const isOwner = (wallet) => {
13+
if(!wallet) {
14+
return {
15+
isOwner: false
16+
}
17+
} else {
18+
return getOwnedNfts(wallet, page)
19+
}
20+
}
21+
22+
const response = await isOwner(wallet)
23+
24+
return {
25+
'statusCode': 200,
26+
'headers': {
27+
'Cache-Control': 'no-cache',
28+
'Content-Type': 'application/json',
29+
},
30+
'body': JSON.stringify(response)
31+
}
32+
}
33+
34+
const getOwnedNfts = async (wallet, page) => {
35+
const url = `https://api.nftport.xyz/v0/accounts/${wallet}/?`;
36+
37+
const options = {
38+
method: 'GET',
39+
headers: {
40+
'Content-Type': 'application/json',
41+
Authorization: AUTH
42+
}
43+
};
44+
const query = new URLSearchParams({
45+
chain,
46+
include,
47+
page_number: page
48+
});
49+
50+
let editions = []
51+
try {
52+
const data = await fetchData(url + query, options)
53+
console.log(`Recieved page ${page}`)
54+
const total = data.total;
55+
const pages = Math.ceil(total / 50);
56+
data.nfts.forEach(nft => {
57+
if(nft.contract_address === CONTRACT) {
58+
editions.push(nft.token_id)
59+
}
60+
})
61+
62+
return {
63+
isOwner: editions.length > 0 ? true : false,
64+
editions,
65+
next_page: +page === pages ? null : +page + 1,
66+
}
67+
} catch(err) {
68+
console.log(`Catch: ${JSON.stringify(err)}`)
69+
return {
70+
error: err
71+
}
72+
}
73+
}
74+
75+
async function fetchData(url, options) {
76+
return new Promise((resolve, reject) => {
77+
return fetch(url, options).then(res => {
78+
const status = res.status;
79+
80+
if(status === 200) {
81+
return resolve(res.json());
82+
} else {
83+
console.log(`Fetch failed with status ${status}`);
84+
return reject(res.json());
85+
}
86+
}).catch(function (error) {
87+
reject(error)
88+
});
89+
});
90+
}

index.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<link rel="icon" type="image/png" sizes="32x32" href="images/x-icon/favicon-32x32.png">
1212
<link rel="icon" type="image/png" sizes="16x16" href="images/x-icon/favicon-16x16.png">
1313
<link rel="manifest" href="images/x-icon/site.webmanifest">
14-
<link rel="stylesheet" href="style.css" />
14+
<link rel="stylesheet" href="css/style.css" />
1515
</head>
1616
<body>
1717
<header>
@@ -51,6 +51,7 @@
5151
</header>
5252

5353
<section class="container">
54+
<div class="owner-status"></div>
5455
<div class="countdown">
5556
<ul
5657
id="countdown"
@@ -87,6 +88,7 @@ <h1>NFT Drop Coming Soon!!</h1>
8788
</section>
8889

8990
<script src="https://cdn.jsdelivr.net/npm/@metamask/[email protected]/dist/metamask-onboarding.bundle.js"></script>
90-
<script src="app.js"></script>
91+
<script src="js/countdown.js"></script>
92+
<script src="js/app.js"></script>
9193
</body>
9294
</html>

js/app.js

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// METAMASK CONNECTION
2+
const TIMEOUT = 1000;
3+
const COLLECTION_NAME = 'CodeCats';
4+
let editions = [];
5+
let dots = 1;
6+
7+
window.addEventListener('DOMContentLoaded', () => {
8+
const onboarding = new MetaMaskOnboarding();
9+
const onboardButton = document.getElementById('connectWallet');
10+
let accounts;
11+
12+
const updateButton = async () => {
13+
if (!MetaMaskOnboarding.isMetaMaskInstalled()) {
14+
onboardButton.innerText = 'Install MetaMask!';
15+
onboardButton.onclick = () => {
16+
onboardButton.innerText = 'Connecting...';
17+
onboardButton.disabled = true;
18+
onboarding.startOnboarding();
19+
};
20+
} else if (accounts && accounts.length > 0) {
21+
onboardButton.innerText = `✔ ...${accounts[0].slice(-4)}`;
22+
onboardButton.disabled = true;
23+
onboarding.stopOnboarding();
24+
checkOwner(accounts[0]);
25+
} else {
26+
onboardButton.innerText = 'Connect MetaMask!';
27+
onboardButton.onclick = async () => {
28+
await window.ethereum.request({
29+
method: 'eth_requestAccounts',
30+
})
31+
.then(function(accounts) {
32+
onboardButton.innerText = `✔ ...${accounts[0].slice(-4)}`;
33+
onboardButton.disabled = true;
34+
checkOwner(accounts[0]);
35+
});
36+
};
37+
}
38+
};
39+
40+
updateButton();
41+
if (MetaMaskOnboarding.isMetaMaskInstalled()) {
42+
window.ethereum.on('accountsChanged', (newAccounts) => {
43+
accounts = newAccounts;
44+
updateButton();
45+
});
46+
}
47+
});
48+
49+
const checkOwner = async (account) => {
50+
if(account) {
51+
let isOwner = false;
52+
let page = 1
53+
54+
const data = await fetchWithRetry(`/.netlify/functions/isowner/?wallet=${account}&page=${page}`);
55+
56+
isOwner = !isOwner ? data.isOwner : isOwner;
57+
updateStatusText(isOwner, true)
58+
59+
editions = [...data.editions]
60+
let nextPage = data.next_page
61+
62+
while(nextPage) {
63+
page = nextPage
64+
const data = await fetchWithRetry(`/.netlify/functions/isowner/?wallet=${account}&page=${page}`);
65+
66+
isOwner = !isOwner ? data.isOwner : isOwner;
67+
updateStatusText(isOwner, true)
68+
69+
editions = [...editions, ...data.editions]
70+
nextPage = data.next_page
71+
}
72+
73+
updateStatusText(isOwner, false)
74+
}
75+
}
76+
77+
function updateStatusText(isOwner, checking) {
78+
const statusText = document.querySelector('.owner-status');
79+
if(checking) {
80+
if(isOwner) {
81+
statusText.innerText = `You do own ${COLLECTION_NAME}!! 😻 Let's see how many${renderDots(dots)}`;
82+
} else {
83+
statusText.innerText = `Checking to see if you own any ${COLLECTION_NAME} 😻${renderDots(dots)}`;
84+
}
85+
} else {
86+
if(isOwner) {
87+
statusText.innerText = `You own ${editions.length} ${COLLECTION_NAME}!! 😻`;
88+
} else {
89+
statusText.innerText = `You don't own any ${COLLECTION_NAME} 😿`;
90+
}
91+
}
92+
dots = dots === 3 ? 1 : dots + 1;
93+
}
94+
95+
function renderDots(dots) {
96+
let dotsString = '';
97+
for (let i = 0; i < dots; i++) {
98+
dotsString += '.';
99+
}
100+
return dotsString;
101+
}
102+
103+
function timer(ms) {
104+
return new Promise(res => setTimeout(res, ms));
105+
}
106+
107+
async function fetchWithRetry(url) {
108+
await timer(TIMEOUT);
109+
return new Promise((resolve, reject) => {
110+
const fetch_retry = (_url) => {
111+
return fetch(_url).then(async (res) => {
112+
const status = res.status;
113+
114+
if(status === 200) {
115+
return resolve(res.json());
116+
}
117+
else {
118+
console.error(`ERROR STATUS: ${status}`)
119+
console.log('Retrying')
120+
await timer(TIMEOUT)
121+
fetch_retry(_url)
122+
}
123+
})
124+
.catch(async (error) => {
125+
console.error(`CATCH ERROR: ${error}`)
126+
console.log('Retrying')
127+
await timer(TIMEOUT)
128+
fetch_retry(_url)
129+
});
130+
}
131+
return fetch_retry(url);
132+
});
133+
}

0 commit comments

Comments
 (0)