In the last lesson, we focused on styling our listed NFTs, checking to see whether an NFT is listed 1
and generally handling a lot of the UI updates to improve the user experience of the listing and selling 2
of NFTs on our website. 3
Now in this lesson, I want to focus on getting this Discover page to work so that all the listed NFTs 4
can show up in here in a tile fashion similar to what we had in My NFTs. 5
That basically means that we're going to be reusing this gallery component and passing it some different 6
IDs as props. 7
To begin, we're going to have to look inside our header because this is the point where we actually 8
have diverging paths because when we head over to the collection route, we're going to pass over the 9
user owned gallery, which is a gallery that we've set up with the NFT IDs from the current users owned 10
NFTs. 11
Now at the moment for the Discover path, all it has is just an H1. 12
So we're going to delete that and we're going to replace it with the listingGallery. 13
And again, we'll need to add a constant up here. 14
And we're going to update it inside all getNFTs function. 15
So we're going to create a constant called the listedNFTIds. 16
And at the moment, we don't actually have a function in our main. 17
mo, 18
our OpenD, that handles grabbing hold of all the listed NFT IDs. 19
So let's go ahead and do that right now. 20
Heading to the main. 21
mo, 22
and I'm going to scroll up to find a section that's sort of somewhat related where we've got the getOwned 23
NFTs, and below it 24
I'm going to add a function that will get our listed NFTs. 25
Let's create a new public query func. 26
And it's going to be called getListedNFTs and it's going to not take any inputs, but it is going 27
to return an array of principal IDs, which will be the principals of all the 28
NFTs that are listed. Now, we're going to be looking through our map of listings and we're going to 29
be using a function in the HashMap which is called keys, which basically returns us an Iter which 30
is a Motoko data type of all the keys in this HashMap. 31
So we're going to say mapOfListings.keys and then parentheses. 32
And with these keys, we're going to turn it into an array using the Iter data type. 33
So we first have to import it in order to use it. 34
And we already worked with the Iter previously when we looked at how to persist HashMaps. 35
So now we're going to use again that really handy method from Iter which is called toArray and we're 36
going to pass in this thing that's iteratable, which is all keys from our map listings. 37
And I'm going to set this to a constant which I'll call ids, 38
and that is what we're going to be returning from this method. 39
So now with this new method getListedNFTs, we can go back to our header.jsx, call await opend 40
. and then paste in the name of that method call. 41
It doesn't take any inputs, so we can just cap it off like this, and then we can console.log these IDs 42
just to make sure that it actually does work. 43
And finally, we're going to set our gallery, setListingGallery not ownedGallery anymore, and 44
we're going to set it to a new gallery component and we're going to pass in some props. 45
So the title is going to be equal to Discover, the ids is going to be equal to our listedNFTIds, 46
and then finally, we can add our /> to close off our gallery component. 47
This basically passes the buck over to our gallery. 48
And if we go ahead and just deploy our canister again, 49
remember, you can find the code in the read me and simply just copy and paste it. 50
I'm always a little bit paranoid about the methods not updating, so 51
always watching this opend did 52
to make sure that the new update which is that new method we created, getListedNFTs, actually gets added 53
which it did. 54
So that worked successfully. 55
Now once that's all done, then we can go and mint a new NFT. 56
Which we're going to list for sale. 57
And if we now head over to our Discover Page, you can see that listed NFT showing up. 58
And just to make sure that it's actually correctly working, I'm going to mint yet another NFT. 59
Which I won't list for sale 60
and you can see the difference between My NFTs and Discover. 61
So if you get this, that means our code is working correctly. 62
So we're able to view our listed NFTs but we want to change the item a little bit because we don't want 63
it to be blurred when it's being shown on the Discover page. 64
We actually want people to buy it, to be able to see it, and we also want to add a buy button. 65
So we need some sort of way of identifying when the Discover page is being rendered and when the collection 66
page is being rendered. 67
Let's go ahead and add another prop 68
when we create a gallery component and this I'm going to call role. 69
So the first role for the My NFTs I'm going to pass as the collection, and the second role I'm going 70
to set as Discover. 71
And now in our gallery, we can simply just pass those props over. 72
So role = props.role 73
and that's going to pass them straight over to our item, which is where we're actually going to care 74
a bit more about this. 75
Now, everything we've done here so far below where we've set the image, set the owner, set name, is pretty 76
much only for our My NFTs page. 77
So we can say if the props.role is equal to collection, and make sure that you don't have any typos 78
between here and where you set it in the header. 79
Then we're going to perform all of these functions like so. But else if our props.role is equal to 80
Discover, 81
well, in that case, we're going to do something a little bit different. 82
And what we want to do here is we want to set the button to say Buy instead, because that's the purpose 83
of the Discover page. 84
So we're going to change the text to buy. 85
And it says, passing over a method called handleSell, we're going to pass over a method called handle 86
Buy which means we have to create it as well. 87
So let's create our async function 88
handleBuy, and simply just console.log that Buy was triggered. 89
Like this. 90
Now 91
let's check to see if it actually works. 92
You can see on the Discover page, we've got our item and it says buy. On our My NFTs page, 93
if there's an item that can be sold, it has the sell button. 94
If it has an item that's already listed, then it doesn't have a sell button because it's already been 95
sold and listed. 96
So notice here, it's actually showing us the buy button for an item that we listed ourselves, 97
right? 98
So we're actually the original owner of this NFT, but because we had to temporarily transfer it over 99
to OpenD to be able to list it, we should not be able to buy our own NFT back. 100
That makes no sense in terms of the logic. 101
So we're going to have to do some checks and balances in our Discover section here. And what we're most interested 102
in is actually the original owner. 103
So not the current owner, which is going to be OpenD's canister ID, but the actual original owner, 104
which we know is ourselves. 105
And we can do this by creating a method in our main.mo. 106
So it's going to be a public query func and I'm going to call it getOriginal 107
Owner, and it's going to require us to pass in the principal ID of the actual NFT, and we're going to 108
return the principal ID of the original owner of the NFT. 109
Remember that previously when we added items into our map of listings, it was a HashMap with keys 110
made up of the principal ID of the NFT, 111
and then values were these listing types which contains the item owner's principal and the item price. 112
So we can tap into that by getting hold of the actual listing by passing over the item ID. Similar to 113
previously when we were working with Optionals, we're going to have to use the switch statement to unwrap 114
it. 115
So I'm going to create a var for getting hold of the listing, which is going to be of data type Listing. 116
And I'm going to switch on the mapOfListings and calling the get on the ID that's passed in here. 117
And once I've got my switch statement, I've got the case null, which means that this particular ID 118
which we found in our mapOfListings doesn't actually correspond to any NFTs, 119
well, then we're going to simply return a empty principal. 120
But otherwise we're going to unwrap the result. And then we're going to return the listing 121
.itemOwner. 122
So now coming back to our frontend, we can check to see who the original owner is by doing await opend 123
. and then the name of our method which is getOriginalOwner passing in props.id, the id of 124
the current NFT. 125
Now we're going to check if the original owner's principal, 126
once it's converted to text is not equal to the current logged in user, which remember we're exporting 127
and keeping hold of in here in our index.jsx. 128
That way we don't have to deploy a canister and we don't have to deal with log in flow. 129
So we have to import that over to our item.jsx right here. 130
So if you start typing CURRENT_USER_ID, let's hit enter, then it should import it for you. 131
And we're going to again turn that into text. 132
So we're going to use toText to compare like with like. 133
And if it is the case that the original owner is not the current owner, then we should be able to see 134
the buy button. 135
So let's go ahead and make sure that we save our main.mo file so that it goes from the italic 136
to non-italic font. 137
And then we're going to deploy our canister again using our long-winded deployment code from our read 138
me just so that all our latest updates are reflected. 139
Now, once our canisters are all deployed, we want to be able to test it out on the front end. 140
But we have a problem here because we're only able to mint NFTs from the front end as ourselves. 141
So we will never be able to see that buy button because the original owner of the NFT is always going 142
to be ourselves, the user. 143
So how do we get around that? 144
Well, that's why we have the command line. In order to test this, 145
I want you to head over to the read me. 146
And if you scroll down, you'll see a section called Creating NFT for Testing. 147
Here, we're going to mint an NFT on the command line to get into the mapOfNFTs. 148
And once that comes back, it'll come back with a ID of the newly minted NFT. 149
So we're going to copy everything that's in between the double-quotes, 150
and I want you to replace what it says here on step #2: list the item into mapOfListings with that 151
ID that you got back. 152
So make sure that you remove the angle brackets and all you're left with is just something that looks 153
like this. 154
Now, go ahead and copy it. 155
Copy it on the preview if you don't want to accidentally copy these back strokes. 156
So let's copy that and paste it in here. 157
And once we list the item and it comes back success as well, 158
and then we're going to go to step 3. where we get hold of OpenD's canister ID by calling dfx canister 159
id opend, and then we're going to copy the canister id of opend and go into step 4. and replace 160
everything that's in between the angle brackets, including the angle brackets, where it says replace 161
with opend canister id. 162
And then finally, we're going to get hold of the canister ID from the previous step here, 163
that's the id of the NFT, and we're going to replace it in here. 164
And notice that this time it doesn't have any double-quotes around this part so that we're calling 165
that canister's transferOwnership method and transferring that NFT to the OpenD. 166
So we're basically doing everything that we normally do on the frontend, but this time using the command 167
line and doing it from the backend. 168
And hopefully that comes back as success. If it doesn't or if any of the steps don't work, 169
just make sure that you haven't got any apparent angle brackets or any double-quotes. 170
The double quotes are meant to be here, but not here, 171
so just be careful there. 172
So now if we refresh our Discover page, you'll see our newly minted NFT that came from the command 173
line. 174
And because we created it from the command line, the original owner is not the same as the one that 175
we would have when we mint it on the frontend. 176
So what this means is that we should be able to get our buy button to show up. 177
And that's exactly what happens. 178
Now, just for you to be able to compare, 179
I'm going to speed through the minting and selling process just to show you the difference. 180
So this is sped up in the editing. 181
Don't worry, I'm not that fast. 182
And now if we head back over to the Discover page, you can see both listed NFTs on display, but only 183
the one where the original owner is not ourselves is available to buy. And the one that we minted ourselves 184
which we still own, even though we listed for sale, we still can't buy it. 185
So that button is not there. 186
Now, the final thing I want to do before we wrap up this lesson is to display the prices of our NFTs 187
that are listed for sale. 188
Because after all, how can people make a buy decision if they don't know how much things cost? 189
In our read me 190
if you scroll up a little bit, you should see a price label HTML. 191
And if you simply copy it and we're going to create a new component which we're going to call Price 192
Label.jsx. 193
And inside here, we're going to create this component. 194
So I'm just going to copy what I had in the button component so I don't have to rewrite a lot of this 195
code. 196
So import React from react. Function, this is called the PriceLabel, capital P, capital L. It's going 197
to receive some props and then it's going to return. 198
And then we've got our HTML and then we're going to close off the function and we're going to do export 199
default PriceLabel. 200
So now we can use this PriceLabel. 201
And to start with, I just want to show you what it looks like. 202
So just above the H2, I want you to go ahead and insert that PriceLabel component. 203
So hopefully that has imported by itself. 204
And now if we go on to the frontend, you can see the price label showing up. 205
Now, we don't want it to always say 23, so we're going to pass this number in as a prop. 206
So we're going to put the props.sellPrice and coming back to item, we're going to turn this 207
into a priceLabel state stateful constant. 208
So it was called priceLabel, setPriceLabel. 209
Now the point where we want to set our price label is when we are in our Discover section. 210
So just below where we've done our if statement, we're going to check the price of the NFT. 211
So that's going to involve a little bit more code in our main.mo where we get hold of the item 212
price. 213
So let's bring up our main.mo 214
and create a new public query function which is called get 215
ListedNFTPrice passing in the id of the NFT and returning a natural number. 216
So add the semicolons where you need to 217
and we're going to get hold of the listing in a very similar way to before. I'm going to create a variable 218
called listing, which is of type Listing. 219
And it's going to use a switch statement to check our mapOfListings to get hold of the one with this 220
ID that's passed in. In the case where it's null in this case, we're simply going to return zero as 221
the price. 222
But in the case where it does exist as an optional, then we're going to use that result and finally 223
return the listing.itemPrice. 224
And now we can call this method, 225
I'm just going to copy it so that I can paste it in here. 226
So I'm going to create a new const to called price and it's going to be set to equal await opend. 227
paste in my method name and then use the props.id. 228
And now using my sell price, I can set my price label to be a new PriceLabel component and I'm going 229
to give it the prop of the sellPrice and then setting our sellPrice to price, but not just the price. 230
We have to use the toString method to change it into an actual string because that's what the span 231
wants to see and is able to display. 232
So making sure that you've got the toString and the parentheses after that method call to make that 233
conversion. 234
So now all we have to do is hit save and redeploy because we've made some changes to our backend. 235
And then once that's done, we have to mint a new NFT and put it up for sale. 236
And once it's listed, on the Discover page, you'll be able to see its price being listed. 237
There you have it. 238
We've managed to style our Discover page to display all of the NFTs that are available for sale. 239
We've managed to get the button to only show up when the NFT is put up for sale by somebody other than 240
ourselves. 241
We've got the price label set up and we've now got a pretty good-looking digital marketplace for NFTs. 242
In the next lesson, we've got the very last aspect of this website to complete, which is the buying 243
of NFTs. So that we can come along, transfer our DANG tokens using that token that we created in the 244
previous module and to be able to purchase actual NFTs and transfer the ownership. 245
So for all of that and more, I'll see you on the next lesson.