From efaf25e58dee6ea6abde36e4726d84c09a7ac90f Mon Sep 17 00:00:00 2001 From: dangershony Date: Tue, 16 Jul 2024 21:19:40 +0100 Subject: [PATCH] Adding an Angor tab --- .../ClientApp/src/app/app.module.ts | 18 +- .../angor-project.component.html | 45 +++++ .../angor-project/angor-project.component.ts | 180 ++++++++++++++++++ .../angor-projects.component.html | 34 ++++ .../angor-projects.component.ts | 155 +++++++++++++++ .../src/app/nav-menu/nav-menu.component.html | 51 ++--- .../src/app/services/setup.service.ts | 4 + .../Properties/launchSettings.json | 9 + 8 files changed, 471 insertions(+), 25 deletions(-) create mode 100644 src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.html create mode 100644 src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.ts create mode 100644 src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.html create mode 100644 src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.ts diff --git a/src/Blockcore.Explorer/ClientApp/src/app/app.module.ts b/src/Blockcore.Explorer/ClientApp/src/app/app.module.ts index d22920e..4f4660e 100644 --- a/src/Blockcore.Explorer/ClientApp/src/app/app.module.ts +++ b/src/Blockcore.Explorer/ClientApp/src/app/app.module.ts @@ -43,6 +43,9 @@ import { ContractListComponent } from './explorer/contract-list/contract-list.co import { ContractListByTypeComponent } from './explorer/contract-listbytype/contract-listbytype.component'; import { ContractNonFungibleTokenComponent } from './explorer/contract-nonfungibletoken/contract-nonfungibletoken.component'; +import { AngorProjects } from './explorer/angor-projects/angor-projects.component'; +import { AngorProject } from './explorer/angor-project/angor-project.component'; + const routes: Routes = [ { path: '', component: HomeComponent, pathMatch: 'full', resolve: { @@ -159,6 +162,17 @@ const routes: Routes = [ chain: LoadingResolverService } }, + + { + path: ':chain/explorer/angor-projects', component: AngorProjects, resolve: { + chain: LoadingResolverService + } + }, + { + path: ':chain/explorer/angor-project/:projectid', component: AngorProject, resolve: { + chain: LoadingResolverService + } + }, ]; @NgModule({ @@ -201,7 +215,9 @@ const routes: Routes = [ ContractStandardTokenComponent, ContractListComponent, ContractListByTypeComponent, - ContractNonFungibleTokenComponent + ContractNonFungibleTokenComponent, + AngorProjects, + AngorProject, ], imports: [ BrowserModule, diff --git a/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.html b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.html new file mode 100644 index 0000000..02ac2e6 --- /dev/null +++ b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.html @@ -0,0 +1,45 @@ + + +
+

Project Details

+ Loading project details... + +
+ Error: {{error.message}}

+ {{error | json}} +
+ +
+

Project Identifier: {{ project.projectIdentifier }}

+

Founder Key: {{ project.founderKey }}

+

Nostr Pub Key: {{ project.nostrPubKey }}

+

Created On Block: {{ project.createdOnBlock }}

+

+ Transaction id: + {{ project.trxId | slice:0:30 }} +

+

Total Investments Count: {{ project.totalInvestmentsCount }}

+ +

Investments

+ + + + + + + + + + + + + + + + + + + +
Transaction idTotal Amount
{{ investment.transactionId | slice:0:30 }}{{ investment.totalAmount | amount }}
+
+
diff --git a/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.ts b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.ts new file mode 100644 index 0000000..cfff63c --- /dev/null +++ b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-project/angor-project.component.ts @@ -0,0 +1,180 @@ +import { Component, HostBinding, OnInit, OnDestroy, HostListener } from '@angular/core'; +import { ActivatedRoute, Route, Router } from '@angular/router'; +import { ApiComponent } from 'src/app/api/api.component'; +import { ApiService, HttpError } from 'src/app/services/api.service'; +import { SetupService } from 'src/app/services/setup.service'; +import { ScrollEvent } from 'src/app/shared/scroll.directive'; + +@Component({ + selector: 'angor-project-component', + templateUrl: './angor-project.component.html' +}) +export class AngorProject implements OnInit, OnDestroy { + @HostBinding('class.content-centered-top') hostClass = true; + + info: any; + node: any; + blockchain: any; + network: any; + configuration: any; + consensus: any; + peers: any; + blocks: any; + project: any; + investments: any + + timerInfo: any; + timerBlocks: any; + timerTransactions: any; + contractType: any; + balance: any; + detailsVisible = false; + lastBlockHeight: number; + subscription: any; + limit = 10; + loading = false; + count = 0; + total: any; + link: string; + error: any; + errorTransactions: any; + navPath: any; + + constructor( + private api: ApiService, + private router: Router, + public setup: SetupService, + private activatedRoute: ActivatedRoute) { + + this.activatedRoute.paramMap.subscribe(async params => { + + + const id: any = params.get('projectid'); + this.project = null; + + try { + + this.navPath = "../../"; + + await this.getProject('/api/query/Angor/projects/' + id); + + await this.getInvestments('/api/query/Angor/projects/' + id + '/investments??offset=&limit=' + this.limit); + + } catch (err) { + if (err.message[0] === '{') { + this.errorTransactions = JSON.parse(err.message); + } else { + this.errorTransactions = err; + } + } + }); + } + + async ngOnInit() { + + } + + toggleDetails() { + this.detailsVisible = !this.detailsVisible; + } + + ngOnDestroy(): void { + + } + + async getProject(url) { + // If no URL, then likely reached the end. + if (!url) { + return; + } + + const baseUrl = this.api.baseUrl.replace('/api', ''); + // For the block scrolling (using link http header), we must manually set full URL. + const response = await this.api.request(baseUrl + url); + + // When the offset is not set (0), we should reverse the order of items. + const list = await response.json(); + + if (response.status !== 200) { + if (list && list.status) { + throw new HttpError(list.status, url, JSON.stringify(list)); + } else { + throw new HttpError(response.status, url, response.statusText); + } + } + + this.project = list; + } + + async getInvestments(url) { + // If no URL, then likely reached the end. + if (!url) { + return; + } + + const baseUrl = this.api.baseUrl.replace('/api', ''); + // For the block scrolling (using link http header), we must manually set full URL. + const response = await this.api.request(baseUrl + url); + + // When the offset is not set (0), we should reverse the order of items. + const list = await response.json(); + + if (response.status !== 200) { + if (list && list.status) { + throw new HttpError(list.status, url, JSON.stringify(list)); + } else { + throw new HttpError(response.status, url, response.statusText); + } + } + + list.sort((b, a) => { + if (a.createdOnBlock === b.createdOnBlock) { + return 0; + } + if (a.createdOnBlock < b.createdOnBlock) { + return -1; + } + if (a.createdOnBlock > b.createdOnBlock) { + return 1; + } + }); + + + this.total = response.headers.get('Pagination-Total'); + const linkHeader = response.headers.get('Link'); + const links = this.api.parseLinkHeader(linkHeader); + + // This will be set to undefined/null when no more next links is available. + this.link = links['previous']; + + if (!this.investments) { + this.investments = []; + } + + this.investments = [...this.investments, ...list]; + this.count++; + } + + async onScroll(event: ScrollEvent) { + console.log('scroll occurred', event); + + if (event.isReachingBottom) { + console.log(`the user is reaching the bottom`); + + this.loading = true; + + setTimeout(async () => { + await this.getInvestments(this.link); + this.loading = false; + }); + + } + if (event.isReachingTop) { + console.log(`the user is reaching the top`); + } + if (event.isWindowEvent) { + console.log(`This event is fired on Window not on an element.`); + } + } +} + diff --git a/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.html b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.html new file mode 100644 index 0000000..8d9d276 --- /dev/null +++ b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.html @@ -0,0 +1,34 @@ + + +
+

Projects

+ Loading projects... + +
+ Error: {{error.message}}

+ {{error | json}} +
+ +
+ + + + + + + + + + + + + + + + + +
Project IdentifierCreated On BlockTransaction ID
{{ project.projectIdentifier }}{{ project.createdOnBlock }}{{ project.trxId | slice:0:30 }}
+
+
diff --git a/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.ts b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.ts new file mode 100644 index 0000000..4e06e21 --- /dev/null +++ b/src/Blockcore.Explorer/ClientApp/src/app/explorer/angor-projects/angor-projects.component.ts @@ -0,0 +1,155 @@ +import { Component, HostBinding, OnInit, OnDestroy, HostListener } from '@angular/core'; +import { ActivatedRoute, Route, Router } from '@angular/router'; +import { ApiComponent } from 'src/app/api/api.component'; +import { ApiService, HttpError } from 'src/app/services/api.service'; +import { SetupService } from 'src/app/services/setup.service'; +import { ScrollEvent } from 'src/app/shared/scroll.directive'; + +@Component({ + selector: 'angor-projects-component', + templateUrl: './angor-projects.component.html' +}) +export class AngorProjects implements OnInit, OnDestroy { + @HostBinding('class.content-centered-top') hostClass = true; + + info: any; + node: any; + blockchain: any; + network: any; + configuration: any; + consensus: any; + peers: any; + blocks: any; + projects: any; + + timerInfo: any; + timerBlocks: any; + timerTransactions: any; + contractType: any; + balance: any; + detailsVisible = false; + lastBlockHeight: number; + subscription: any; + limit = 10; + loading = false; + count = 0; + total: any; + link: string; + error: any; + errorTransactions: any; + navPath: any; + + constructor( + private api: ApiService, + private router: Router, + public setup: SetupService, + private activatedRoute: ActivatedRoute) { + + this.activatedRoute.paramMap.subscribe(async params => { + + + this.projects = null; + + try { + + this.navPath = "../../"; + + await this.getProjects('/api/query/Angor/projects/?offset=&limit=' + this.limit); + + } catch (err) { + if (err.message[0] === '{') { + this.errorTransactions = JSON.parse(err.message); + } else { + this.errorTransactions = err; + } + } + }); + } + + async ngOnInit() { + + } + + toggleDetails() { + this.detailsVisible = !this.detailsVisible; + } + + ngOnDestroy(): void { + + } + + + async getProjects(url) { + // If no URL, then likely reached the end. + if (!url) { + return; + } + + const baseUrl = this.api.baseUrl.replace('/api', ''); + // For the block scrolling (using link http header), we must manually set full URL. + const response = await this.api.request(baseUrl + url); + + // When the offset is not set (0), we should reverse the order of items. + const list = await response.json(); + + if (response.status !== 200) { + if (list && list.status) { + throw new HttpError(list.status, url, JSON.stringify(list)); + } else { + throw new HttpError(response.status, url, response.statusText); + } + } + + list.sort((b, a) => { + if (a.createdOnBlock === b.createdOnBlock) { + return 0; + } + if (a.createdOnBlock < b.createdOnBlock) { + return -1; + } + if (a.createdOnBlock > b.createdOnBlock) { + return 1; + } + }); + + + this.total = response.headers.get('Pagination-Total'); + const linkHeader = response.headers.get('Link'); + const links = this.api.parseLinkHeader(linkHeader); + + // This will be set to undefined/null when no more next links is available. + this.link = links['previous']; + + + + if (!this.projects) { + this.projects = []; + } + + this.projects = [...this.projects, ...list]; + this.count++; + } + + async onScroll(event: ScrollEvent) { + console.log('scroll occurred', event); + + if (event.isReachingBottom) { + console.log(`the user is reaching the bottom`); + + this.loading = true; + + setTimeout(async () => { + await this.getProjects(this.link); + this.loading = false; + }); + + } + if (event.isReachingTop) { + console.log(`the user is reaching the top`); + } + if (event.isWindowEvent) { + console.log(`This event is fired on Window not on an element.`); + } + } +} + diff --git a/src/Blockcore.Explorer/ClientApp/src/app/nav-menu/nav-menu.component.html b/src/Blockcore.Explorer/ClientApp/src/app/nav-menu/nav-menu.component.html index e732d8a..b64d1ec 100644 --- a/src/Blockcore.Explorer/ClientApp/src/app/nav-menu/nav-menu.component.html +++ b/src/Blockcore.Explorer/ClientApp/src/app/nav-menu/nav-menu.component.html @@ -13,28 +13,31 @@ diff --git a/src/Blockcore.Explorer/ClientApp/src/app/services/setup.service.ts b/src/Blockcore.Explorer/ClientApp/src/app/services/setup.service.ts index 6568533..ea339fb 100644 --- a/src/Blockcore.Explorer/ClientApp/src/app/services/setup.service.ts +++ b/src/Blockcore.Explorer/ClientApp/src/app/services/setup.service.ts @@ -111,6 +111,10 @@ export class SetupService { return null; } + isBitcoin() { + return this.Chain.Symbol.includes('BTC'); + } + featureEnabled(feature) { // If feature is something and it is explicit set to false, hide. if (feature === false) { diff --git a/src/Blockcore.Explorer/Properties/launchSettings.json b/src/Blockcore.Explorer/Properties/launchSettings.json index 3c7c144..00d19e4 100644 --- a/src/Blockcore.Explorer/Properties/launchSettings.json +++ b/src/Blockcore.Explorer/Properties/launchSettings.json @@ -17,6 +17,15 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, + "TBTC": { + "commandName": "Project", + "commandLineArgs": "--chain=TBTC", + "launchBrowser": true, + "applicationUrl": "http://localhost:9911", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, "CITY": { "commandName": "Project", "commandLineArgs": "--chain=CITY",