Skip to content

Commit

Permalink
Add a basic registry viewer for Blockchain Social Network (BSN)
Browse files Browse the repository at this point in the history
  • Loading branch information
sondreb committed Jun 15, 2024
1 parent 00795b1 commit 1782fd8
Show file tree
Hide file tree
Showing 9 changed files with 348 additions and 14 deletions.
9 changes: 9 additions & 0 deletions app/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ export const routes: Routes = [
title: 'Registries',
data: { icon: 'folder_shared' }
},
{
path: 'registries/:id',
loadComponent: () =>
import('./registries//registry/registry.component').then(
(c) => c.RegistryComponent
),
title: 'Registry',
data: { icon: 'folder_shared' }
},
{
path: 'data',
loadComponent: () =>
Expand Down
111 changes: 111 additions & 0 deletions app/src/app/registries/registries-datasource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { DataSource } from '@angular/cdk/collections';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { map } from 'rxjs/operators';
import { Observable, of as observableOf, merge } from 'rxjs';

// TODO: Replace this with your own data model type
export interface TableItem {
name: string;
id: number;
}

// TODO: replace this with real data from your application
const EXAMPLE_DATA: TableItem[] = [
{id: 1, name: 'Hydrogen'},
{id: 2, name: 'Helium'},
{id: 3, name: 'Lithium'},
{id: 4, name: 'Beryllium'},
{id: 5, name: 'Boron'},
{id: 6, name: 'Carbon'},
{id: 7, name: 'Nitrogen'},
{id: 8, name: 'Oxygen'},
{id: 9, name: 'Fluorine'},
{id: 10, name: 'Neon'},
{id: 11, name: 'Sodium'},
{id: 12, name: 'Magnesium'},
{id: 13, name: 'Aluminum'},
{id: 14, name: 'Silicon'},
{id: 15, name: 'Phosphorus'},
{id: 16, name: 'Sulfur'},
{id: 17, name: 'Chlorine'},
{id: 18, name: 'Argon'},
{id: 19, name: 'Potassium'},
{id: 20, name: 'Calcium'},
];

/**
* Data source for the Table view. This class should
* encapsulate all logic for fetching and manipulating the displayed data
* (including sorting, pagination, and filtering).
*/
export class TableDataSource extends DataSource<TableItem> {
data: TableItem[] = EXAMPLE_DATA;
paginator: MatPaginator | undefined;
sort: MatSort | undefined;

constructor() {
super();
}

/**
* Connect this data source to the table. The table will only update when
* the returned stream emits new items.
* @returns A stream of the items to be rendered.
*/
connect(): Observable<TableItem[]> {
if (this.paginator && this.sort) {
// Combine everything that affects the rendered data into one update
// stream for the data-table to consume.
return merge(observableOf(this.data), this.paginator.page, this.sort.sortChange)
.pipe(map(() => {
return this.getPagedData(this.getSortedData([...this.data ]));
}));
} else {
throw Error('Please set the paginator and sort on the data source before connecting.');
}
}

/**
* Called when the table is being destroyed. Use this function, to clean up
* any open connections or free any held resources that were set up during connect.
*/
disconnect(): void {}

/**
* Paginate the data (client-side). If you're using server-side pagination,
* this would be replaced by requesting the appropriate data from the server.
*/
private getPagedData(data: TableItem[]): TableItem[] {
if (this.paginator) {
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
return data.splice(startIndex, this.paginator.pageSize);
} else {
return data;
}
}

/**
* Sort the data (client-side). If you're using server-side sorting,
* this would be replaced by requesting the appropriate data from the server.
*/
private getSortedData(data: TableItem[]): TableItem[] {
if (!this.sort || !this.sort.active || this.sort.direction === '') {
return data;
}

return data.sort((a, b) => {
const isAsc = this.sort?.direction === 'asc';
switch (this.sort?.active) {
case 'name': return compare(a.name, b.name, isAsc);
case 'id': return compare(+a.id, +b.id, isAsc);
default: return 0;
}
});
}
}

/** Simple sort comparator for example ID/Name columns (for client-side sorting). */
function compare(a: string | number, b: string | number, isAsc: boolean): number {
return (a < b ? -1 : 1) * (isAsc ? 1 : -1);
}
53 changes: 52 additions & 1 deletion app/src/app/registries/registries.component.html
Original file line number Diff line number Diff line change
@@ -1 +1,52 @@
<p>registries works!</p>
<h1>Registries</h1>

<h3>Highlighted</h3>

<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>Blockchain Social Network (BSN)</mat-card-title>
<mat-card-subtitle>Stellar blockchain data registry</mat-card-subtitle>
</mat-card-header>
<mat-card-actions>
<button (click)="openRegistry('bsn')" mat-button>Open</button>
</mat-card-actions>
</mat-card>
<br>
<mat-card appearance="outlined">
<mat-card-header>
<mat-card-title>Liberstad CC</mat-card-title>
<mat-card-subtitle>Crypto Company Registry</mat-card-subtitle>
</mat-card-header>
<mat-card-actions>
<button mat-button>Open</button>
</mat-card-actions>
</mat-card>

<h3>All registries</h3>

<div class="mat-elevation-z8">
<table mat-table class="full-width-table" matSort aria-label="Elements">
<!-- Id Column -->
<ng-container matColumnDef="id">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Id</th>
<td mat-cell *matCellDef="let row">{{row.id}}</td>
</ng-container>

<!-- Name Column -->
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
<td mat-cell *matCellDef="let row">{{row.name}}</td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

<mat-paginator #paginator
[length]="dataSource.data.length"
[pageIndex]="0"
[pageSize]="10"
[pageSizeOptions]="[5, 10, 20]"
aria-label="Select page">
</mat-paginator>
</div>
3 changes: 3 additions & 0 deletions app/src/app/registries/registries.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.full-width-table {
width: 100%;
}
18 changes: 10 additions & 8 deletions app/src/app/registries/registries.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';

import { RegistriesComponent } from './registries.component';

describe('RegistriesComponent', () => {
describe('TableComponent', () => {
let component: RegistriesComponent;
let fixture: ComponentFixture<RegistriesComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [RegistriesComponent]
})
.compileComponents();
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [NoopAnimationsModule]
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(RegistriesComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
it('should compile', () => {
expect(component).toBeTruthy();
});
});
37 changes: 32 additions & 5 deletions app/src/app/registries/registries.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,39 @@
import { Component } from '@angular/core';
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatTableModule, MatTable } from '@angular/material/table';
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
import { MatSortModule, MatSort } from '@angular/material/sort';
import { TableDataSource, TableItem } from './registries-datasource';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { Router } from '@angular/router';

@Component({
selector: 'app-registries',
standalone: true,
imports: [],
templateUrl: './registries.component.html',
styleUrl: './registries.component.scss'
styleUrl: './registries.component.scss',
standalone: true,
imports: [MatButtonModule, MatCardModule, MatTableModule, MatPaginatorModule, MatSortModule]
})
export class RegistriesComponent {
export class RegistriesComponent implements AfterViewInit {
@ViewChild(MatPaginator) paginator!: MatPaginator;
@ViewChild(MatSort) sort!: MatSort;
@ViewChild(MatTable) table!: MatTable<TableItem>;
dataSource = new TableDataSource();

/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
displayedColumns = ['id', 'name'];

constructor(private router: Router) {

}

ngAfterViewInit(): void {
this.dataSource.sort = this.sort;
this.dataSource.paginator = this.paginator;
this.table.dataSource = this.dataSource;
}

openRegistry(registry: string) {
this.router.navigate(['registries', registry]);
}
}
38 changes: 38 additions & 0 deletions app/src/app/registries/registry/registry.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<h1>Registry: Blockchain Social Network (BSN)</h1>

<p>
Updated: {{updated | date: 'medium'}}
</p>


<mat-list>
<div mat-subheader>Tokens</div>
@for (token of tokens; track token) {
<mat-list-item>
<mat-icon matListItemIcon>folder</mat-icon>
<div matListItemTitle>{{token.name}}</div>
<div matListItemLine>{{token.short}}</div>
</mat-list-item>
}
<mat-divider></mat-divider>
<div mat-subheader>Sources</div>
@for (source of sources; track source) {
<mat-list-item>
<mat-icon matListItemIcon>note</mat-icon>
<div matListItemTitle>{{source.name}}</div>
<div matListItemLine>{{source.short}}</div>
</mat-list-item>
}
<div mat-subheader>Accounts</div>
@for (account of accounts; track account) {
<mat-list-item>
<mat-icon matListItemIcon>note</mat-icon>
<div matListItemTitle>{{account.short}}</div>
<div matListItemLine>
@for (tag of account.tags; track tag) {
{{tag.id}},
}
</div>
</mat-list-item>
}
</mat-list>
Empty file.
93 changes: 93 additions & 0 deletions app/src/app/registries/registry/registry.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { AfterViewInit, Component, ViewChild } from '@angular/core';
import { MatTableModule, MatTable } from '@angular/material/table';
import { MatPaginatorModule, MatPaginator } from '@angular/material/paginator';
import { MatSortModule, MatSort } from '@angular/material/sort';
import { MatCardModule } from '@angular/material/card';
import { MatButtonModule } from '@angular/material/button';
import { Router } from '@angular/router';
import { MatListModule } from '@angular/material/list';
import { MatIconModule } from '@angular/material/icon';
import { MatDividerModule } from '@angular/material/divider';
import { DatePipe } from '@angular/common';
import { MatTooltipModule } from '@angular/material/tooltip';

export interface Section {
name: string;
updated: Date;
}

@Component({
selector: 'app-registry',
templateUrl: './registry.component.html',
styleUrl: './registry.component.scss',
standalone: true,
imports: [MatButtonModule,
MatListModule,
MatIconModule,
DatePipe,
MatDividerModule,
MatCardModule, MatTableModule,
MatTooltipModule,
MatPaginatorModule, MatSortModule]
})
export class RegistryComponent implements AfterViewInit {

tokens: any[] = [

];

sources: any[] = [

];

accounts: any[] = [

];


updated: any;

constructor(private router: Router) {

}

ngAfterViewInit(): void {

}

shorten(str: string) {
return str.substring(0, 5) + '...' + str.substring(str.length - 5);
}

async ngOnInit() {
var request = await fetch("https://bsn.mtla.me/json");

if (request.ok) {
var data = await request.json();

this.updated = data.createDate;

this.tokens = data.knownTokens.map((token: { split: (arg0: string) => [any, any]; }) => {
const [name, id] = token.split("-");
return { id, name, short: this.shorten(id) };
});

this.sources = data.usedSources.map((token: { split: (arg0: string) => [any, any]; }) => {
const [name, id] = token.split("-");
return { id, name, short: this.shorten(id) };
});

this.accounts = Object.entries(data.accounts).map(([id, accountData]: [string, any]) => {
return {
id,
short: this.shorten(id),
balances: accountData.balances,
tags: accountData.tags ? Object.entries(accountData.tags).map(([tag, value]: [string, any]) => {
return { id: tag, value };
})
: []
};
});
}
}
}

0 comments on commit 1782fd8

Please sign in to comment.