Skip to content

Commit

Permalink
feat: add collection page
Browse files Browse the repository at this point in the history
Signed-off-by: Tim Schmidt <tim@launchbadge.com>
  • Loading branch information
Sheng-Long committed Dec 15, 2023
1 parent a102bbe commit bfab57c
Show file tree
Hide file tree
Showing 7 changed files with 404 additions and 17 deletions.
132 changes: 132 additions & 0 deletions src/components/account/CollectionTable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<!--
-
- Hedera Mirror Node Explorer
-
- Copyright (C) 2021 - 2023 Hedera Hashgraph, LLC
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
-
-->

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- TEMPLATE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<template>
<o-table
:data="collection"
:loading="loading"
:hoverable="true"
:paginated="!isTouchDevice"
backend-pagination
:total="totalRowCount"
:current-page="currentPage"
:per-page="pageSize"
@page-change="onPageChange"
:striped="true"
:v-model:current-page="currentPage"
:mobile-breakpoint="ORUGA_MOBILE_BREAKPOINT"
aria-current-label="Current page"
aria-next-label="Next page"
aria-page-label="Page"
aria-previous-label="Previous page"
@cell-click="handleClick"
>
<o-table-column v-slot="props" field="serial" label="Serial">
{{ props.row.serial_number }}
</o-table-column>
</o-table>

<EmptyTable v-if="!collection.length"/>

</template>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- SCRIPT -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<script lang="ts">
import {defineComponent, inject, PropType, ref, ComputedRef, Ref} from 'vue';
import { Nft } from "@/schemas/HederaSchemas";
import TokenLink from "@/components/values/TokenLink.vue";
import {ORUGA_MOBILE_BREAKPOINT} from '@/App.vue';
import EmptyTable from "@/components/EmptyTable.vue";
import {routeManager} from "@/router";
import { Collection, NftsTableController } from "@/components/account/NftsTableController";
import { useRoute } from "vue-router";
import { CollectionTableController } from "@/components/account/CollectionTableController";
export default defineComponent({
name: 'CollectionTable',
components: {
EmptyTable,
TokenLink,
},
props: {
controller: {
type: Object as PropType<CollectionTableController>,
required: true
},
tokenId: {
type: String,
required: true
}
},
setup(props) {
const isTouchDevice = inject('isTouchDevice', false)
const isMediumScreen = inject('isMediumScreen', true)
const handleClick = (
n: Nft,
c: unknown,
i: number,
ci: number,
event: MouseEvent,
) => {
if (n.token_id && n.serial_number) {
routeManager.routeToSerial(
n.token_id,
n.serial_number,
event.ctrlKey || event.metaKey,
);
}
};
return {
isTouchDevice,
isMediumScreen,
collection: props.controller.rows as ComputedRef<Nft[]>,
loading: props.controller.loading as ComputedRef<boolean>,
totalRowCount: props.controller.totalRowCount as ComputedRef<number>,
currentPage: props.controller.currentPage as Ref<number>,
onPageChange: props.controller.onPageChange,
pageSize: props.controller.pageSize as Ref<Number>,
handleClick,
ORUGA_MOBILE_BREAKPOINT
}
}
});
</script>

<!-- --------------------------------------------------------------------------------------------------------------- -->
<!-- STYLE -->
<!-- --------------------------------------------------------------------------------------------------------------- -->

<style scoped>
</style>
101 changes: 101 additions & 0 deletions src/components/account/CollectionTableController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*-
*
* Hedera Mirror Node Explorer
*
* Copyright (C) 2021 - 2023 Hedera Hashgraph, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import {
Nft,
Nfts,
NftTransactionHistory,
TokenBalancesResponse,
TokenDistribution,
TokenRelationship
} from "@/schemas/HederaSchemas";
import {ComputedRef, Ref} from "vue";
import axios, {AxiosResponse} from "axios";
import {KeyOperator, SortOrder, TableController} from "@/utils/table/TableController";
import {Router} from "vue-router";

export class CollectionTableController extends TableController<Nft, string> {

public readonly accountId: Ref<string | null>
public readonly tokenId: string

//
// Public
//

public constructor(router: Router, tokenId: string, accountId: Ref<string | null>, pageSize: ComputedRef<number>) {
super(router, pageSize, 10 * pageSize.value, 5000, 10, 100)
this.accountId = accountId
this.tokenId = tokenId
}

//
// TableController
//

public async load(tokenId: string | null, operator: KeyOperator, order: SortOrder, limit: number): Promise<Nft[] | null> {
if (this.accountId.value == null) {
return Promise.resolve(null)
}

const params = {} as {
limit: number
"token.id": string | undefined
order: string
}
params.limit = 10000
params.order = order
if (tokenId !== null) {
params["token.id"] = operator + ":" + tokenId
}

const { data } = await axios.get<Nfts>(
`api/v1/accounts/${this.accountId.value}/nfts`,
{params: params},
)

const collectionsMap = new Map();
for (const nft of data.nfts!) {
let collection = collectionsMap.get(nft.token_id);
if (collection === undefined) {
collection = { count: 0, nfts: [] };
collectionsMap.set(nft.token_id, collection);
}
collection.count += 1;
collection.nfts.push(nft);
}

const nfts = collectionsMap.get(this.tokenId).nfts

return nfts
}

public keyFor(row: Nft): string {
return row.token_id ?? ""
}

public stringFromKey(key: string): string {
return key;
}

public keyFromString(s: string): string | null {
return s;
}
}
21 changes: 10 additions & 11 deletions src/components/account/NftsTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
<!-- --------------------------------------------------------------------------------------------------------------- -->

<template>

<o-table
:data="nfts"
:loading="loading"
Expand All @@ -50,11 +49,9 @@
v-bind:no-anchor="true"
/>
</o-table-column>

<o-table-column v-slot="props" field="serial" label="Serial Number" position="right">
{{props.row.serial_number}}
<o-table-column v-slot="props" field="owned" label="Owned" position="right">
{{props.row.tokens.count}}
</o-table-column>

</o-table>

<EmptyTable v-if="!nfts.length"/>
Expand All @@ -68,13 +65,13 @@
<script lang="ts">
import {defineComponent, inject, PropType, ref, ComputedRef, Ref} from 'vue';
import { Nft, TokenBalance, TokenRelationship } from "@/schemas/HederaSchemas";
import { Nft } from "@/schemas/HederaSchemas";
import TokenLink from "@/components/values/TokenLink.vue";
import TokenAmount from "@/components/values/TokenAmount.vue";
import {ORUGA_MOBILE_BREAKPOINT} from '@/App.vue';
import EmptyTable from "@/components/EmptyTable.vue";
import {routeManager} from "@/router";
import { NftsTableController } from "@/components/account/NftsTableController";
import { Collection, NftsTableController } from "@/components/account/NftsTableController";
import { useRoute } from "vue-router";
export default defineComponent({
name: 'NftsTable',
Expand All @@ -92,19 +89,21 @@ export default defineComponent({
},
setup(props) {
const route = useRoute();
const isTouchDevice = inject('isTouchDevice', false)
const isMediumScreen = inject('isMediumScreen', true)
const handleClick = (nft: Nft, c: unknown, i: number, ci: number, event: MouseEvent) => {
if (nft.token_id && nft.serial_number) {
routeManager.routeToSerial(nft.token_id, nft.serial_number, event.ctrlKey || event.metaKey)
if (nft.token_id) {
routeManager.routeToCollection(route.params.accountId as string, nft.token_id, event.ctrlKey || event.metaKey)
}
}
return {
isTouchDevice,
isMediumScreen,
nfts: props.controller.rows as ComputedRef<Nft[]>,
nfts: props.controller.rows as ComputedRef<Collection[]>,
loading: props.controller.loading as ComputedRef<boolean>,
totalRowCount: props.controller.totalRowCount as ComputedRef<number>,
currentPage: props.controller.currentPage as Ref<number>,
Expand Down
32 changes: 26 additions & 6 deletions src/components/account/NftsTableController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,15 @@ import axios, {AxiosResponse} from "axios";
import {KeyOperator, SortOrder, TableController} from "@/utils/table/TableController";
import {Router} from "vue-router";

export class NftsTableController extends TableController<Nft, string> {
export interface Collection {
token_id: string,
tokens: {
count: number,
nfts: Nft[]
}
}

export class NftsTableController extends TableController<Collection, string> {

public readonly accountId: Ref<string | null>

Expand All @@ -48,7 +56,7 @@ export class NftsTableController extends TableController<Nft, string> {
// TableController
//

public async load(tokenId: string | null, operator: KeyOperator, order: SortOrder, limit: number): Promise<Nft[] | null> {
public async load(tokenId: string | null, operator: KeyOperator, order: SortOrder, limit: number): Promise<Collection[] | null> {
if (this.accountId.value == null) {
return Promise.resolve(null)
}
Expand All @@ -58,20 +66,32 @@ export class NftsTableController extends TableController<Nft, string> {
"token.id": string | undefined
order: string
}
params.limit = limit
params.limit = 10000
params.order = order
if (tokenId !== null) {
params["token.id"] = operator + ":" + tokenId
}

const r = await axios.get<Nfts>(
const { data } = await axios.get<Nfts>(
`api/v1/accounts/${this.accountId.value}/nfts`,
{params: params},
)
return r.data.nfts ?? []

const collectionsMap = new Map();
for (const nft of data.nfts!) {
let collection = collectionsMap.get(nft.token_id);
if (collection === undefined) {
collection = { count: 0, nfts: [] };
collectionsMap.set(nft.token_id, collection);
}
collection.count += 1;
collection.nfts.push(nft);
}

return Array.from(collectionsMap, ([token_id, tokens]) => ({ token_id, tokens})) ?? []
}

public keyFor(row: Nft): string {
public keyFor(row: Collection): string {
return row.token_id ?? ""
}

Expand Down
Loading

0 comments on commit bfab57c

Please sign in to comment.