Search…
Lists and Leaderboards
One thing to keep in mind when working with decentralized systems is that there's very minimal support for things like APIs and databases. Instead, we use a combination of on-chain data (via NFTs and GraphQL tags on Arweave) and Koii Tasks running on decentralized nodes.
When constructing a leaderboard or list of content (such as a collection of an Artist's NFTs, or a personalized newsfeed) it's usually necessary to either configure an index or hijack an existing one.
Koii Tasks will go live in Q2 2022, at which point you'll be able to fully customize your system, but until then, the best course of action is likely to process the current Koii Leaderboard APIs clientside and construct your own newsfeed using filters.
View Filters
Filters can be applied to any list component as shown:
1
📦src
2
┣ 📂components
3
┃ ┗ 📂filters
4
┃ ┗ 📂TimeFilter
5
// Set up Time Filter components /src/components/TimeFilter
6
function TimeFilter() {
7
const [timeframe, setTimeframe] = useQueryParam<string>("t", withDefault(StringParam, "1w"));
8
9
const onTimeframeChange = (newValue: string) => {
10
setTimeframe(newValue, "replaceIn");
11
return (
12
<Flex>
13
<ToggleButtonGroup value={timeframe} defaultValue={timeframe} onChange={onTimeframeChange} name="timeframe" isAttached variant="outline" aria-label="Change timeframe">
14
<ToggleButton value="24h" aria-label="24 hours" children="24h" />
15
<ToggleButton value="1w" aria-label="1 week" children="1w" />
16
<ToggleButton value="1m" aria-label="1 month" children="1m" />
17
<ToggleButton value="1y" aria-label="1 year" children="1y" />
18
<ToggleButton value="all" aria-label="all time" children="all" />
19
</ToggleButtonGroup>
20
</Flex>
21
);
22
};
23
24
📦src
25
┣ 📂api
26
┃ ┗ 📂hooks
27
┃ ┗ 📜useNfts.ts
28
// Use Time Filter /src/api/hooks/useNfts.ts
29
const fetchNfts = async (timeframe: string = "1w") => {
30
try {
31
let nsfwList: any[] = [];
32
/* Get nsfw list */
33
await fetchNsfwList()
34
.then(res => {
35
nsfwList = res?.data?.NSFWList || [];
36
})
37
.catch(() => {}); // We don't want to catch anything.
38
39
/* Get nfts based on the timeframe */
40
const { data } = await axios.get(`/attention/nft-summaries?period=${timeframe}`);
41
const dataWithNsfwTag = data?.map((nft: any) => ({ ...nft, isNsfw: [...nsfwList].includes(nft?.id) }));
42
43
return dataWithNsfwTag;
44
} catch (error) {
45
return undefined;
46
}
47
};
48
49
export function useNfts({ timeframe = "1w" }: Props) {
50
const [isNsfw] = useQueryParam("nsfw");
51
return useQuery(`nfts-${timeframe}`, () => fetchNfts(timeframe), {
52
staleTime: 60 * 1000 * 5, // 5min cache
53
refetchOnWindowFocus: undefined,
54
select: data => {
55
return isNsfw ? data : [...data].filter((nft: any) => !nft.isNsfw);
56
}
57
});
58
}
59
Copied!
The default list component fetches the fully Koii NFT list, so configuring views can allow you to select any content you like without rebuilding the Tasks API. If this doesn't work for your use case, please contact our support team and we can help you deploy an Indexing Task of your own!
NSFW
Koii currently manages an NSFW blocked content list which can be accessed using the same API as the main feed. NSFW content can be toggled in list views like this:
1
📦src
2
┣ 📂components
3
┃ ┗ 📂filters
4
┃ ┗ 📂NsfwFilter
5
// Set up NSFW components /src/components/TimeFilter
6
function NsfwFilter() {
7
const [isOn, setIsOn] = useQueryParam("nsfw", withDefault(BooleanParam, false));
8
const onNsfwChange = async (e: ChangeEvent<HTMLInputElement>) => {
9
setIsOn(e.target.checked || undefined, "replaceIn");
10
};
11
12
return (
13
<>
14
<FormControl display="flex" alignItems="center" justifyContent="center">
15
<FormLabel htmlFor="isNsfw" m="0 6px 0 0" fontSize="xs" userSelect="none">
16
Show NSFW content
17
</FormLabel>
18
<Switch id="isNsfw" size="sm" defaultChecked={isOn} value={isOn ? 1 : 0} onChange={onNsfwChange} />
19
</FormControl>
20
</>
21
);
22
}
23
24
📦src
25
┣ 📂api
26
┃ ┗ 📂hooks
27
┃ ┗ 📜useNsfw.ts
28
// Use NSFW /src/api/hooks/useNsfw.ts
29
const fetchNsfw = async () => {
30
try {
31
const { data } = await axios.get(`/getNSFWList`, {
32
baseURL: process.env.REACT_APP_API_URL
33
});
34
const nsfwList = data?.NSFWList || [];
35
return nsfwList;
36
} catch (error) {
37
return undefined;
38
}
39
};
40
41
export function useNsfw() {
42
return useQuery(`nsfw-list`, fetchNsfw, {
43
staleTime: 60 * 1000 * 60, // 1hr cache, since the nft details does not change.
44
refetchOnWindowFocus: undefined
45
});
46
}
Copied!
Search
Search is currently implemented using clientside libraries which draw from the same core NFT list, and can be customized by editing the Search Component, shown here.
Thumbnails
Since your app will live on decentralized storage, it can sometimes be difficult to generate thumbnail images on the fly for your NFT assets, and it can be even harder to set OpenGraph tags for sharing content.
To streamline this process, Koii has provided a thumbnail generation API powered by Koii Tasks, to which you can POST new NFTs so that they will have nicely formatted previews when sharing on social media.
1
📦src
2
┣ 📂api
3
┃ ┗ 📜upload.ts line:137-143
4
// The code will send POST request to https://api.koii.live/generateCardWithData to get a thumbnail
5
const body = {
6
data: {
7
...initialState,
8
id: tx.id
9
},
10
media
11
};
12
13
// Your media tag should looks like:
14
// "media" : {
15
// "url" : "NFT file in decode in base64 format"
16
// }
17
// For example of the body:
18
//{
19
// "data": {
20
// "id": "QfZ3CRZYX3dvkmAwml6qTfJZJL2atFCyF1_mM7ChggU",
21
// "owner": null,
22
// "title": "Tons of code",
23
// "name": "Neo Matrix",
24
// "description": "Lots of unused vars in my code\n",
25
// "ticker": "KOINFT",
26
// "balances": {
27
// "null": 1
28
// },
29
// "contentType": "image/png",
30
// "createdAt": "1632748295",
31
// "tags": ["The one"],
32
// "isNsfw": false
33
// },
34
// "media":{
35
// "url": "/9j/4AAQSkZJRgABAQAAAQABAAD/......"
36
// }
37
//}
38
39
📦src
40
┣ 📂api
41
┃ ┗ 📜upload.ts line:188-194
42
const generateCardWithData = async (body: any) => {
43
return await axios.post(`https://api.koii.live/generateCardWithData`, body, {
44
transformRequest: (data, headers: any) => {
45
headers.common["Access-Control-Allow-Origin"] = "*";
46
return data;
47
},
48
baseURL: undefined
49
});
50
};
Copied!
Copy link