clear-wallet/src/views/AssetsTab.vue

576 lines
17 KiB
Vue

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Assets</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-loading
:is-open="loading"
cssClass="my-custom-class"
message="Please wait..."
:duration="4000"
@didDismiss="loading = false"
>
</ion-loading>
<ion-toast
:is-open="toastState"
@didDismiss="toastState = false"
message="Copied to clipboard"
:duration="1500"
></ion-toast>
<ion-item>
<ion-label>Assests for Account: {{ selectedAccount?.name }}</ion-label>
</ion-item>
<ion-item button @click="copyAddress(selectedAccount?.address, getToastRef())">
<p style="font-size: 0.7rem">{{ selectedAccount?.address }}</p>
<ion-icon style="margin-left: 0.5rem" :icon="copyOutline"></ion-icon>
</ion-item>
<template v-if="isError">
Assets info could not be retrieved because of an http error, API down or
conectivity issues.
</template>
<template v-else-if="noAssets"> No assets found for this wallet address. </template>
<template v-else>
<template v-if="ethTokens.length || polyTokens.length">
<template v-if="ethTokens.length">
<ion-item>Ethereum Tokens</ion-item>
<ion-list>
<ion-item v-for="token of ethTokens" :key="token.address">
<ion-avatar
v-if="token?.image"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="token?.name"
:src="token?.image"
@error="token.image = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label
>
</ion-item>
<ion-item v-if="hasMore.ethTokens">
<ion-button @click="loadMore('ethTokens')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
<template v-if="polyTokens.length">
<ion-item>Polygon Tokens</ion-item>
<ion-list>
<ion-item v-for="token of polyTokens" :key="token.address">
<ion-avatar
v-if="token?.image"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="token?.name"
:src="token?.image"
@error="token.image = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ token?.symbol }}:</b> {{ token?.balance }}</ion-label
>
</ion-item>
<ion-item v-if="hasMore.polyTokens">
<ion-button @click="loadMore('polyTokens')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
</template>
<template v-if="ethNfts.length || polyNfts.length">
<template v-if="ethNfts.length">
<ion-item>Ethereum NFTs</ion-item>
<ion-list>
<ion-item v-for="nft of ethNfts" :key="nft.address">
<ion-avatar
v-if="nft?.imageURI"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="nft?.collectionName"
:src="nft?.imageURI"
@error="nft.imageURI = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ nft?.collectionName }}</b></ion-label
>
</ion-item>
<ion-item v-if="hasMore.ethNfts">
<ion-button @click="loadMore('ethNfts')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
<template v-if="polyNfts.length">
<ion-item>Polygon NFTs</ion-item>
<ion-list>
<ion-item v-for="nft of polyNfts" :key="nft.address">
<ion-avatar
v-if="nft?.imageURI"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="nft?.collectionName"
:src="nft?.imageURI"
@error="nft.imageURI = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ nft?.collectionName }}</b></ion-label
>
</ion-item>
<ion-item v-if="hasMore.polyNfts">
<ion-button @click="loadMore('polyNfts')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
</template>
<template v-if="poaps.length">
<ion-item>POAPs</ion-item>
<ion-list>
<ion-item v-for="nft of poaps" :key="nft.eventId">
<ion-avatar
v-if="nft?.image"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="nft?.title"
:src="nft?.image"
@error="nft.image = getUrl('assets/randomGrad.svg')"
/>
</ion-avatar>
<ion-label
><b>{{ nft?.title }}</b></ion-label
>
</ion-item>
<ion-item v-if="hasMore.poaps">
<ion-button @click="loadMore('poaps')">Load More</ion-button>
</ion-item>
</ion-list>
</template>
</template>
</ion-content>
</ion-page>
</template>
<script lang="ts">
import { defineComponent, Ref, ref, reactive } from "vue";
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
onIonViewWillEnter,
IonItem,
IonLabel,
IonAvatar,
IonList,
IonButton,
IonToast,
IonLoading,
IonIcon,
} from "@ionic/vue";
import { getSelectedAccount, copyAddress, getUrl } from "@/utils/platform";
import type { Account } from "@/extension/types";
import { copyOutline } from "ionicons/icons";
interface IProfileToken {
address: string;
balance: number;
image: string;
name: string;
symbol: string;
}
interface IProfileNFT {
address: string;
collectionImageURI: string;
collectionName: string;
imageURI: string;
link: string;
tokenId: number;
}
interface IProfilePOAP {
description: string;
eventId: string;
image: string;
link: string;
title: string;
}
export default defineComponent({
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonAvatar,
IonList,
IonButton,
IonToast,
IonLoading,
IonIcon,
},
setup: () => {
const selectedAccount = ref({}) as Ref<Account>;
const loading = ref(true);
const isError = ref(false);
const noAssets = ref(false);
const toastState = ref(false);
const ethTokens = ref([]) as Ref<IProfileToken[]>;
const polyTokens = ref([]) as Ref<IProfileToken[]>;
const ethNfts = ref([]) as Ref<IProfileNFT[]>;
const polyNfts = ref([]) as Ref<IProfileNFT[]>;
const poaps = ref([]) as Ref<IProfilePOAP[]>;
const hasMore = reactive({
poaps: true,
ethTokens: true,
polyTokens: true,
ethNfts: true,
polyNfts: true,
});
const getToastRef = () => toastState;
const resources = ["nfts", "poaps", "tokens"];
const chains = ["ethereum", "polygon"];
const fetchFromWallet = async ({
apiBase = "https://api.yup.io",
address,
resource = resources[0],
chain = chains[0],
start = 0,
limit = 10,
}: {
apiBase?: string;
address: string;
resource?: string;
chain?: string;
start?: number;
limit?: number;
}) => {
try {
const res = await fetch(
`${apiBase}/web3-profiles/${resource}/${address}?chain=${chain}&start=${start}&limit=${limit}`
);
const req = await res.json();
if (res.ok) {
return req;
} else {
return null;
}
} catch (error) {
console.error("Failed to fetch web3 profiles", error);
return null;
}
};
const walletLoadArgs = {
address: "",
start: 0,
limit: 11,
res: resources,
ch: chains,
apiBase: "https://api.yup.io",
};
const getProfileWallet = async ({
address,
start,
limit,
res,
ch,
apiBase,
}: {
address: string;
start: number;
limit: number;
res: string[];
ch: string[];
apiBase: string;
}) => {
const r = {
poaps: [] as IProfilePOAP[],
ethNfts: [] as IProfileNFT[],
polyNfts: [] as IProfileNFT[],
ethTokens: [] as IProfileToken[],
polyTokens: [] as IProfileToken[],
};
try {
const promises = [];
if (res.includes("poaps")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "poaps",
chain: "ethereum",
}).then((rz) => {
r.poaps = rz ?? [];
})
);
}
if (res.includes("nfts")) {
if (ch.includes("ethereum")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "nfts",
chain: "ethereum",
}).then((rz) => {
r.ethNfts = rz ?? [];
})
);
}
if (ch.includes("polygon")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "nfts",
chain: "polygon",
}).then((rz) => {
r.polyNfts = rz ?? [];
})
);
}
}
if (res.includes("tokens")) {
if (ch.includes("ethereum")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "tokens",
chain: "ethereum",
}).then((rz) => {
r.ethTokens = rz ?? [];
})
);
}
if (ch.includes("polygon")) {
promises.push(
fetchFromWallet({
apiBase,
address,
start,
limit,
resource: "tokens",
chain: "polygon",
}).then((rz) => {
r.polyTokens = rz ?? [];
})
);
}
}
await Promise.all(promises);
return r;
} catch {
return r;
}
};
onIonViewWillEnter(async () => {
selectedAccount.value = await getSelectedAccount();
walletLoadArgs.address = selectedAccount.value.address;
const r = await getProfileWallet(walletLoadArgs);
ethNfts.value = r.ethNfts.slice(0, 10);
if (r.ethNfts.length !== walletLoadArgs.limit) {
hasMore.ethNfts = false;
}
polyNfts.value = r.polyNfts.slice(0, 10);
if (r.polyNfts.length !== walletLoadArgs.limit) {
hasMore.polyNfts = false;
}
ethTokens.value = r.ethTokens.slice(0, 10);
if (r.ethTokens.length !== walletLoadArgs.limit) {
hasMore.ethTokens = false;
}
polyTokens.value = r.polyTokens.slice(0, 10);
if (r.polyTokens.length !== walletLoadArgs.limit) {
hasMore.polyTokens = false;
}
poaps.value = r.poaps.slice(0, -1);
if (r.poaps.length !== walletLoadArgs.limit) {
hasMore.poaps = false;
}
noAssets.value =
poaps.value.length &&
ethNfts.value.length &&
polyNfts.value.length &&
ethTokens.value.length &&
polyTokens.value.length
? false
: true;
loading.value = false;
// const req = await fetch(`${yupAssetsApi}/${selectedAccount.value.address}`);
// if (req.ok) {
// assets.value = (await req.json()) ?? {};
// if (
// !("poaps" in assets.value) &&
// !("tokens" in assets.value) &&
// !("nfts" in assets.value)
// ) {
// noAssets.value = true;
// }
// if ("poaps" in assets.value) {
// poaps.value = assets.value?.poaps?.slice(0, 10) ?? [];
// if (poaps.value.length >= (assets.value?.poaps?.length ?? 0)) {
// hasMore.poaps = false;
// }
// }
// if ("nfts" in assets.value) {
// ethNfts.value =
// assets.value?.nfts?.filter((n) => n.chain === "ethereum").slice(0, 10) ?? [];
// if (
// ethNfts.value.length >=
// (assets.value?.nfts?.filter((n) => n.chain === "ethereum").length ?? 0)
// ) {
// hasMore.ethNfts = false;
// }
// polyNfts.value =
// assets.value?.nfts?.filter((n) => n.chain === "polygon").slice(0, 10) ?? [];
// if (
// polyNfts.value.length >=
// (assets.value?.nfts?.filter((n) => n.chain === "polygon").length ?? 0)
// ) {
// hasMore.polyNfts = false;
// }
// }
// if ("tokens" in assets.value) {
// ethTokens.value =
// assets.value?.tokens?.filter((n) => n.chain === "ethereum").slice(0, 10) ??
// [];
// if (
// ethTokens.value.length >=
// (assets.value?.tokens?.filter((n) => n.chain === "ethereum").length ?? 0)
// ) {
// hasMore.ethTokens = false;
// }
// polyTokens.value =
// assets.value?.tokens?.filter((n) => n.chain === "polygon").slice(0, 10) ?? [];
// if (
// polyTokens.value.length >=
// (assets.value?.tokens?.filter((n) => n.chain === "polygon").length ?? 0)
// ) {
// hasMore.polyTokens = false;
// }
// }
// } else {
// isError.value = true;
// }
// loading.value = false;
});
const loadMore = async (type: string) => {
switch (type) {
case "ethTokens": {
walletLoadArgs.start = ethTokens.value.length;
walletLoadArgs.res = ["tokens"];
walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.ethTokens.length !== walletLoadArgs.limit) {
hasMore.ethTokens = false;
return;
}
ethTokens.value = [...ethTokens.value, ...r.ethTokens.slice(0, 10)];
break;
}
case "polyTokens": {
walletLoadArgs.start = polyTokens.value.length;
walletLoadArgs.res = ["tokens"];
walletLoadArgs.ch = ["polygon"];
const r = await getProfileWallet(walletLoadArgs);
if (r.polyTokens.length !== walletLoadArgs.limit) {
hasMore.polyTokens = false;
return;
}
polyTokens.value = [...polyTokens.value, ...r.polyTokens.slice(0, 10)];
break;
}
case "ethNfts": {
walletLoadArgs.start = ethNfts.value.length;
walletLoadArgs.res = ["nfts"];
walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.ethNfts.length !== walletLoadArgs.limit) {
hasMore.ethNfts = false;
return;
}
ethNfts.value = [...ethNfts.value, ...r.ethNfts.slice(0, 10)];
break;
}
case "polyNfts": {
walletLoadArgs.start = polyNfts.value.length;
walletLoadArgs.res = ["nfts"];
walletLoadArgs.ch = ["polygon"];
const r = await getProfileWallet(walletLoadArgs);
if (r.polyNfts.length !== walletLoadArgs.limit) {
hasMore.polyNfts = false;
return;
}
polyNfts.value = [...polyNfts.value, ...r.polyNfts.slice(0, 10)];
break;
}
case "poaps": {
walletLoadArgs.start = poaps.value.length;
walletLoadArgs.res = ["poaps"];
walletLoadArgs.ch = ["ethereum"];
const r = await getProfileWallet(walletLoadArgs);
if (r.poaps.length !== walletLoadArgs.limit) {
hasMore.poaps = false;
return;
}
poaps.value = [...poaps.value, ...r.poaps.slice(0, 10)];
break;
}
}
};
return {
selectedAccount,
loading,
isError,
noAssets,
getToastRef,
copyAddress,
copyOutline,
ethTokens,
polyTokens,
ethNfts,
poaps,
hasMore,
polyNfts,
loadMore,
toastState,
getUrl,
};
},
});
</script>