Skip to content

Commit 32a1bbc

Browse files
committed
Initial commit
0 parents  commit 32a1bbc

18 files changed

+17058
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
snoovatars-website.iml
3+
.nuxt
4+
/node_modules/

README.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Nuxt 3 Minimal Starter
2+
3+
Look at the [nuxt 3 documentation](https://v3.nuxtjs.org) to learn more.
4+
5+
## Setup
6+
7+
Make sure to install the dependencies:
8+
9+
```bash
10+
# yarn
11+
yarn install
12+
13+
# npm
14+
npm install
15+
16+
# pnpm
17+
pnpm install --shamefully-hoist
18+
```
19+
20+
## Development Server
21+
22+
Start the development server on http://localhost:3000
23+
24+
```bash
25+
npm run dev
26+
```
27+
28+
## Production
29+
30+
Build the application for production:
31+
32+
```bash
33+
npm run build
34+
```
35+
36+
Locally preview production build:
37+
38+
```bash
39+
npm run preview
40+
```
41+
42+
Checkout the [deployment documentation](https://v3.nuxtjs.org/guide/deploy/presets) for more information.

app.vue

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<template>
2+
<div class="p-4 lg:p-6 flex flex-col items-center">
3+
<div class="flex flex-row justify-center">
4+
<NuxtLink class="pb-6 text-lg md:text-3xl text-white font-bold" to="/">snoovatars</NuxtLink>
5+
</div>
6+
<NuxtPage/>
7+
<div class="py-12 flex flex-col text-neutral-600 text-center">
8+
<span>This website is not affiliated or endorsed by reddit Inc.</span>
9+
</div>
10+
</div>
11+
</template>
12+
13+
<script setup lang="ts">
14+
import {useHead} from "nuxt/app";
15+
import {set_collection_list, useAlertList} from "~/composables/states";
16+
import {collection_list_from_object, CollectionList} from "~/models/reddit_collection";
17+
import {alert_list_from_object} from "~/models/alert";
18+
19+
useHead({
20+
title: 'snoovatars',
21+
meta: [
22+
{ name: 'description', content: 'Realtime price alerts for your favourite avatars!' }
23+
]
24+
})
25+
26+
await fetch("http://localhost:3000/lists")
27+
.then(async (data) => {
28+
data = await data.json();
29+
30+
if (data['collections']) {
31+
let collectionList: CollectionList = collection_list_from_object(data['collections']);
32+
33+
await set_collection_list(collectionList);
34+
}
35+
});
36+
37+
fetch("http://localhost:3000/alerts/1")
38+
.then(async (data) => {
39+
data = await data.json();
40+
41+
if (data['alerts']) {
42+
useAlertList().value = alert_list_from_object(data['alerts']);
43+
}
44+
});
45+
</script>
46+
47+
<style>
48+
html, body {
49+
@apply bg-zinc-900;
50+
}
51+
52+
input.input, select.input {
53+
/*@apply py-2 px-4 w-full text-white bg-neutral-800 border border-neutral-700 rounded-md text-sm shadow-sm cursor-pointer placeholder-neutral-400 hover:border-sky-500 focus:bg-neutral-800 focus:outline-none focus:border-sky-500 focus:ring-1 focus:ring-sky-500 transition duration-200;*/
54+
@apply px-4 py-3.5 block w-full max-w-xl bg-transparent text-neutral-200 placeholder-neutral-400 font-medium border-2 border-neutral-800 hover:border-neutral-600 focus:border-neutral-400 text-sm rounded-2xl outline-0 duration-200;
55+
}
56+
57+
label.input-label {
58+
@apply block mb-2 text-sm font-medium text-neutral-400;
59+
}
60+
</style>

composables/states.ts

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import {useState} from "nuxt/app";
2+
import {CollectionList, CollectionSlug, RedditCollection} from "~/models/reddit_collection";
3+
import {RedditCollectionTier, TierHash, TierList} from "~/models/reddit_collection_tier";
4+
import {AvatarHash, AvatarList, RedditAvatar} from "~/models/reddit_avatar";
5+
import {Alert, AlertHash, AlertList} from "~/models/alert";
6+
7+
export const useCollectionList = () => useState<CollectionList>('collection-list', () => new Map<CollectionSlug, RedditCollection>());
8+
export const useTierList = () => useState<TierList>('tier-list', () => new Map<TierHash, RedditCollectionTier>());
9+
export const useAvatarList = () => useState<AvatarList>('avatar-list', () => new Map<AvatarHash, RedditAvatar>());
10+
export const useAlertList = () => useState<AlertList>('alert-list', () => new Map<AlertHash, Alert>());
11+
12+
async function update_tier_and_avatar_list() {
13+
let tierList: Map<TierHash, RedditCollectionTier> = new Map<TierHash, RedditCollectionTier>();
14+
let avatarList: Map<AvatarHash, RedditAvatar> = new Map<AvatarHash, RedditAvatar>();
15+
16+
let collectionList = useCollectionList().value;
17+
18+
collectionList.forEach((collection) => {
19+
collection.tiers.forEach( async (tier) => {
20+
tierList.set(await tier.calculate_hash(), tier);
21+
22+
for (let mint = 1; mint <= tier.mints; mint++) {
23+
let avatar = new RedditAvatar(
24+
await tier.calculate_hash(),
25+
tier.contract_address,
26+
tier.tier,
27+
tier.name,
28+
mint,
29+
""
30+
);
31+
32+
avatarList.set(await avatar.calculate_hash(), avatar);
33+
}
34+
})
35+
})
36+
37+
useTierList().value = tierList;
38+
useAvatarList().value = avatarList;
39+
}
40+
41+
export async function set_collection_list(collectionList: CollectionList) {
42+
useCollectionList().value = collectionList;
43+
44+
await update_tier_and_avatar_list()
45+
}

global/hasher.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { blake3 } from 'hash-wasm';
2+
import {IDataType} from "hash-wasm/lib/util";
3+
4+
export class Hasher {
5+
public static async hash(data: IDataType): Promise<string> {
6+
return await blake3(data, 128)
7+
}
8+
}

models/alert.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {TierHash} from "~/models/reddit_collection_tier";
2+
import {AvatarHash} from "~/models/reddit_avatar";
3+
4+
export type AlertHash = string;
5+
export type AlertList = Map<AlertHash, Alert>;
6+
7+
export class Alert {
8+
public user_id: string;
9+
public collection_tier_hash: TierHash;
10+
public item_hash: AvatarHash;
11+
public type: AlertType;
12+
public repeating: boolean;
13+
public max_mint_number: number;
14+
public price_threshold: bigint;
15+
}
16+
17+
export enum AlertType {
18+
ListingBelow,
19+
SaleAbove
20+
}
21+
22+
export function alert_list_from_object(alertsObject: Object): AlertList {
23+
let alertList: AlertList = new Map<AlertHash, Alert>();
24+
25+
Object.entries(alertsObject).forEach(([alertHash, alertObject]) => {
26+
let alert: Alert = new Alert();
27+
28+
Object.assign(alert, alertObject);
29+
30+
alertList.set(alertHash, alert);
31+
})
32+
33+
return alertList;
34+
}

models/reddit_avatar.ts

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {TierHash} from "~/models/reddit_collection_tier";
2+
import {Hasher} from "~/global/hasher";
3+
4+
export type AvatarHash = string;
5+
export type AvatarList = Map<AvatarHash, RedditAvatar>;
6+
7+
export class RedditAvatar {
8+
collection_tier_hash: TierHash;
9+
contract_address: string;
10+
tier: number;
11+
name: string;
12+
mint_number: number;
13+
// Purchase link.
14+
link: string;
15+
16+
public constructor(
17+
collection_tier_hash: TierHash,
18+
contract_address: string,
19+
tier: number,
20+
name: string,
21+
mint_number: number,
22+
link: string
23+
) {
24+
this.collection_tier_hash = collection_tier_hash;
25+
this.contract_address = contract_address;
26+
this.tier = tier;
27+
this.name = name;
28+
this.mint_number = mint_number;
29+
this.link = link;
30+
}
31+
32+
public async calculate_hash(): Promise<string> {
33+
let encoded = this.contract_address + this.name + this.mint_number;
34+
35+
return await Hasher.hash(encoded)
36+
}
37+
}

models/reddit_collection.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {RedditCollectionTier} from "~/models/reddit_collection_tier";
2+
3+
export type CollectionSlug = string;
4+
export type CollectionList = Map<CollectionSlug, RedditCollection>;
5+
6+
export class RedditCollection {
7+
public name: string;
8+
public slug: CollectionSlug;
9+
public tiers: Array<RedditCollectionTier>;
10+
}
11+
12+
export function collection_list_from_object(collectionsObject): CollectionList {
13+
let collectionList: CollectionList = new Map<CollectionSlug, RedditCollection>();
14+
15+
Object.entries(collectionsObject).forEach(([slug, collectionObject]) => {
16+
let collection: RedditCollection = new RedditCollection();
17+
18+
let tiers: Array<RedditCollectionTier> = new Array<RedditCollectionTier>();
19+
20+
collectionObject['tiers'].forEach((tierObject, index) => {
21+
let tier: RedditCollectionTier = new RedditCollectionTier();
22+
23+
Object.assign(tier, JSON.parse(JSON.stringify(tierObject)));
24+
25+
tiers.push(tier);
26+
})
27+
28+
collectionObject['tiers'] = [];
29+
30+
Object.assign(collection, JSON.parse(JSON.stringify(collectionObject)));
31+
32+
collection.tiers = tiers;
33+
34+
collectionList.set(collection.slug, collection);
35+
})
36+
37+
return collectionList;
38+
}

models/reddit_collection_tier.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import {Hasher} from "~/global/hasher";
2+
3+
4+
export type TierHash = string;
5+
export type TierList = Map<TierHash, RedditCollectionTier>;
6+
7+
export class RedditCollectionTier {
8+
collection_slug: string;
9+
contract_address: string;
10+
tier: number;
11+
name: string;
12+
mints: number;
13+
14+
public async calculate_hash(): Promise<string> {
15+
let encoded = this.contract_address + this.name;
16+
17+
return await Hasher.hash(encoded)
18+
}
19+
}

models/snoovatars_api.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {CollectionList} from "~/models/reddit_collection";
2+
3+
export class CollectionsResponse {
4+
collections: CollectionList
5+
}

nuxt.config.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// https://v3.nuxtjs.org/api/configuration/nuxt.config
2+
export default defineNuxtConfig({
3+
modules: ['@nuxtjs/tailwindcss'],
4+
build: {
5+
postcss: {
6+
postcssOptions: require("./postcss.config.js"),
7+
},
8+
},
9+
ssr: false
10+
})

0 commit comments

Comments
 (0)