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

Commit d12be61

Browse files
authored
Merge pull request #875 from roiLeo/feature/collection/sort
feat(collection): filter & search
2 parents 127cf5d + 794ae72 commit d12be61

File tree

6 files changed

+221
-18
lines changed

6 files changed

+221
-18
lines changed

src/components/rmrk/Gallery/CollectionItem.vue

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
</div>
5050
</div>
5151

52+
<Search v-bind.sync="searchQuery" />
53+
5254
<GalleryCardList :items="collection.nfts" />
5355

5456
</div>
@@ -63,12 +65,17 @@ import { sanitizeIpfsUrl, fetchCollectionMetadata } from '../utils'
6365
import isShareMode from '@/utils/isShareMode'
6466
import collectionById from '@/queries/collectionById.graphql'
6567
import { CollectionMetadata } from '../types'
68+
import { NFT } from '@/components/rmrk/service/scheme'
69+
import { SearchQuery } from './Search/types'
70+
71+
6672
const components = {
6773
GalleryCardList: () => import('@/components/rmrk/Gallery/GalleryCardList.vue'),
6874
CollectionActivity: () => import('@/components/rmrk/Gallery/CollectionActivity.vue'),
6975
Sharing: () => import('@/components/rmrk/Gallery/Item/Sharing.vue'),
7076
ProfileLink: () => import('@/components/rmrk/Profile/ProfileLink.vue'),
7177
VueMarkdown: () => import('vue-markdown-render'),
78+
Search: () => import('./Search/SearchBarCollection.vue'),
7279
}
7380
@Component<CollectionItem>({
7481
metaInfo() {
@@ -94,45 +101,73 @@ export default class CollectionItem extends Vue {
94101
private collection: CollectionWithMeta = emptyObject<CollectionWithMeta>();
95102
private isLoading = false;
96103
public meta: CollectionMetadata = emptyObject<CollectionMetadata>();
97-
98-
get image() {
104+
private searchQuery: SearchQuery = {
105+
search: '',
106+
type: '',
107+
sortBy: 'BLOCK_NUMBER_DESC',
108+
listed: false,
109+
};
110+
111+
get image(): string {
99112
return this.meta.image || ''
100113
}
101114
102-
get description() {
115+
get description(): string {
103116
return this.meta.description || ''
104117
}
105118
106-
get name() {
119+
get name(): string {
107120
return this.collection.name || this.id
108121
}
109122
110-
get nfts() {
123+
get nfts(): NFT[] {
111124
return this.collection.nfts || []
112125
}
113126
114-
get issuer() {
127+
get issuer(): string {
115128
return this.collection.issuer || ''
116129
}
117130
118-
get owner() {
131+
get owner(): string {
119132
return this.collection.issuer === (this.collection as any).currentOwner ? '' : (this.collection as any).currentOwner
120133
}
121134
122-
get sharingVisible() {
135+
get sharingVisible(): boolean {
123136
return !isShareMode
124137
}
125138
139+
private buildSearchParam(): Record<string, unknown>[] {
140+
const params = []
141+
142+
if (this.searchQuery.search) {
143+
params.push({
144+
name: { likeInsensitive: `%${this.searchQuery.search}%` }
145+
})
146+
}
147+
148+
if (this.searchQuery.listed) {
149+
params.push({
150+
price: { greaterThan: '0' }
151+
})
152+
}
153+
154+
return params
155+
}
156+
126157
public created() {
127158
this.isLoading = true
128159
this.checkId()
129-
this.$apollo.addSmartQuery('collection',{
160+
this.$apollo.addSmartQuery('collection', {
130161
query: collectionById,
131-
variables: {
132-
id: this.id
162+
variables: () => {
163+
return {
164+
id: this.id,
165+
orderBy: this.searchQuery.sortBy,
166+
search: this.buildSearchParam()
167+
}
133168
},
134169
update: ({ collectionEntity }) => { return { ...collectionEntity, nfts: collectionEntity.nfts.nodes } },
135-
result: () => this.fetchMetadata()
170+
result: () => this.fetchMetadata(),
136171
})
137172
this.isLoading = false
138173
}

src/components/rmrk/Gallery/Search/SearchBar.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export default class SearchBar extends Vue {
122122
replaceUrl(value: string, key = 'search'): void {
123123
this.$router
124124
.replace({
125-
name: 'nft',
125+
name: String(this.$route.name),
126126
query: { ...this.$route.query, search: this.searchQuery, [key]: value }
127127
})
128128
.catch(console.warn /*Navigation Duplicate err fix later */)
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<template>
2+
<div class="box mb-3 mt-5">
3+
<b-field grouped>
4+
<b-field>
5+
<b-button
6+
aria-controls="contentIdForA11y1"
7+
label="Filter"
8+
icon-left="filter"
9+
type="is-primary"
10+
expanded
11+
@click="isVisible = !isVisible"
12+
/>
13+
</b-field>
14+
<b-field expanded>
15+
<b-input
16+
placeholder="Search..."
17+
type="search"
18+
v-model="searchQuery"
19+
icon="search"
20+
expanded
21+
>
22+
</b-input>
23+
</b-field>
24+
<BasicSwitch
25+
class="is-flex"
26+
v-model="vListed"
27+
label="sort.listed"
28+
size="is-medium"
29+
/>
30+
</b-field>
31+
<slot />
32+
33+
<transition name="fade">
34+
<div v-if="isVisible">
35+
<Sort
36+
:value="sortBy"
37+
@input="updateSortBy"
38+
/>
39+
</div>
40+
</transition>
41+
</div>
42+
</template>
43+
44+
<script lang="ts" >
45+
import { Component, Prop, Vue, Emit } from 'vue-property-decorator'
46+
import { Debounce } from 'vue-debounce-decorator'
47+
import shouldUpdate from '@/utils/shouldUpdate'
48+
import { exist } from './exist'
49+
50+
@Component({
51+
components: {
52+
Sort: () => import('./SearchSortDropdown.vue'),
53+
TypeTagInput: () => import('./TypeTagInput.vue'),
54+
Pagination: () => import('@/components/rmrk/Gallery/Pagination.vue'),
55+
BasicSwitch: () => import('@/components/shared/form/BasicSwitch.vue'),
56+
},
57+
})
58+
export default class SearchBar extends Vue {
59+
@Prop(String) public search!: string
60+
@Prop(String) public type!: string
61+
@Prop(String) public sortBy!: string
62+
@Prop(Boolean) public listed!: boolean
63+
64+
protected isVisible = false
65+
66+
public mounted(): void {
67+
exist(this.$route.query.search, this.updateSearch)
68+
exist(this.$route.query.type, this.updateType)
69+
exist(this.$route.query.sort, this.updateSortBy)
70+
exist(this.$route.query.listed, this.updateListed)
71+
}
72+
73+
get vListed(): boolean {
74+
return this.listed
75+
}
76+
77+
set vListed(listed: boolean) {
78+
this.updateListed(listed)
79+
}
80+
81+
get searchQuery(): string {
82+
return this.search
83+
}
84+
85+
set searchQuery(value: string) {
86+
this.updateSearch(value)
87+
}
88+
89+
get typeQuery(): string {
90+
return this.type
91+
}
92+
93+
set typeQuery(value: string) {
94+
this.updateType(value)
95+
}
96+
97+
@Emit('update:listed')
98+
@Debounce(50)
99+
updateListed(value: string | boolean): boolean {
100+
const v = String(value)
101+
this.replaceUrl(v, 'listed')
102+
return v === 'true'
103+
}
104+
105+
@Emit('update:type')
106+
@Debounce(50)
107+
updateType(value: string): string {
108+
this.replaceUrl(value, 'type')
109+
return value
110+
}
111+
112+
@Emit('update:sortBy')
113+
@Debounce(400)
114+
updateSortBy(value: string): string {
115+
this.replaceUrl(value, 'sort')
116+
return value
117+
}
118+
119+
@Emit('update:search')
120+
@Debounce(400)
121+
updateSearch(value: string): string {
122+
shouldUpdate(value, this.searchQuery) && this.replaceUrl(value)
123+
return value
124+
}
125+
126+
@Debounce(100)
127+
replaceUrl(value: string, key = 'search'): void {
128+
this.$router
129+
.replace({
130+
name: String(this.$route.name),
131+
query: {
132+
...this.$route.query,
133+
search: this.searchQuery,
134+
[key]: value,
135+
},
136+
})
137+
.catch(console.warn /*Navigation Duplicate err fix later */)
138+
}
139+
}
140+
</script>
141+
142+
<style scoped lang="scss">
143+
@import '@/styles/variables';
144+
145+
.card {
146+
box-shadow: 0px 0px 5px 0.5px $primary;
147+
}
148+
149+
.fade-enter-active,
150+
.fade-leave-active {
151+
transition: opacity 0.5s ease;
152+
}
153+
154+
.fade-enter-from,
155+
.fade-leave-to {
156+
opacity: 0;
157+
}
158+
</style>

src/icons.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
faArrowsAlt, faCompressAlt, faCompressArrowsAlt,
2020
faAngleLeft, faAngleRight, faReply,
2121
faExternalLinkSquareAlt, faGift,
22-
faEyeSlash, faArrowUp, faUser, faGlobe, faExclamationCircle,
22+
faEyeSlash, faArrowUp, faUser, faGlobe, faExclamationCircle, faFilter,
2323

2424
} from '@fortawesome/free-solid-svg-icons'
2525

@@ -58,7 +58,7 @@ library.add(
5858
faExclamationTriangle, faCalculator,
5959
faArrowsAlt, faCompressAlt, faCompressArrowsAlt,
6060
faEyeSlash, faArrowUp, faUser, faGlobe,
61-
faExclamationCircle, faGift,
61+
faExclamationCircle, faFilter, faGift,
6262

6363
// Social
6464
faTwitter, faTelegram, faFacebook,

src/locales/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@
187187
"UPDATED_AT_ASC": "Long time no interaction",
188188
"PRICE_DESC": "From most expensive",
189189
"PRICE_ASC": "From cheaper",
190-
"listed": "Buy now"
190+
"listed": "BUY NOW"
191191
},
192192
"mint": {
193193
"submit": "Click to create NFT(s)",

src/queries/collectionById.graphql

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
#import "./collection.graphql"
22

3-
query collectionById($id: String!) {
3+
query collectionById(
4+
$id: String!
5+
$search: [NFTEntityFilter!]
6+
$orderBy: NftEntitiesOrderBy = BLOCK_NUMBER_DESC
7+
) {
48
collectionEntity(id: $id) {
59
...collection
6-
nfts(filter: { burned: { distinctFrom: true } }) {
10+
nfts(
11+
orderBy: [$orderBy, BLOCK_NUMBER_DESC]
12+
filter: {
13+
burned: { distinctFrom: true }
14+
and: $search
15+
}
16+
) {
717
nodes {
818
id
919
metadata

0 commit comments

Comments
 (0)