Skip to content
This repository was archived by the owner on Dec 5, 2025. It is now read-only.

Commit 1648eba

Browse files
authored
Merge pull request #1147 from roiLeo/feature/collection/skelton
feat(collection): skeleton
2 parents 43a5468 + 1a1bc60 commit 1648eba

File tree

5 files changed

+97
-70
lines changed

5 files changed

+97
-70
lines changed

src/components/rmrk/Gallery/CollectionActivity.vue

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,51 @@
11
<template>
22
<div>
3-
<div class="level my-4 collection" v-if="nfts">
3+
<div class="level my-4 collection">
44
<div class="level-item has-text-centered">
5-
<div>
6-
<p class="title">{{ listedCount }} ⊆ {{ collectionLength }}</p>
5+
<b-skeleton position="is-centered" width="50%" :active="!nfts.length" size="is-small"></b-skeleton>
6+
<div v-if="nfts.length">
7+
<p class="title">
8+
{{ listedCount }} ⊆ {{ collectionLength }}
9+
</p>
710
<p class="heading">Listed / Total Items</p>
811
</div>
912
</div>
1013
<div class="level-item has-text-centered">
11-
<div>
14+
<b-skeleton position="is-centered" width="50%" :active="!nfts.length" size="is-small"></b-skeleton>
15+
<div v-if="nfts.length">
1216
<p class="title">
1317
<Money :value="collectionFloorPrice" inline />
1418
</p>
1519
<p class="heading">Floor price</p>
1620
</div>
1721
</div>
1822
<div class="level-item has-text-centered">
19-
<div>
23+
<b-skeleton position="is-centered" width="50%" :active="!nfts.length" size="is-small"></b-skeleton>
24+
<div v-if="nfts.length">
2025
<p class="title">
2126
<Money :value="collectionTradedVolumeNumber" inline />
2227
</p>
2328
<p class="heading">Volume traded</p>
2429
</div>
2530
</div>
2631
<div class="level-item has-text-centered">
27-
<div>
32+
<b-skeleton position="is-centered" width="50%" :active="!nfts.length" size="is-small"></b-skeleton>
33+
<div v-if="nfts.length">
2834
<p class="title">{{ uniqueOwnerCount }} ⊆ {{ differentOwnerCount }}</p>
2935
<p class="heading">Unique / Owners</p>
3036
</div>
3137
</div>
3238
<div class="level-item has-text-centered">
33-
<div>
34-
<p class="title">
35-
{{ disributionCount }}
36-
</p>
39+
<b-skeleton position="is-centered" width="50%" :active="!nfts.length" size="is-small"></b-skeleton>
40+
<div v-if="nfts.length">
41+
<p class="title">{{ disributionCount }}</p>
3742
<p class="heading">Distribution</p>
3843
</div>
3944
</div>
4045

4146
<div class="level-item has-text-centered">
42-
<div>
47+
<b-skeleton position="is-centered" width="50%" :active="!nfts.length" size="is-small"></b-skeleton>
48+
<div v-if="nfts.length">
4349
<p class="title">
4450
<Money
4551
:value="collectionDailyTradedVolumeNumber"
@@ -91,8 +97,8 @@ export default class extends Vue {
9197
return Math.min(...this.onlyListedNfts)
9298
}
9399
94-
get disributionCount(): string {
95-
return (this.differentOwnerCount / this.uniqueOwnerCount || 1).toFixed(4)
100+
get disributionCount(): number {
101+
return Number((this.differentOwnerCount / (this.uniqueOwnerCount || 1)).toFixed(4))
96102
}
97103
98104
get uniqueOwnerCount(): number {

src/components/rmrk/Gallery/CollectionItem.vue

Lines changed: 69 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
11
<template>
22
<div class="section">
3-
<div class="pack-item-wrapper container">
3+
<div class="container">
44
<div class="columns is-centered">
55
<div class="column is-half has-text-centered">
66
<div class="container image is-128x128 mb-2">
7-
<b-image
8-
v-if="!isLoading"
7+
<BasicImage
98
:src="image"
109
:alt="name"
11-
ratio="1by1"
1210
rounded
13-
></b-image>
11+
customClass="collection__image"
12+
/>
1413
</div>
1514
<h1 class="title is-2">
16-
{{ name }}
15+
<template v-if="!isLoading">
16+
{{ name }}
17+
</template>
18+
<b-skeleton :active="isLoading" size="is-medium"></b-skeleton>
1719
</h1>
1820
</div>
1921
</div>
2022

2123
<div class="columns is-align-items-center">
2224
<div class="column">
23-
<div class="label">
24-
{{ $t('creator') }}
25-
</div>
26-
<div class="subtitle is-size-6">
27-
<ProfileLink :address="issuer" :inline="true" :showTwitter="true"/>
28-
</div>
29-
</div>
30-
<div class="column" v-if="owner">
31-
<div class="label">
32-
{{ $t('owner') }}
33-
</div>
34-
<div class="subtitle is-size-6">
35-
<ProfileLink :address="owner" :inline="true" :showTwitter="true" />
25+
<div v-if="!isLoading">
26+
<div class="label">
27+
{{ $t('creator') }}
28+
</div>
29+
<div v-if="issuer" class="subtitle is-size-6">
30+
<ProfileLink :address="issuer" inline showTwitter />
31+
</div>
3632
</div>
33+
<b-skeleton :active="isLoading" width="40%" size="is-small"></b-skeleton>
34+
<b-skeleton :active="isLoading" width="60%" size="is-small"></b-skeleton>
3735
</div>
36+
3837
<div class="column is-6-tablet is-7-desktop is-8-widescreen">
3938
<CollectionActivity :nfts="stats" />
4039
</div>
40+
4141
<div class="column has-text-right">
4242
<Sharing v-if="sharingVisible"
4343
class="mb-2"
@@ -48,8 +48,8 @@
4848
</div>
4949
</div>
5050

51-
<b-tabs v-model="activeTab">
52-
<b-tab-item label="Collection">
51+
<b-tabs position="is-centered" v-model="activeTab">
52+
<b-tab-item label="Collection" value="collection">
5353
<div class="columns is-centered">
5454
<div class="column is-8 has-text-centered">
5555
<CollapseWrapper
@@ -68,7 +68,7 @@
6868
</b-field>
6969
</Search>
7070

71-
<GalleryCardList :items="collection.nfts" :horizontalLayout="true" />
71+
<GalleryCardList :items="collection.nfts" horizontalLayout />
7272

7373
<Pagination
7474
class="py-5"
@@ -79,8 +79,8 @@
7979
:per-page="first"
8080
/>
8181
</b-tab-item>
82-
<b-tab-item label="Activity">
83-
<CollectionPriceChart v-if="activeTab === 1" :priceData="priceData" />
82+
<b-tab-item label="Activity" value="activity">
83+
<CollectionPriceChart :priceData="priceData" />
8484
</b-tab-item>
8585
</b-tabs>
8686
</div>
@@ -89,17 +89,19 @@
8989

9090
<script lang="ts" >
9191
import { emptyObject } from '@/utils/empty'
92-
import { Component, Mixins } from 'vue-property-decorator'
92+
import { Component, Mixins, Watch } from 'vue-property-decorator'
9393
import { CollectionWithMeta, Interaction } from '../service/scheme'
9494
import {
9595
sanitizeIpfsUrl, fetchCollectionMetadata, sortByTimeStamp, onlyEvents, onlyPriceEvents,
9696
eventTimestamp, soldNFTPrice, collectionFloorPriceList, PriceDataType, onlyBuyEvents
9797
} from '../utils'
9898
import isShareMode from '@/utils/isShareMode'
99+
import shouldUpdate from '@/utils/shouldUpdate'
99100
import collectionById from '@/queries/collectionById.graphql'
100101
import nftListByCollection from '@/queries/nftListByCollection.graphql'
101102
import { CollectionMetadata } from '../types'
102103
import { NFT } from '@/components/rmrk/service/scheme'
104+
import { exist } from '@/components/rmrk/Gallery/Search/exist'
103105
import { SearchQuery } from './Search/types'
104106
import ChainMixin from '@/utils/mixins/chainMixin'
105107
@@ -114,6 +116,7 @@ const components = {
114116
DonationButton: () => import('@/components/transfer/DonationButton.vue'),
115117
Layout: () => import('@/components/rmrk/Gallery/Layout.vue'),
116118
CollectionPriceChart: () => import('@/components/rmrk/Gallery/CollectionPriceChart.vue'),
119+
BasicImage: () => import('@/components/shared/view/BasicImage.vue'),
117120
CollapseWrapper: () => import('@/components/shared/collapse/CollapseWrapper.vue'),
118121
}
119122
@Component<CollectionItem>({
@@ -140,27 +143,30 @@ export default class CollectionItem extends Mixins(
140143
) {
141144
private id = '';
142145
private collection: CollectionWithMeta = emptyObject<CollectionWithMeta>();
143-
private isLoading = false;
144146
public meta: CollectionMetadata = emptyObject<CollectionMetadata>();
145147
private searchQuery: SearchQuery = {
146148
search: '',
147149
type: '',
148150
sortBy: 'BLOCK_NUMBER_DESC',
149151
listed: false,
150152
};
151-
private activeTab = 0;
153+
public activeTab = 'collection';
152154
private currentValue = 1;
153155
private first = 15;
154-
private total = 0;
156+
protected total = 0;
155157
protected stats: NFT[] = [];
156158
protected priceData: any = [];
157159
160+
get isLoading(): boolean {
161+
return this.$apollo.queries.collection.loading
162+
}
163+
158164
get offset(): number {
159165
return this.currentValue * this.first - this.first
160166
}
161167
162-
get image(): string {
163-
return this.meta.image || ''
168+
get image(): string|undefined {
169+
return this.meta.image
164170
}
165171
166172
get description(): string {
@@ -179,10 +185,6 @@ export default class CollectionItem extends Mixins(
179185
return this.collection.issuer || ''
180186
}
181187
182-
get owner(): string {
183-
return this.collection.issuer === (this.collection as any).currentOwner ? '' : (this.collection as any).currentOwner
184-
}
185-
186188
get sharingVisible(): boolean {
187189
return !isShareMode
188190
}
@@ -206,10 +208,12 @@ export default class CollectionItem extends Mixins(
206208
}
207209
208210
public created(): void {
209-
this.isLoading = true
210211
this.checkId()
212+
this.checkActiveTab()
213+
this.loadStats()
211214
this.$apollo.addSmartQuery('collection', {
212215
query: collectionById,
216+
loadingKey: 'isLoading',
213217
variables: () => {
214218
return {
215219
id: this.id,
@@ -226,11 +230,9 @@ export default class CollectionItem extends Mixins(
226230
result: this.handleResult,
227231
})
228232
229-
this.loadStats()
230-
this.isLoading = false
231233
}
232234
233-
public async loadStats(): Promise<void> {
235+
public loadStats(): void {
234236
const nftStatsP = this.$apollo.query({
235237
query: nftListByCollection,
236238
variables: {
@@ -245,29 +247,27 @@ export default class CollectionItem extends Mixins(
245247
}
246248
247249
public loadPriceData(): void {
248-
249250
this.priceData = []
250251
251-
const events : Interaction[][] = this.stats?.map(onlyEvents) || []
252-
const priceEvents : Interaction[][] = events.map(this.priceEvents) || []
252+
const events: Interaction[][] = this.stats?.map(onlyEvents) || []
253+
const priceEvents: Interaction[][] = events.map(this.priceEvents) || []
253254
254-
const overTime : string[] = priceEvents.flat().sort(sortByTimeStamp).map(eventTimestamp)
255+
const overTime: string[] = priceEvents.flat().sort(sortByTimeStamp).map(eventTimestamp)
255256
256-
const floorPriceData : PriceDataType[] = overTime.map(collectionFloorPriceList(priceEvents, this.decimals))
257+
const floorPriceData: PriceDataType[] = overTime.map(collectionFloorPriceList(priceEvents, this.decimals))
257258
258259
const buyEvents = events.map(onlyBuyEvents)?.flat().sort(sortByTimeStamp)
259-
const soldPriceData : PriceDataType[] = buyEvents?.map(soldNFTPrice(this.decimals))
260+
const soldPriceData: PriceDataType[] = buyEvents?.map(soldNFTPrice(this.decimals))
260261
261262
this.priceData = [floorPriceData, soldPriceData]
262263
}
263264
264265
public async handleResult({data}: any): Promise<void> {
265266
this.total = data.collectionEntity.nfts.totalCount
266-
this.fetchMetadata()
267+
await this.fetchMetadata()
267268
}
268269
269270
public async fetchMetadata(): Promise<void> {
270-
console.log(this.collection['metadata'], !this.meta['image'])
271271
if (this.collection['metadata'] && !this.meta['image']) {
272272
const meta = await fetchCollectionMetadata(this.collection)
273273
this.meta = {
@@ -283,12 +283,34 @@ export default class CollectionItem extends Mixins(
283283
}
284284
}
285285
286+
public checkActiveTab(): void {
287+
exist(this.$route.query.tab, (val) => {
288+
this.activeTab = val
289+
})
290+
}
291+
292+
@Watch('activeTab')
293+
protected onTabChange(val: string, oldVal: string): void {
294+
if (shouldUpdate(val, oldVal)) {
295+
this.$router.replace({
296+
name: String(this.$route.name),
297+
query: { tab: val },
298+
})
299+
}
300+
}
301+
286302
get iframeSettings(): Record<string, unknown> {
287303
return { width: '100%', height: '100vh' }
288304
}
289305
290-
protected priceEvents(nftEvents:Interaction[]) : Interaction[] {
306+
protected priceEvents(nftEvents: Interaction[]): Interaction[] {
291307
return nftEvents.filter(onlyPriceEvents)
292308
}
293309
}
294310
</script>
311+
312+
<style>
313+
.collection__image img {
314+
color: transparent;
315+
}
316+
</style>

src/components/rmrk/Gallery/GalleryCardList.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ export default class GalleryCardList extends Vue {
3838
@Prop({ default: 'nftDetail' }) public type!: string;
3939
@Prop({ default: 'rmrk/detail' }) public link!: string;
4040
@Prop() public items!: RmrkType[];
41-
@Prop({ default: false }) public horizontalLayout!: boolean;
42-
get classLayout() {
41+
@Prop({ type: Boolean, default: false }) public horizontalLayout!: boolean;
42+
get classLayout(): string {
4343
return this.$store.getters.getLayoutClass
4444
}
4545
}

src/components/shared/view/BasicImage.vue

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
:rounded="rounded"
1010
>
1111
<template #placeholder>
12-
<b-skeleton class="skeleton-placeholder" height="100%"></b-skeleton>
12+
<b-skeleton class="skeleton-placeholder" :circle="rounded" height="100%"></b-skeleton>
1313
</template>
1414
</b-image>
1515
</template>
@@ -32,9 +32,10 @@ export default class BasicImage extends Vue {
3232
</script>
3333

3434
<style scoped>
35-
.b-skeleton {
36-
height: 100%;
37-
position: absolute;
38-
top: 0;
35+
.b-skeleton {
36+
height: 100%;
37+
position: absolute;
38+
top: 0;
39+
left: 0;
3940
}
4041
</style>

src/utils/math.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,6 @@ export const between = (dateA: Date, dateB: Date) => (event: Interaction): boole
4040
(isBefore(parseISO(event.timestamp), dateB))
4141

4242

43-
export function uniqueCount<T>(self: T[]): number {
44-
return new Set(self).size
45-
}
43+
export const uniqueCount = <T>(self: T[]): number => new Set(self).size
4644

4745

0 commit comments

Comments
 (0)