diff --git a/backend/src/index.ts b/backend/src/index.ts
index f78c5922b..5dea199fc 100644
--- a/backend/src/index.ts
+++ b/backend/src/index.ts
@@ -319,7 +319,9 @@ class Server {
if (Common.isLiquid()) {
this.app
.get(config.MEMPOOL.API_URL_PREFIX + 'assets/icons', routes.getAllLiquidIcon)
+ .get(config.MEMPOOL.API_URL_PREFIX + 'assets/featured', routes.$getAllFeaturedLiquidAssets)
.get(config.MEMPOOL.API_URL_PREFIX + 'asset/:assetId/icon', routes.getLiquidIcon)
+ .get(config.MEMPOOL.API_URL_PREFIX + 'asset-group/:id', routes.$getAssetGroup)
;
}
diff --git a/backend/src/routes.ts b/backend/src/routes.ts
index 044f9a3ac..e937678cc 100644
--- a/backend/src/routes.ts
+++ b/backend/src/routes.ts
@@ -21,6 +21,7 @@ import bitcoinClient from './api/bitcoin/bitcoin-client';
import elementsParser from './api/liquid/elements-parser';
import icons from './api/liquid/icons';
import miningStats from './api/mining';
+import axios from 'axios';
class Routes {
constructor() {}
@@ -855,6 +856,25 @@ class Routes {
res.status(404).send('Asset icons not found');
}
}
+
+ public async $getAllFeaturedLiquidAssets(req: Request, res: Response) {
+ try {
+ const response = await axios.get('https://mempool.space/api/v1/assets/featured', { responseType: 'stream', timeout: 10000 });
+ response.data.pipe(res);
+ } catch (e) {
+ res.status(500).end();
+ }
+ }
+
+ public async $getAssetGroup(req: Request, res: Response) {
+ try {
+ const response = await axios.get('https://mempool.space/api/v1/asset-group/' + parseInt(req.params.id, 10),
+ { responseType: 'stream', timeout: 10000 });
+ response.data.pipe(res);
+ } catch (e) {
+ res.status(500).end();
+ }
+ }
}
export default new Routes();
diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts
index 36a53781f..243691661 100644
--- a/frontend/src/app/app-routing.module.ts
+++ b/frontend/src/app/app-routing.module.ts
@@ -10,7 +10,7 @@ import { TelevisionComponent } from './components/television/television.componen
import { StatisticsComponent } from './components/statistics/statistics.component';
import { MempoolBlockComponent } from './components/mempool-block/mempool-block.component';
import { AssetComponent } from './components/asset/asset.component';
-import { AssetsComponent } from './assets/assets.component';
+import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component';
import { StatusViewComponent } from './components/status-view/status-view.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { LatestBlocksComponent } from './components/latest-blocks/latest-blocks.component';
@@ -23,6 +23,9 @@ import { SponsorComponent } from './components/sponsor/sponsor.component';
import { LiquidMasterPageComponent } from './components/liquid-master-page/liquid-master-page.component';
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
import { PoolRankingComponent } from './components/pool-ranking/pool-ranking.component';
+import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component';
+import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component';
+import { AssetsComponent } from './components/assets/assets.component';
let routes: Routes = [
{
@@ -343,13 +346,31 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
path: 'address/:id',
component: AddressComponent
},
- {
- path: 'asset/:id',
- component: AssetComponent
- },
{
path: 'assets',
- component: AssetsComponent,
+ component: AssetsNavComponent,
+ children: [
+ {
+ path: 'featured',
+ component: AssetsFeaturedComponent,
+ },
+ {
+ path: 'all',
+ component: AssetsComponent,
+ },
+ {
+ path: 'asset/:id',
+ component: AssetComponent
+ },
+ {
+ path: 'asset-group/:id',
+ component: AssetGroupComponent
+ },
+ {
+ path: '**',
+ redirectTo: 'featured'
+ }
+ ]
},
{
path: 'docs/api/:type',
@@ -440,7 +461,7 @@ if (browserWindowEnv && browserWindowEnv.BASE_MODULE === 'liquid') {
},
{
path: 'assets',
- component: AssetsComponent,
+ component: AssetsNavComponent,
},
{
path: 'docs/api/:type',
diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts
index f9eae0666..97fc16204 100644
--- a/frontend/src/app/app.module.ts
+++ b/frontend/src/app/app.module.ts
@@ -40,7 +40,8 @@ import { MempoolGraphComponent } from './components/mempool-graph/mempool-graph.
import { PoolRankingComponent } from './components/pool-ranking/pool-ranking.component';
import { LbtcPegsGraphComponent } from './components/lbtc-pegs-graph/lbtc-pegs-graph.component';
import { AssetComponent } from './components/asset/asset.component';
-import { AssetsComponent } from './assets/assets.component';
+import { AssetsComponent } from './components/assets/assets.component';
+import { AssetsNavComponent } from './components/assets/assets-nav/assets-nav.component';
import { StatusViewComponent } from './components/status-view/status-view.component';
import { MinerComponent } from './components/miner/miner.component';
import { SharedModule } from './shared/shared.module';
@@ -64,6 +65,8 @@ import { LanguageService } from './services/language.service';
import { SponsorComponent } from './components/sponsor/sponsor.component';
import { PushTransactionComponent } from './components/push-transaction/push-transaction.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { AssetsFeaturedComponent } from './components/assets/assets-featured/assets-featured.component';
+import { AssetGroupComponent } from './components/assets/asset-group/asset-group.component';
@NgModule({
declarations: [
@@ -110,6 +113,9 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
PushTransactionComponent,
DocsComponent,
ApiDocsNavComponent,
+ AssetsNavComponent,
+ AssetsFeaturedComponent,
+ AssetGroupComponent,
],
imports: [
BrowserModule.withServerTransition({ appId: 'serverApp' }),
diff --git a/frontend/src/app/assets/assets.component.html b/frontend/src/app/assets/assets.component.html
deleted file mode 100644
index c8962cd15..000000000
--- a/frontend/src/app/assets/assets.component.html
+++ /dev/null
@@ -1,71 +0,0 @@
-
-
-
Registered assets
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Name |
- Ticker |
- Issuer domain |
- Asset ID |
-
-
-
- |
- |
- |
- |
-
-
-
-
-
-
-
-
- Error loading assets data.
-
- {{ error.error }}
-
-
-
-
-
-
diff --git a/frontend/src/app/assets/assets.component.spec.ts b/frontend/src/app/assets/assets.component.spec.ts
deleted file mode 100644
index ed39b7122..000000000
--- a/frontend/src/app/assets/assets.component.spec.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-
-import { AssetsComponent } from './assets.component';
-
-describe('AssetsComponent', () => {
- let component: AssetsComponent;
- let fixture: ComponentFixture;
-
- beforeEach(async(() => {
- TestBed.configureTestingModule({
- declarations: [ AssetsComponent ]
- })
- .compileComponents();
- }));
-
- beforeEach(() => {
- fixture = TestBed.createComponent(AssetsComponent);
- component = fixture.componentInstance;
- fixture.detectChanges();
- });
-
- it('should create', () => {
- expect(component).toBeTruthy();
- });
-});
diff --git a/frontend/src/app/components/assets/asset-group/asset-group.component.html b/frontend/src/app/components/assets/asset-group/asset-group.component.html
new file mode 100644
index 000000000..a6858b76f
--- /dev/null
+++ b/frontend/src/app/components/assets/asset-group/asset-group.component.html
@@ -0,0 +1,26 @@
+
+
+
+
{{ group.group.name }}
+
+
Group of {{ group.group.assets.length | number }} assets
+
+
+
+
+
+
+
+
+
+
+
![]()
+
+
{{ asset.ticker }}
+
+
+
+
+
diff --git a/frontend/src/app/components/assets/asset-group/asset-group.component.scss b/frontend/src/app/components/assets/asset-group/asset-group.component.scss
new file mode 100644
index 000000000..7a0735a24
--- /dev/null
+++ b/frontend/src/app/components/assets/asset-group/asset-group.component.scss
@@ -0,0 +1,57 @@
+.image {
+ width: 150px;
+ float: left;
+}
+
+.main-title {
+ float: left
+}
+
+.sub-title {
+ color: grey;
+}
+
+.featuredBox {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: center;
+ gap: 27px;
+}
+
+.card {
+ background-color: #1d1f31;
+ width: 200px;
+ height: 200px;
+ align-items: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.title {
+ font-size: 14px;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+.sub-title {
+ color: grey;
+}
+
+.assetIcon {
+ width: 100px;
+ height: 100px;
+}
+
+.image {
+ width: 100px;
+ height: 100px;
+ align-self: center;
+}
+
+.view-link {
+ margin-top: 30px;
+}
+
+.ticker {
+ color: grey;
+}
diff --git a/frontend/src/app/components/assets/asset-group/asset-group.component.ts b/frontend/src/app/components/assets/asset-group/asset-group.component.ts
new file mode 100644
index 000000000..0143121b5
--- /dev/null
+++ b/frontend/src/app/components/assets/asset-group/asset-group.component.ts
@@ -0,0 +1,45 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { combineLatest, Observable } from 'rxjs';
+import { map, switchMap } from 'rxjs/operators';
+import { ApiService } from 'src/app/services/api.service';
+import { AssetsService } from 'src/app/services/assets.service';
+
+@Component({
+ selector: 'app-asset-group',
+ templateUrl: './asset-group.component.html',
+ styleUrls: ['./asset-group.component.scss']
+})
+export class AssetGroupComponent implements OnInit {
+ group$: Observable;
+
+ constructor(
+ private route: ActivatedRoute,
+ private apiService: ApiService,
+ private assetsService: AssetsService,
+ ) { }
+
+ ngOnInit(): void {
+ this.group$ = this.route.paramMap
+ .pipe(
+ switchMap((params: ParamMap) => {
+ return combineLatest([
+ this.assetsService.getAssetsJson$,
+ this.apiService.getAssetGroup$(params.get('id')),
+ ]);
+ }),
+ map(([assets, group]) => {
+ const items = [];
+ // @ts-ignore
+ for (const item of group.assets) {
+ items.push(assets[item]);
+ }
+ console.log(group);
+ return {
+ group: group,
+ assets: items
+ };
+ })
+ );
+ }
+}
diff --git a/frontend/src/app/components/assets/assets-featured/assets-featured.component.html b/frontend/src/app/components/assets/assets-featured/assets-featured.component.html
new file mode 100644
index 000000000..bb5ab7f3c
--- /dev/null
+++ b/frontend/src/app/components/assets/assets-featured/assets-featured.component.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+ Group of {{ group.assets.length | number }} assets
+
+
+
+
+ {{ group.ticker }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/app/components/assets/assets-featured/assets-featured.component.scss b/frontend/src/app/components/assets/assets-featured/assets-featured.component.scss
new file mode 100644
index 000000000..41be2b748
--- /dev/null
+++ b/frontend/src/app/components/assets/assets-featured/assets-featured.component.scss
@@ -0,0 +1,46 @@
+
+.featuredBox {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: center;
+ gap: 27px;
+}
+
+.card {
+ background-color: #1d1f31;
+ width: 200px;
+ height: 200px;
+ align-items: center;
+ justify-content: center;
+ flex-wrap: wrap;
+}
+
+.title {
+ font-size: 14px;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+.sub-title {
+ color: grey;
+ font-size: 12px;
+}
+
+.assetIcon {
+ width: 100px;
+ height: 100px;
+}
+
+.image {
+ width: 100px;
+ height: 100px;
+ align-self: center;
+}
+
+.view-link {
+ margin-top: 30px;
+}
+
+.ticker {
+ color: grey;
+}
diff --git a/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts b/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts
new file mode 100644
index 000000000..23c84679b
--- /dev/null
+++ b/frontend/src/app/components/assets/assets-featured/assets-featured.component.ts
@@ -0,0 +1,34 @@
+import { Component, OnInit } from '@angular/core';
+import { combineLatest, Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { ApiService } from 'src/app/services/api.service';
+import { AssetsService } from 'src/app/services/assets.service';
+
+@Component({
+ selector: 'app-assets-featured',
+ templateUrl: './assets-featured.component.html',
+ styleUrls: ['./assets-featured.component.scss']
+})
+export class AssetsFeaturedComponent implements OnInit {
+ featuredAssets$: Observable;
+
+ constructor(
+ private apiService: ApiService,
+ private assetsService: AssetsService,
+ ) { }
+
+ ngOnInit(): void {
+ this.featuredAssets$ = combineLatest([
+ this.assetsService.getAssetsJson$,
+ this.apiService.listFeaturedAssets$(),
+ ]).pipe(
+ map(([assetsJson, featured]) => {
+ return {
+ assetsJson: assetsJson,
+ featured: featured,
+ };
+ })
+ );
+ }
+
+}
diff --git a/frontend/src/app/components/assets/assets-nav/assets-nav.component.html b/frontend/src/app/components/assets/assets-nav/assets-nav.component.html
new file mode 100644
index 000000000..9ee7ac72a
--- /dev/null
+++ b/frontend/src/app/components/assets/assets-nav/assets-nav.component.html
@@ -0,0 +1,31 @@
+
+
+
Assets
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/assets/assets-nav/assets-nav.component.scss b/frontend/src/app/components/assets/assets-nav/assets-nav.component.scss
new file mode 100644
index 000000000..530ed7bdf
--- /dev/null
+++ b/frontend/src/app/components/assets/assets-nav/assets-nav.component.scss
@@ -0,0 +1,9 @@
+ul {
+ margin-bottom: 20px;
+ float: left;
+
+}
+
+form {
+ float: right;
+}
diff --git a/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts b/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts
new file mode 100644
index 000000000..e1b31372b
--- /dev/null
+++ b/frontend/src/app/components/assets/assets-nav/assets-nav.component.ts
@@ -0,0 +1,23 @@
+import { Component, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+@Component({
+ selector: 'app-assets-nav',
+ templateUrl: './assets-nav.component.html',
+ styleUrls: ['./assets-nav.component.scss']
+})
+export class AssetsNavComponent implements OnInit {
+ activeTab = 0;
+ searchForm: FormGroup;
+
+ constructor(
+ private formBuilder: FormBuilder,
+ ) { }
+
+ ngOnInit(): void {
+ this.searchForm = this.formBuilder.group({
+ searchText: [{ value: '', disabled: true }, Validators.required]
+ });
+ }
+
+}
diff --git a/frontend/src/app/components/assets/assets.component.html b/frontend/src/app/components/assets/assets.component.html
new file mode 100644
index 000000000..ec9ac079f
--- /dev/null
+++ b/frontend/src/app/components/assets/assets.component.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ Ticker |
+ Issuer domain |
+ Asset ID |
+
+
+
+ |
+ |
+ |
+ |
+
+
+
+
+
+
+
+
+ Error loading assets data.
+
+ {{ error.error }}
+
+
diff --git a/frontend/src/app/assets/assets.component.scss b/frontend/src/app/components/assets/assets.component.scss
similarity index 100%
rename from frontend/src/app/assets/assets.component.scss
rename to frontend/src/app/components/assets/assets.component.scss
diff --git a/frontend/src/app/assets/assets.component.ts b/frontend/src/app/components/assets/assets.component.ts
similarity index 94%
rename from frontend/src/app/assets/assets.component.ts
rename to frontend/src/app/components/assets/assets.component.ts
index 49c42d76e..d5cbd10ab 100644
--- a/frontend/src/app/assets/assets.component.ts
+++ b/frontend/src/app/components/assets/assets.component.ts
@@ -1,13 +1,13 @@
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
-import { AssetsService } from '../services/assets.service';
+import { AssetsService } from 'src/app/services/assets.service';
import { environment } from 'src/environments/environment';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { distinctUntilChanged, map, filter, mergeMap, tap, take } from 'rxjs/operators';
import { ActivatedRoute, Router } from '@angular/router';
import { merge, combineLatest, Observable } from 'rxjs';
-import { AssetExtended } from '../interfaces/electrs.interface';
-import { SeoService } from '../services/seo.service';
-import { StateService } from '../services/state.service';
+import { AssetExtended } from 'src/app/interfaces/electrs.interface';
+import { SeoService } from 'src/app/services/seo.service';
+import { StateService } from 'src/app/services/state.service';
@Component({
selector: 'app-assets',
@@ -23,9 +23,9 @@ export class AssetsComponent implements OnInit {
searchForm: FormGroup;
assets$: Observable;
+ page = 1;
error: any;
- page = 1;
itemsPerPage: number;
contentSpace = window.innerHeight - (250 + 200);
fiveItemsPxSize = 250;
@@ -49,7 +49,7 @@ export class AssetsComponent implements OnInit {
this.assets$ = combineLatest([
this.assetsService.getAssetsJson$,
- this.route.queryParams
+ this.route.queryParams,
])
.pipe(
take(1),
diff --git a/frontend/src/app/services/api.service.ts b/frontend/src/app/services/api.service.ts
index 03200c64c..3d9125c25 100644
--- a/frontend/src/app/services/api.service.ts
+++ b/frontend/src/app/services/api.service.ts
@@ -117,6 +117,14 @@ export class ApiService {
return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/pegs/month');
}
+ listFeaturedAssets$(): Observable {
+ return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/assets/featured');
+ }
+
+ getAssetGroup$(id: string): Observable {
+ return this.httpClient.get(this.apiBaseUrl + this.apiBasePath + '/api/v1/asset-group/' + id);
+ }
+
postTransaction$(hexPayload: string): Observable {
return this.httpClient.post(this.apiBaseUrl + this.apiBasePath + '/api/tx', hexPayload, { responseType: 'text' as 'json'});
}