mirror of
https://github.com/ringtools/ringtools-web-v2.git
synced 2024-11-19 07:40:01 +01:00
Reorg and implemented file service
This commit is contained in:
parent
ab6d8617ac
commit
104de4ba40
@ -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",
|
||||
|
@ -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(
|
||||
|
@ -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 }),
|
||||
|
37
src/app/components/components.module.ts
Normal file
37
src/app/components/components.module.ts
Normal 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 { }
|
@ -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>
|
@ -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) {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,6 @@ export class RingSetting {
|
||||
ringName!: string;
|
||||
cleanRingName!: string;
|
||||
ringParticipants!: NodeOwner[];
|
||||
ringSize?: number;
|
||||
ringSize!: number;
|
||||
ringLeader?: NodeOwner;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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}) => {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
16
src/app/services/ring-data.service.spec.ts
Normal file
16
src/app/services/ring-data.service.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
80
src/app/services/ring-data.service.ts
Normal file
80
src/app/services/ring-data.service.ts
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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) {}
|
||||
|
||||
|
@ -12,6 +12,7 @@ import { BrowserModule } from '@angular/platform-browser';
|
||||
ToastComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
NgbToastModule,
|
||||
CommonModule
|
||||
],
|
||||
|
10
yarn.lock
10
yarn.lock
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user