Reorg and implemented file service

This commit is contained in:
Djuri Baars 2022-01-17 14:32:14 +01:00
parent ab6d8617ac
commit 104de4ba40
27 changed files with 426 additions and 104 deletions

View File

@ -27,10 +27,12 @@
"@ngrx/store": "^13.0.2",
"@ngrx/store-devtools": "^13.0.2",
"@types/d3": "^7.1.0",
"@types/lz-string": "^1.3.34",
"bootstrap": "^4.6.0",
"bootstrap-icons": "^1.7.2",
"d3": "^7.3.0",
"keycharm": "^0.4.0",
"lz-string": "^1.4.4",
"ng2-dragula": "^2.1.1",
"ngrx-store-localstorage": "^12.0.1",
"rxjs": "7.5.2",

View File

@ -1,5 +1,6 @@
import { createAction, props } from '@ngrx/store';
import { NodeOwner } from '../models/node-owner.model';
import { RingSetting } from '../models/ring-setting.model';
export const loadSettings = createAction(
'[Setting] Load Settings'
@ -10,9 +11,14 @@ export const setRingName = createAction(
(ringName: string) => ({ringName})
);
export const loadRingSetting = createAction(
'[Setting] Load Ring Setting',
(ringSetting: RingSetting) => ({ringSetting})
);
export const setRingSize = createAction(
'[Setting] set Ring Size',
(ringSize: number) => ({ringSize})
(ringSize: number | undefined) => ({ringSize})
);
export const setRingLeader = createAction(

View File

@ -1,46 +1,36 @@
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { environment } from '../environments/environment';
import { reducers, metaReducers } from './reducers';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { AppEffects } from './app.effects';
import { ComponentsModule } from './components/components.module';
import { HomeComponent } from './components/home/home.component';
import { OverviewComponent } from './components/overview/overview.component';
import { VisualComponent } from './components/visual/visual.component';
import { SettingsComponent } from './components/settings/settings.component';
import { VisualComponent } from './components/visual/visual.component';
import { LayoutModule } from './layout/layout.module';
import { SharedModule } from './shared/shared.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ParticipantTableComponent } from './components/participant-table/participant-table.component';
import { ParticipantRingComponent } from './components/participant-ring/participant-ring.component';
import { PartialsModule } from './partials/partials.module';
import { VisModule } from './vis/vis.module';
import { metaReducers, reducers } from './reducers';
import { SharedModule } from './shared/shared.module';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
OverviewComponent,
VisualComponent,
SettingsComponent,
ParticipantTableComponent,
ParticipantRingComponent
],
imports: [
SharedModule,
LayoutModule,
PartialsModule,
ComponentsModule,
BrowserModule,
AppRoutingModule,
FormsModule,
ReactiveFormsModule,
NgbModule,
StoreModule.forRoot({}, {}),
EffectsModule.forRoot([AppEffects]),
StoreDevtoolsModule.instrument({ maxAge: 25, logOnly: environment.production }),

View File

@ -0,0 +1,37 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HomeComponent } from './home/home.component';
import { OverviewComponent } from './overview/overview.component';
import { SettingsComponent } from './settings/settings.component';
import { VisualComponent } from './visual/visual.component';
import { SharedModule } from '../shared/shared.module';
import { PartialsModule } from '../partials/partials.module';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgbButtonsModule } from '@ng-bootstrap/ng-bootstrap';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
declarations: [
HomeComponent,
OverviewComponent,
SettingsComponent,
VisualComponent
],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule,
NgbButtonsModule,
SharedModule,
PartialsModule
],
exports: [
HomeComponent,
OverviewComponent,
SettingsComponent,
VisualComponent
],
})
export class ComponentsModule { }

View File

@ -1,45 +1,50 @@
<div class="container-fluid">
<div class="row">
<div class="col-md-12 col-lg-4" >
<form>
<div class="form-group">
<label for="ringName">Ringname</label>
<input type="text" class="form-control" id="ringName" placeholder="Ring Name" [(ngModel)]="ringName"
name="ringName">
<button (click)="processRingname()" class="btn btn-primary mb-2">Save Ring Name</button>
<button (click)="parseCapacityName()" class="btn btn-secondary mb-2">Parse capacity</button>
<div class="col-md-12 col-lg-4">
<form [formGroup]="ringForm" (submit)="saveRingSettings()">
<div class="form-group row">
<label for="ringName" class="col-sm-2 col-form-label">Ringname</label>
<div class="col-sm-10 input-group">
<input type="text" class="form-control" formControlName="name" id="ringName"
placeholder="Ring Name" name="ringName">
<div class="input-group-append">
<button (click)="parseCapacityName()" class="btn btn-secondary">Parse capacity</button>
</div>
</div>
</div>
<div class="form-group">
<label for="ringName">Ring capacity</label>
<input type="number" class="form-control" id="ringName" placeholder="Ring size" [(ngModel)]="ringSize"
name="ringSize">
<button (click)="setRingSize()" class="btn btn-primary mb-2">Save Ring Size</button>
<div class="form-group row">
<label for="ringName" class="col-sm-2 col-form-label">Ring size</label>
<div class="col-sm-10">
<input type="number" class="form-control" id="ringName" placeholder="Ring size"
formControlName="size" name="ringSize">
</div>
</div>
<div class="form-group">
<label for="pubkeys">Import groupnodes from 🧀 CheeseRobot ₿ <small>(or use <a href="https://t.me/ringtools_bot" target="_blank">@ringtools_bot</a> command <code>/ringurl</code>)</small></label>
<label for="pubkeys">Import groupnodes from 🧀 CheeseRobot ₿ <small>(or use <a
href="https://t.me/ringtools_bot" target="_blank">@ringtools_bot</a> command
<code>/ringurl</code>)</small></label>
<textarea class="form-control" id="pubkeys" rows="3" [(ngModel)]="pubkeysText"
[ngModelOptions]="{standalone: true}" spellcheck="false"></textarea>
<button (click)="processGroupnodes()" class="btn btn-primary mb-2">Import Groupnodes</button>
</div>
<!-- <div class="form-group">
<label for="ringName">LND PubSub server</label>
<input type="text" class="form-control" id="pubsubServer" placeholder="PubSub Server" [(ngModel)]="pubsubServer"
[ngModelOptions]="{standalone: true}">
<button (click)="setPubSubServer()" class="btn btn-primary mb-2">Save server</button>
</div> -->
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="showSrRofLogo" [(ngModel)]="showLogo"
(ngModelChange)="updateShowLogo($event)"
[ngModelOptions]="{standalone: true}">
<label class="form-check-label" for="showSrRofLogo">
Show SR RoF logo
</label>
</div>
<div class="custom-control custom-switch">
<input class="custom-control-input" type="checkbox" value="" id="showSrRofLogo"
[(ngModel)]="showLogo" (ngModelChange)="updateShowLogo($event)"
[ngModelOptions]="{standalone: true}">
<label class="custom-control-label" for="showSrRofLogo">
Show SR RoF logo
</label>
</div>
</div>
</form>
</div>
<div class="col-md-12 col-lg-8">
<form>
<form [formGroup]="nodeForm" (submit)="addNodeOwner()">
<table class="table table-sm table-striped small">
<thead>
<tr>
@ -51,7 +56,7 @@
</tr>
</thead>
<tbody>
<ng-container *ngFor="let item of nodeOwners$ | async">
<ng-container *ngFor="let item of nodeOwners">
<tr>
<td class="service-icon amboss">
<a [attr.href]="'https://amboss.space/node/' + item.pub_key" target="_blank">
@ -68,9 +73,10 @@
</td>
<td>{{ item.nodename }}</td>
<td>{{ item.username_or_name }}</td>
<td><button class="btn btn-danger btn-small btn-sm delete-btn" (click)="removeNodeOwner(item)">
<i class="bi bi-trash"></i>
</button>
<td><button class="btn btn-danger btn-small btn-sm delete-btn"
(click)="removeNodeOwner(item)">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
</ng-container>
@ -83,17 +89,17 @@
<th scope="row" class="no-padding" colspan="1">
<div class="form-check">
<input class="form-check-input position-static" type="text" id="pubkey"
[(ngModel)]="addPubKey" name="pubKey" placeholder="Public Key">
formControlName="pubKey" name="pubKey" placeholder="Public Key">
</div>
</th>
<td>{{ tempNodename }}</td>
<td class="no-padding">
<div class="form-check">
<input class="form-check-input position-static" type="text" id="TG username"
[(ngModel)]="addTgUsername" name="tgUserName" placeholder="TG username">
formControlName="tgUsername" name="tgUsername" placeholder="TG username">
</div>
</td>
<td><button class="btn btn-primary btn-small btn-sm" (click)="addNodeOwner()">Add</button>
<td><button class="btn btn-primary btn-small btn-sm">Add</button>
</td>
</tr>
</tbody>
@ -119,4 +125,4 @@
</ul>
</div>
</div>
</div>
</div>

View File

@ -1,7 +1,14 @@
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { loadNodeOwners } from 'src/app/actions/node-owner.actions';
import { deleteNodeOwner, loadNodeOwners } from 'src/app/actions/node-owner.actions';
import {
loadRingSetting,
setRingName,
setRingSize,
} from 'src/app/actions/setting.actions';
import { NodeOwner } from 'src/app/models/node-owner.model';
import { RingSetting } from 'src/app/models/ring-setting.model';
import { SettingState } from 'src/app/reducers/setting.reducer';
@ -12,77 +19,197 @@ import { FileService } from 'src/app/services/file.service';
import { NotificationService } from 'src/app/services/notification.service';
import { environment } from 'src/environments/environment';
import * as fromRoot from '../../reducers';
import * as LZString from 'lz-string';
import { data } from 'vis-network';
import { deleteRingSetting, upsertRingSetting } from 'src/app/actions/ring-setting.actions';
import { nodeOwnersReducer } from 'src/app/reducers/node-owner.reducer';
import { RingDataService } from 'src/app/services/ring-data.service';
@Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss'],
})
export class SettingsComponent {
export class SettingsComponent implements OnInit {
ringSettings$ = new Observable<RingSetting[]>();
ringSettings: RingSetting[] = [];
nodeOwners$ = new Observable<any>();
nodeOwners$ = new Observable<NodeOwner[]>();
nodeOwners: NodeOwner[] = [];
shareUrl: string = '';
showLogo: boolean = true;
settings!: SettingState;
// TODO: improve
addPubKey: any;
addTgUsername: any;
tempNodename = '';
pubkeysText: any = '';
ringName: any = '';
ringSize!: number;
ringForm = new FormGroup({
name: new FormControl(''),
size: new FormControl(''),
});
nodeForm = new FormGroup({
pubKey: new FormControl(''),
tgUsername: new FormControl(''),
});
pubkeysText: string = '';
tempNodename: string = '';
constructor(
private file: FileService,
private notification: NotificationService,
private route: ActivatedRoute,
private ringData: RingDataService,
private store: Store<fromRoot.State>
) {
) {
this.store.select(selectSettings).subscribe((settings: SettingState) => {
this.settings = settings;
});
this.ringSettings$ = this.store.select(selectRingSettings);
this.nodeOwners$ = this.store.select(selectNodeOwners);
this.nodeOwners$.subscribe((nodeOwners: NodeOwner[]) => {
this.nodeOwners = nodeOwners.map((no) => Object.assign(new NodeOwner, no));
});
}
ngOnInit(): void {
const loadRingNew = this.route.snapshot.queryParamMap.get('l');
if (loadRingNew) {
this.parseLoadQueryString(loadRingNew);
}
}
parseLoadQueryString(loadRing: string) {
const decodedData = LZString.decompressFromEncodedURIComponent(loadRing);
if (!decodedData) return;
let importData = JSON.parse(decodedData);
let segments = this.file.parseNewExportFormat(importData['data']);
let ringSetting = Object.assign(new RingSetting(), {
cleanRingName: importData['name'],
ringName: importData['name'],
ringSize: importData['size'],
ringParticipants: segments,
});
this.loadSettings(ringSetting);
}
loadSettings(item: RingSetting) {
console.log('load', item);
this.pubkeysText = this.file.convertToCsv(item.ringParticipants);
this.store.dispatch(loadNodeOwners({ nodeOwners: item.ringParticipants }));
this.ringData.loadSettings(item);
this.ringForm.setValue({
name: item.ringName,
size: item.ringSize,
});
this.notification.showSuccess(`Ring load ${item.cleanRingName} successful`);
}
removeSettings(item: any) {}
removeSettings(item: RingSetting) {
this.ringData.removeSettings(item);
addNodeOwner() {}
try {
this.notification.show(`Ring remove ${item.cleanRingName} successful`, {
classname: 'bg-success',
});
} catch (e) {
this.notification.show('Error removing ring', {
classname: 'bg-error',
});
}
}
emojiPrefix(item: any) {
addNodeOwner() {
const pubKey = this.nodeForm.get('pubKey')?.value;
const tgUsername = this.nodeForm.get('tgUsername')?.value;
if (!pubKey || !tgUsername) return;
try {
this.ringData.addNodeOwner(pubKey, tgUsername)
this.notification.show(`Added node ${pubKey}`, {
classname: 'bg-success',
});
} catch {
this.notification.show('Error adding node', {
classname: 'bg-danger',
});
}
}
emojiPrefix(item: RingSetting) {
return `${item.ringName} (${item.ringSize})`;
}
parseCapacityName() {}
parseCapacityName() {
const ringName = this.ringForm.get('name')?.value;
if (!ringName) return;
let capacity: String = ringName.match(/_(\d+[K|M])sats_/)[1];
capacity = capacity.toString().replace('K', '000');
capacity = capacity.toString().replace('M', '000000');
this.ringForm.get('size')?.patchValue(capacity);
}
exportJSON() {}
saveRingSettings() {}
saveRingSettings() {
const ringName = this.ringForm.get('name')?.value;
const ringSize = this.ringForm.get('size')?.value;
processGroupnodes() {}
if (!ringSize || !ringName || !this.nodeOwners.length) {
this.notification.show('Ring name, size or participants missing', {
classname: 'bg-warning',
});
return;
};
removeNodeOwner(item: any) {}
let ringSettings: RingSetting = {
ringName: ringName,
ringParticipants: this.nodeOwners,
cleanRingName: ringName.replace(
/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g,
''
),
id: ringName.replace(
/([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g,
''
),
ringSize: ringSize,
};
this.store.dispatch(upsertRingSetting({ ringSetting: ringSettings }));
}
processGroupnodes() {
const nodeOwners = this.file.parseCsvToType(this.pubkeysText);
this.ringData.setNodeOwners(nodeOwners);
}
removeNodeOwner(nodeOwner: NodeOwner) {
try {
this.store.dispatch(deleteNodeOwner({id: nodeOwner.pub_key }));
this.notification.show(`Removed node ${nodeOwner.nodename}`, {
classname: 'bg-success',
});
} catch (e) {
this.notification.show('Error removing node', {
classname: 'bg-danger',
});
}
}
updateShowLogo(event: any) {}
setRingSize() {}
processRingname() {}
get1MlLink(node: any) {

View File

@ -9,6 +9,8 @@ import { selectSettings } from 'src/app/selectors/setting.selectors';
import { selectRingSettings } from 'src/app/selectors/ring-setting.selectors';
import { RingSettingsState } from 'src/app/reducers/ring-setting.reducer';
import { RingSetting } from 'src/app/models/ring-setting.model';
import { loadNodeOwners } from 'src/app/actions/node-owner.actions';
import { loadRingSetting } from 'src/app/actions/setting.actions';
@Component({
selector: 'app-navigation',
@ -47,8 +49,8 @@ export class NavigationComponent {
return this.settings.ringName;
}
// TODO: Add type
loadSettings(item: any) {
loadSettings(item: RingSetting) {
this.store.dispatch(loadNodeOwners({ nodeOwners: item.ringParticipants }));
this.store.dispatch(loadRingSetting(item));
}
}

View File

@ -1,6 +1,6 @@
export class NodeOwner {
id?: string;
first_name?: string = '';
first_name: string = '';
last_name?: string = '';
pub_key: string = '';
nodename?: string;
@ -13,4 +13,8 @@ export class NodeOwner {
}
return `@${this.username}`;
}
set username_or_name(_username: string) {
}
}

View File

@ -5,6 +5,6 @@ export class RingSetting {
ringName!: string;
cleanRingName!: string;
ringParticipants!: NodeOwner[];
ringSize?: number;
ringSize!: number;
ringLeader?: NodeOwner;
}

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { ExportFile } from 'src/app/models/export_file.enum';
import { FileService } from 'src/app/services/file.service';
import { RingDataService } from 'src/app/services/ring-data.service';
@Component({
selector: 'app-file-exporter',
@ -10,22 +11,23 @@ import { FileService } from 'src/app/services/file.service';
export class FileExporterComponent {
constructor(
private file: FileService
private file: FileService,
private ringData: RingDataService
) { }
persistOrder() {
console.log('Method not implemented');
}
downloadChannelsTxt() {
this.file.generateAndDownload(ExportFile.RingToolsChannelsTxt);
async downloadChannelsTxt() {
this.file.generateAndDownload(ExportFile.RingToolsChannelsTxt, await this.ringData.getRing());
}
downloadPubKeysTxt() {
this.file.generateAndDownload(ExportFile.RingToolsPubKeysTxt);
async downloadPubKeysTxt() {
this.file.generateAndDownload(ExportFile.RingToolsPubKeysTxt, await this.ringData.getRing());
}
downloadIgniterPubkeys() {
this.file.generateAndDownload(ExportFile.IgniterPubkeys);
async downloadIgniterPubkeys() {
this.file.generateAndDownload(ExportFile.IgniterPubkeys, await this.ringData.getRing());
}
}

View File

@ -9,11 +9,15 @@ import { FormsModule } from '@angular/forms';
import { NodeConnectionsComponent } from './node-connections/node-connections.component';
import { VisModule } from '../vis/vis.module';
import { HttpClientModule } from '@angular/common/http';
import { ParticipantRingComponent } from './participant-ring/participant-ring.component';
import { ParticipantTableComponent } from './participant-table/participant-table.component';
@NgModule({
declarations: [
ParticipantRingComponent,
ParticipantTableComponent,
FileExporterComponent,
EditRingOrderComponent,
ReorderParticipantsComponent,
@ -28,6 +32,8 @@ import { HttpClientModule } from '@angular/common/http';
CommonModule
],
exports: [
ParticipantRingComponent,
ParticipantTableComponent,
FileExporterComponent,
EditRingOrderComponent,
ReorderParticipantsComponent,

View File

@ -31,7 +31,7 @@ export const settingReducer = createReducer(
}),
on(SettingActions.setRingSize,
(state: SettingState, {ringSize}) => {
return {...state, ringSize: ringSize }
return {...state, ringSize: Number(ringSize) }
}),
on(SettingActions.setViewMode,
(state: SettingState, {viewMode}) => {

View File

@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { ExportFile } from '../models/export_file.enum';
import { NodeOwner } from '../models/node-owner.model';
import { IRing } from '../models/ring.model';
@Injectable({
providedIn: 'root',
@ -8,7 +9,7 @@ import { NodeOwner } from '../models/node-owner.model';
export class FileService {
constructor() {}
convertToCsv(ringParticipants:NodeOwner[], header: boolean = true) {
convertToCsv(ringParticipants: NodeOwner[], header: boolean = true) {
let pkContents = '';
if (header)
@ -36,9 +37,9 @@ export class FileService {
nodename: parts[1],
pub_key: parts[2],
username: parts[4],
username_or_name: undefined
username_or_name: '',
};
segments.push(nodeOwner);
segments.push(Object.assign(new NodeOwner(), nodeOwner));
}
}
return segments;
@ -62,7 +63,7 @@ export class FileService {
let add = '';
for (let p of ringParticipants) {
pkContents += `${add}${p.first_name},${p.username},${p.pub_key},${p.nodename}`;
add = "|";
add = '|';
}
return pkContents;
@ -74,12 +75,12 @@ export class FileService {
for (let line of segmentLines) {
let parts = line.split(',');
if (parts.length > 1) {
let nodeOwner:NodeOwner = {
let nodeOwner: NodeOwner = {
first_name: parts[0],
username: parts[1],
pub_key: parts[2],
nodename: parts[3],
username_or_name: undefined
username_or_name: '',
};
segments.push(nodeOwner);
}
@ -87,7 +88,32 @@ export class FileService {
return segments;
}
generateAndDownload(file_template: ExportFile) {
generateAndDownload(file_template: ExportFile, ring: IRing) {
let data: string = '';
switch (file_template) {
case ExportFile.RingToolsChannelsTxt:
for (let no of ring) {
data += no[2] + '\r\n';
}
break;
case ExportFile.RingToolsPubKeysTxt:
for (let no of ring) {
data += `${no[0].pub_key},${no[0].username_or_name}\r\n`;
}
break;
case ExportFile.IgniterPubkeys:
data = 'declare pub_keys=(\r\n';
for (let no of ring) {
data += ` ${no[0].pub_key} # ${no[0].username_or_name}\r\n`;
}
data += ')';
break;
default:
break;
}
this.doDownloadFileWithData(data, file_template);
}
}

View File

@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
import { io, Socket } from 'socket.io-client';
import { NodeInfo } from '../models/node-info.model';
import { HttpClient } from '@angular/common/http';
import { firstValueFrom } from 'rxjs';
@Injectable({
providedIn: 'root'
@ -23,4 +24,8 @@ export class LnDataService {
`http://localhost:7464/node/${pubKey}`
);
}
getNodeInfoAsync(pubKey: string) {
return firstValueFrom(this.getNodeInfo(pubKey));
}
}

View File

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { RingDataService } from './ring-data.service';
describe('RingDataService', () => {
let service: RingDataService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(RingDataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

View File

@ -0,0 +1,80 @@
import { Injectable } from '@angular/core';
import * as fromRoot from '../reducers';
import * as LZString from 'lz-string';
import { Store } from '@ngrx/store';
import { RingSetting } from '../models/ring-setting.model';
import { addNodeOwner, loadNodeOwners } from '../actions/node-owner.actions';
import { loadRingSetting } from '../actions/setting.actions';
import { deleteRingSetting } from '../actions/ring-setting.actions';
import { LnDataService } from './ln-data.service';
import { NodeInfo } from '../models/node-info.model';
import { NodeOwner } from '../models/node-owner.model';
import { IRing } from '../models/ring.model';
import { Observable } from 'rxjs';
import { selectNodeOwners } from '../selectors/node-owner.selectors';
@Injectable({
providedIn: 'root'
})
export class RingDataService {
nodeOwners$!: Observable<NodeOwner[]>;
nodeOwners: NodeOwner[] = [];
constructor(
private store: Store<fromRoot.State>,
private lnData: LnDataService
) {
this.nodeOwners$ = this.store.select(selectNodeOwners);
this.nodeOwners$.subscribe((nodeOwners: NodeOwner[]) => {
this.nodeOwners = nodeOwners.map(no => Object.assign(new NodeOwner, no));
});
}
loadSettings(item: RingSetting) {
this.store.dispatch(loadNodeOwners({ nodeOwners: item.ringParticipants }));
this.store.dispatch(loadRingSetting(item));
}
removeSettings(item: RingSetting) {
this.store.dispatch(deleteRingSetting({ id: item.cleanRingName }));
}
setNodeOwners(nodeOwners: any) {
this.store.dispatch(loadNodeOwners({ nodeOwners: nodeOwners }))
}
addNodeOwner(pubKey: string, tgUsername: string) {
this.lnData.getNodeInfo(pubKey).subscribe((nodeInfo: NodeInfo) => {
let no: NodeOwner = {
pub_key: nodeInfo.node.alias,
nodename: nodeInfo.node.alias,
first_name: tgUsername,
username: tgUsername,
username_or_name: ''
};
this.store.dispatch(addNodeOwner({nodeOwner: no }));
})
}
async getRing(): Promise<IRing> {
let ring: IRing = [];
for (const [i, node] of this.nodeOwners.entries()) {
let nextIndex = (i + 1) % this.nodeOwners.length;
const nodeInfo = Object.assign(new NodeInfo, (await this.lnData.getNodeInfoAsync(this.nodeOwners[i].pub_key)));
let channel = nodeInfo.hasChannelWith(this.nodeOwners[nextIndex].pub_key);
ring.push([
Object.assign(new NodeOwner(), this.nodeOwners[i]),
Object.assign(new NodeOwner(), this.nodeOwners[nextIndex]),
channel,
channel ? nodeInfo.getChannelPolicies(this.nodeOwners[nextIndex].pub_key, channel) : undefined
]);
}
return ring;
}
}

View File

@ -1,13 +1,15 @@
import { Component, OnInit, TemplateRef } from '@angular/core';
import { Component, HostBinding, OnInit, TemplateRef } from '@angular/core';
import { Toast } from './toast.model';
import { ToastService } from './toast.service';
@Component({
selector: 'app-toast',
templateUrl: './toast.component.html',
styleUrls: ['./toast.component.scss']
styleUrls: ['./toast.component.scss'],
})
export class ToastComponent {
@HostBinding('class.ngb-toasts')
readonly ngbToasts = true;
constructor(public toastService: ToastService) {}

View File

@ -12,6 +12,7 @@ import { BrowserModule } from '@angular/platform-browser';
ToastComponent
],
imports: [
BrowserModule,
NgbToastModule,
CommonModule
],

View File

@ -1843,6 +1843,11 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
"@types/lz-string@^1.3.34":
version "1.3.34"
resolved "https://registry.yarnpkg.com/@types/lz-string/-/lz-string-1.3.34.tgz#69bfadde419314b4a374bf2c8e58659c035ed0a5"
integrity sha512-j6G1e8DULJx3ONf6NdR5JiR2ZY3K3PaaqiEuKYkLQO0Czfi1AzrtjfnfCROyWGeDd5IVMKCwsgSmMip9OWijow==
"@types/node@*", "@types/node@>=10.0.0", "@types/node@^17.0":
version "17.0.8"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.8.tgz#50d680c8a8a78fe30abe6906453b21ad8ab0ad7b"
@ -5175,6 +5180,11 @@ lru-cache@^6.0.0:
dependencies:
yallist "^4.0.0"
lz-string@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26"
integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=
magic-string@0.25.7, magic-string@^0.25.0:
version "0.25.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"