Merge branch 'spencer/metadata-history' into 'master'
Add metadata history datatable. See merge request grassrootseconomics/cic-staff-client!44
This commit is contained in:
commit
a4813152ed
29002
package-lock.json
generated
29002
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -74,6 +74,7 @@
|
|||||||
"karma-jasmine-html-reporter": "^1.5.0",
|
"karma-jasmine-html-reporter": "^1.5.0",
|
||||||
"karma-junit-reporter": "^2.0.1",
|
"karma-junit-reporter": "^2.0.1",
|
||||||
"lint-staged": "^11.0.0",
|
"lint-staged": "^11.0.0",
|
||||||
|
"openpgp": "^4.10.10",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"pretty-quick": "^3.1.0",
|
"pretty-quick": "^3.1.0",
|
||||||
"protractor": "~7.0.0",
|
"protractor": "~7.0.0",
|
||||||
|
@ -10,3 +10,4 @@ export * from '@app/_helpers/read-csv';
|
|||||||
export * from '@app/_helpers/schema-validation';
|
export * from '@app/_helpers/schema-validation';
|
||||||
export * from '@app/_helpers/sync';
|
export * from '@app/_helpers/sync';
|
||||||
export * from '@app/_helpers/online-status';
|
export * from '@app/_helpers/online-status';
|
||||||
|
export * from './to-hex';
|
||||||
|
9
src/app/_helpers/to-hex.ts
Normal file
9
src/app/_helpers/to-hex.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
function asciiToHex(str: string): string {
|
||||||
|
const arr = [];
|
||||||
|
for (let n = 0, l = str.length; n < l; n++) {
|
||||||
|
arr.push(Number(str.charCodeAt(n)).toString(16));
|
||||||
|
}
|
||||||
|
return arr.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
export { asciiToHex };
|
@ -13,6 +13,7 @@ import { CICRegistry } from '@cicnet/cic-client';
|
|||||||
import { personValidation, updateSyncable, vcardValidation } from '@app/_helpers';
|
import { personValidation, updateSyncable, vcardValidation } from '@app/_helpers';
|
||||||
import { add0x, strip0x } from '@src/assets/js/ethtx/dist/hex';
|
import { add0x, strip0x } from '@src/assets/js/ethtx/dist/hex';
|
||||||
import { KeystoreService } from '@app/_services/keystore.service';
|
import { KeystoreService } from '@app/_services/keystore.service';
|
||||||
|
import * as Automerge from 'automerge';
|
||||||
const vCard = require('vcard-parser');
|
const vCard = require('vcard-parser');
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -38,6 +39,10 @@ export class UserService {
|
|||||||
private categoriesList: BehaviorSubject<object> = new BehaviorSubject<object>(this.categories);
|
private categoriesList: BehaviorSubject<object> = new BehaviorSubject<object>(this.categories);
|
||||||
categoriesSubject: Observable<object> = this.categoriesList.asObservable();
|
categoriesSubject: Observable<object> = this.categoriesList.asObservable();
|
||||||
|
|
||||||
|
history: Array<any> = [];
|
||||||
|
private historyList: BehaviorSubject<any> = new BehaviorSubject<any>(this.history);
|
||||||
|
historySubject: Observable<Array<any>> = this.historyList.asObservable();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private httpClient: HttpClient,
|
private httpClient: HttpClient,
|
||||||
private loggingService: LoggingService,
|
private loggingService: LoggingService,
|
||||||
@ -243,13 +248,22 @@ export class UserService {
|
|||||||
|
|
||||||
async getAccountByAddress(
|
async getAccountByAddress(
|
||||||
accountAddress: string,
|
accountAddress: string,
|
||||||
limit: number = 100
|
limit: number = 100,
|
||||||
|
history: boolean = false
|
||||||
): Promise<Observable<AccountDetails>> {
|
): Promise<Observable<AccountDetails>> {
|
||||||
const accountSubject: Subject<any> = new Subject<any>();
|
const accountSubject: Subject<any> = new Subject<any>();
|
||||||
this.getAccountDetailsFromMeta(await User.toKey(add0x(accountAddress)))
|
this.getAccountDetailsFromMeta(await User.toKey(add0x(accountAddress)))
|
||||||
.pipe(first())
|
.pipe(first())
|
||||||
.subscribe(async (res) => {
|
.subscribe(async (res) => {
|
||||||
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
||||||
|
if (history) {
|
||||||
|
try {
|
||||||
|
// @ts-ignore
|
||||||
|
this.historyList.next(Automerge.getHistory(account.m).reverse());
|
||||||
|
} catch (error) {
|
||||||
|
this.loggingService.sendErrorLevelMessage('No history found', this, { error });
|
||||||
|
}
|
||||||
|
}
|
||||||
const accountInfo = account.m.data;
|
const accountInfo = account.m.data;
|
||||||
await personValidation(accountInfo);
|
await personValidation(accountInfo);
|
||||||
this.tokenService.load.subscribe(async (status: boolean) => {
|
this.tokenService.load.subscribe(async (status: boolean) => {
|
||||||
|
@ -44,6 +44,23 @@ export class AppComponent implements OnInit {
|
|||||||
await this.tokenService.init();
|
await this.tokenService.init();
|
||||||
await this.userService.init();
|
await this.userService.init();
|
||||||
await this.transactionService.init();
|
await this.transactionService.init();
|
||||||
|
try {
|
||||||
|
const publicKeys = await this.authService.getPublicKeys();
|
||||||
|
await this.authService.mutableKeyStore.importPublicKey(publicKeys);
|
||||||
|
this.authService.getTrustedUsers();
|
||||||
|
} catch (error) {
|
||||||
|
this.errorDialogService.openDialog({
|
||||||
|
message: 'Trusted keys endpoint cannot be reached. Please try again later.',
|
||||||
|
});
|
||||||
|
// TODO do something to halt user progress...show a sad cicada page 🦗?
|
||||||
|
}
|
||||||
|
if (!this.swUpdate.isEnabled) {
|
||||||
|
this.swUpdate.available.subscribe(() => {
|
||||||
|
if (confirm('New Version available. Load New Version?')) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
await this.router.events
|
await this.router.events
|
||||||
.pipe(filter((e) => e instanceof NavigationEnd))
|
.pipe(filter((e) => e instanceof NavigationEnd))
|
||||||
.forEach(async (routeInfo) => {
|
.forEach(async (routeInfo) => {
|
||||||
@ -69,23 +86,6 @@ export class AppComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
try {
|
|
||||||
const publicKeys = await this.authService.getPublicKeys();
|
|
||||||
await this.authService.mutableKeyStore.importPublicKey(publicKeys);
|
|
||||||
this.authService.getTrustedUsers();
|
|
||||||
} catch (error) {
|
|
||||||
this.errorDialogService.openDialog({
|
|
||||||
message: 'Trusted keys endpoint cannot be reached. Please try again later.',
|
|
||||||
});
|
|
||||||
// TODO do something to halt user progress...show a sad cicada page 🦗?
|
|
||||||
}
|
|
||||||
if (!this.swUpdate.isEnabled) {
|
|
||||||
this.swUpdate.available.subscribe(() => {
|
|
||||||
if (confirm('New Version available. Load New Version?')) {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load resize
|
// Load resize
|
||||||
|
@ -333,7 +333,88 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<mat-tab-group *ngIf="account" dynamicHeight mat-align-tabs="start">
|
<div class="card mt-1">
|
||||||
|
<app-account-history
|
||||||
|
*ngIf="history"
|
||||||
|
[account]="history?.snapshot.data"
|
||||||
|
(closeWindow)="history = $event"
|
||||||
|
></app-account-history>
|
||||||
|
<mat-card-title class="card-header"> HISTORY </mat-card-title>
|
||||||
|
<div class="card-body">
|
||||||
|
<div *ngIf="historyLoading">
|
||||||
|
<h2 class="text-center"><strong>Loading History!</strong></h2>
|
||||||
|
<mat-progress-bar [mode]="'query'"></mat-progress-bar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-table
|
||||||
|
class="mat-elevation-z10"
|
||||||
|
[dataSource]="historyDataSource"
|
||||||
|
matSort
|
||||||
|
matSortActive="timestamp"
|
||||||
|
#HistoryTableSort="matSort"
|
||||||
|
matSortDirection="asc"
|
||||||
|
matSortDisableClear
|
||||||
|
>
|
||||||
|
<ng-container matColumnDef="actor">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Actor</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let history">
|
||||||
|
{{ history?.change.actor }}
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="signer">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Signer</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let history">
|
||||||
|
{{ history?.snapshot.signature?.data | signatureUser | async }}
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="message">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Message</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let history">
|
||||||
|
{{ history?.change.message }}
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="sequence">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Sequence</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let history">
|
||||||
|
{{ history?.change.seq }}
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="dependencies">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>Dependencies</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let history">
|
||||||
|
{{ getKeyValue(history?.change.deps) }}
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<ng-container matColumnDef="timestamp">
|
||||||
|
<mat-header-cell *matHeaderCellDef mat-sort-header>ChangedAt</mat-header-cell>
|
||||||
|
<mat-cell *matCellDef="let history">
|
||||||
|
{{ history?.snapshot.timestamp | unixDate }}
|
||||||
|
</mat-cell>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<mat-header-row *matHeaderRowDef="historyDisplayedColumns"></mat-header-row>
|
||||||
|
<mat-row
|
||||||
|
*matRowDef="let history; columns: historyDisplayedColumns"
|
||||||
|
(click)="viewHistory(history)"
|
||||||
|
matRipple
|
||||||
|
></mat-row>
|
||||||
|
</mat-table>
|
||||||
|
|
||||||
|
<mat-paginator
|
||||||
|
#HistoryTablePaginator="matPaginator"
|
||||||
|
[pageSize]="historyDefaultPageSize"
|
||||||
|
[pageSizeOptions]="historyPageSizeOptions"
|
||||||
|
showFirstLastButtons
|
||||||
|
></mat-paginator>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-tab-group dynamicHeight mat-align-tabs="start">
|
||||||
<mat-tab label="Transactions">
|
<mat-tab label="Transactions">
|
||||||
<app-transaction-details
|
<app-transaction-details
|
||||||
[transaction]="transaction"
|
[transaction]="transaction"
|
||||||
|
@ -47,6 +47,20 @@ export class AccountDetailsComponent implements OnInit, AfterViewInit {
|
|||||||
@ViewChild('UserTablePaginator', { static: true }) userTablePaginator: MatPaginator;
|
@ViewChild('UserTablePaginator', { static: true }) userTablePaginator: MatPaginator;
|
||||||
@ViewChild('UserTableSort', { static: true }) userTableSort: MatSort;
|
@ViewChild('UserTableSort', { static: true }) userTableSort: MatSort;
|
||||||
|
|
||||||
|
historyDataSource: MatTableDataSource<any>;
|
||||||
|
historyDisplayedColumns: Array<string> = [
|
||||||
|
'actor',
|
||||||
|
'signer',
|
||||||
|
'message',
|
||||||
|
'sequence',
|
||||||
|
'dependencies',
|
||||||
|
'timestamp',
|
||||||
|
];
|
||||||
|
historyDefaultPageSize: number = 10;
|
||||||
|
historyPageSizeOptions: Array<number> = [10, 20, 50, 100];
|
||||||
|
@ViewChild('HistoryTablePaginator', { static: true }) historyTablePaginator: MatPaginator;
|
||||||
|
@ViewChild('HistoryTableSort', { static: true }) historyTableSort: MatSort;
|
||||||
|
|
||||||
accountInfoForm: FormGroup;
|
accountInfoForm: FormGroup;
|
||||||
account: AccountDetails;
|
account: AccountDetails;
|
||||||
accountAddress: string;
|
accountAddress: string;
|
||||||
@ -71,6 +85,9 @@ export class AccountDetailsComponent implements OnInit, AfterViewInit {
|
|||||||
areaType: string;
|
areaType: string;
|
||||||
accountsLoading: boolean = true;
|
accountsLoading: boolean = true;
|
||||||
transactionsLoading: boolean = true;
|
transactionsLoading: boolean = true;
|
||||||
|
histories: Array<any> = [];
|
||||||
|
history: any;
|
||||||
|
historyLoading: boolean = true;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
@ -109,7 +126,7 @@ export class AccountDetailsComponent implements OnInit, AfterViewInit {
|
|||||||
this.transactionService.resetTransactionsList();
|
this.transactionService.resetTransactionsList();
|
||||||
await this.blockSyncService.blockSync(this.accountAddress);
|
await this.blockSyncService.blockSync(this.accountAddress);
|
||||||
this.userService.resetAccountsList();
|
this.userService.resetAccountsList();
|
||||||
(await this.userService.getAccountByAddress(this.accountAddress, 100)).subscribe(
|
(await this.userService.getAccountByAddress(this.accountAddress, 100, true)).subscribe(
|
||||||
async (res) => {
|
async (res) => {
|
||||||
if (res !== undefined) {
|
if (res !== undefined) {
|
||||||
this.account = res;
|
this.account = res;
|
||||||
@ -173,6 +190,18 @@ export class AccountDetailsComponent implements OnInit, AfterViewInit {
|
|||||||
}
|
}
|
||||||
this.cdr.detectChanges();
|
this.cdr.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.userService.historySubject.subscribe(async (histories) => {
|
||||||
|
this.historyDataSource = new MatTableDataSource<any>(histories);
|
||||||
|
this.historyDataSource.paginator = this.historyTablePaginator;
|
||||||
|
this.historyDataSource.sort = this.historyTableSort;
|
||||||
|
this.histories = histories;
|
||||||
|
if (histories.length > 0) {
|
||||||
|
this.historyLoading = false;
|
||||||
|
}
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
this.userService.getCategories();
|
this.userService.getCategories();
|
||||||
this.userService.categoriesSubject.subscribe((res) => {
|
this.userService.categoriesSubject.subscribe((res) => {
|
||||||
this.categories = Object.keys(res);
|
this.categories = Object.keys(res);
|
||||||
@ -213,6 +242,10 @@ export class AccountDetailsComponent implements OnInit, AfterViewInit {
|
|||||||
this.transactionsDataSource.paginator = this.transactionTablePaginator;
|
this.transactionsDataSource.paginator = this.transactionTablePaginator;
|
||||||
this.transactionsDataSource.sort = this.transactionTableSort;
|
this.transactionsDataSource.sort = this.transactionTableSort;
|
||||||
}
|
}
|
||||||
|
if (this.historyDataSource) {
|
||||||
|
this.historyDataSource.paginator = this.historyTablePaginator;
|
||||||
|
this.historyDataSource.sort = this.historyTableSort;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doTransactionFilter(value: string): void {
|
doTransactionFilter(value: string): void {
|
||||||
@ -227,6 +260,10 @@ export class AccountDetailsComponent implements OnInit, AfterViewInit {
|
|||||||
this.transaction = transaction;
|
this.transaction = transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewHistory(history): void {
|
||||||
|
this.history = history;
|
||||||
|
}
|
||||||
|
|
||||||
viewAccount(account): void {
|
viewAccount(account): void {
|
||||||
this.router.navigateByUrl(
|
this.router.navigateByUrl(
|
||||||
`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`
|
`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`
|
||||||
@ -308,4 +345,14 @@ export class AccountDetailsComponent implements OnInit, AfterViewInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getKeyValue(obj: any): string {
|
||||||
|
let str = '';
|
||||||
|
if (obj instanceof Object) {
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
str += `${key}: ${value} `;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
<div *ngIf="account" class="mb-3 mt-1">
|
||||||
|
<div class="card text-center">
|
||||||
|
<mat-card-title class="card-header">
|
||||||
|
<div class="row">
|
||||||
|
ACCOUNT DETAILS
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary ml-auto mr-2"
|
||||||
|
(click)="close()"
|
||||||
|
>
|
||||||
|
CLOSE
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</mat-card-title>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Name: {{ account?.vcard?.fn[0].value }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Phone Number: {{ account?.vcard?.tel[0].value }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Account Type: {{ account?.type }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Gender: {{ account?.gender }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Age: {{ account?.age }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<ul class="list-group list-group-flush">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Bio: {{ account?.products }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Business Category: {{ account?.category }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>User Location: {{ account?.location?.area_name }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Location: {{ account?.location?.area }}</span>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>Location Type: {{ account?.location?.area_type }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,24 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AccountHistoryComponent } from './account-history.component';
|
||||||
|
|
||||||
|
describe('AccountHistoryComponent', () => {
|
||||||
|
let component: AccountHistoryComponent;
|
||||||
|
let fixture: ComponentFixture<AccountHistoryComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [AccountHistoryComponent],
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(AccountHistoryComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
SimpleChanges,
|
||||||
|
OnChanges,
|
||||||
|
} from '@angular/core';
|
||||||
|
const vCard = require('vcard-parser');
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-account-history',
|
||||||
|
templateUrl: './account-history.component.html',
|
||||||
|
styleUrls: ['./account-history.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class AccountHistoryComponent implements OnInit, OnChanges {
|
||||||
|
@Input() account;
|
||||||
|
|
||||||
|
@Output() closeWindow: EventEmitter<any> = new EventEmitter<any>();
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (this.account) {
|
||||||
|
this.account.vcard = vCard.parse(atob(this.account.vcard));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
this.account = null;
|
||||||
|
this.closeWindow.emit(this.account);
|
||||||
|
}
|
||||||
|
}
|
@ -24,6 +24,7 @@ import { ReactiveFormsModule } from '@angular/forms';
|
|||||||
import { AccountSearchComponent } from './account-search/account-search.component';
|
import { AccountSearchComponent } from './account-search/account-search.component';
|
||||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
import { AccountHistoryComponent } from './account-history/account-history.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -31,7 +32,9 @@ import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|||||||
AccountDetailsComponent,
|
AccountDetailsComponent,
|
||||||
CreateAccountComponent,
|
CreateAccountComponent,
|
||||||
AccountSearchComponent,
|
AccountSearchComponent,
|
||||||
|
AccountHistoryComponent,
|
||||||
],
|
],
|
||||||
|
exports: [AccountHistoryComponent],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
AccountsRoutingModule,
|
AccountsRoutingModule,
|
||||||
|
8
src/app/shared/_pipes/signature-user.pipe.spec.ts
Normal file
8
src/app/shared/_pipes/signature-user.pipe.spec.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { SignatureUserPipe } from './signature-user.pipe';
|
||||||
|
|
||||||
|
describe('SignatureUserPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new SignatureUserPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
20
src/app/shared/_pipes/signature-user.pipe.ts
Normal file
20
src/app/shared/_pipes/signature-user.pipe.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import * as openpgp from 'openpgp';
|
||||||
|
import { asciiToHex } from '@app/_helpers';
|
||||||
|
import { KeystoreService } from '@app/_services';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'signatureUser',
|
||||||
|
})
|
||||||
|
export class SignatureUserPipe implements PipeTransform {
|
||||||
|
async transform(armoredSignature: string, ...args: unknown[]): Promise<string> {
|
||||||
|
const keystore = await KeystoreService.getKeystore();
|
||||||
|
const signature = await openpgp.signature.readArmored(armoredSignature);
|
||||||
|
const keyId = asciiToHex(signature.packets[0].issuerKeyId.bytes);
|
||||||
|
const pubKey = keystore.getPublicKeyForId(keyId);
|
||||||
|
if (pubKey) {
|
||||||
|
return pubKey.users[0].userId.userid;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import { MatDialogModule } from '@angular/material/dialog';
|
|||||||
import { SafePipe } from '@app/shared/_pipes/safe.pipe';
|
import { SafePipe } from '@app/shared/_pipes/safe.pipe';
|
||||||
import { NetworkStatusComponent } from './network-status/network-status.component';
|
import { NetworkStatusComponent } from './network-status/network-status.component';
|
||||||
import { UnixDatePipe } from './_pipes/unix-date.pipe';
|
import { UnixDatePipe } from './_pipes/unix-date.pipe';
|
||||||
|
import { SignatureUserPipe } from './_pipes/signature-user.pipe';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -26,6 +27,7 @@ import { UnixDatePipe } from './_pipes/unix-date.pipe';
|
|||||||
SafePipe,
|
SafePipe,
|
||||||
NetworkStatusComponent,
|
NetworkStatusComponent,
|
||||||
UnixDatePipe,
|
UnixDatePipe,
|
||||||
|
SignatureUserPipe,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
TopbarComponent,
|
TopbarComponent,
|
||||||
@ -36,6 +38,7 @@ import { UnixDatePipe } from './_pipes/unix-date.pipe';
|
|||||||
SafePipe,
|
SafePipe,
|
||||||
NetworkStatusComponent,
|
NetworkStatusComponent,
|
||||||
UnixDatePipe,
|
UnixDatePipe,
|
||||||
|
SignatureUserPipe,
|
||||||
],
|
],
|
||||||
imports: [CommonModule, RouterModule, MatIconModule, MatDialogModule],
|
imports: [CommonModule, RouterModule, MatIconModule, MatDialogModule],
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user