mirror of
https://github.com/mempool/mempool.git
synced 2025-02-25 07:07:36 +01:00
Merge branch 'nymkappa/subscription-tag' into nymkappa/mega-branch
This commit is contained in:
commit
70ca75ac12
13 changed files with 227 additions and 101 deletions
|
@ -22,6 +22,7 @@ import { FiatCurrencyPipe } from './shared/pipes/fiat-currency.pipe';
|
||||||
import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe';
|
import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe';
|
||||||
import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe';
|
import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe';
|
||||||
import { AppPreloadingStrategy } from './app.preloading-strategy';
|
import { AppPreloadingStrategy } from './app.preloading-strategy';
|
||||||
|
import { ServicesApiServices } from './services/services-api.service';
|
||||||
|
|
||||||
const providers = [
|
const providers = [
|
||||||
ElectrsApiService,
|
ElectrsApiService,
|
||||||
|
@ -40,6 +41,7 @@ const providers = [
|
||||||
FiatCurrencyPipe,
|
FiatCurrencyPipe,
|
||||||
CapAddressPipe,
|
CapAddressPipe,
|
||||||
AppPreloadingStrategy,
|
AppPreloadingStrategy,
|
||||||
|
ServicesApiServices,
|
||||||
{ provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }
|
{ provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { Subscription, catchError, of, tap } from 'rxjs';
|
||||||
import { StorageService } from '../../services/storage.service';
|
import { StorageService } from '../../services/storage.service';
|
||||||
import { Transaction } from '../../interfaces/electrs.interface';
|
import { Transaction } from '../../interfaces/electrs.interface';
|
||||||
import { nextRoundNumber } from '../../shared/common.utils';
|
import { nextRoundNumber } from '../../shared/common.utils';
|
||||||
|
import { ServicesApiServices } from '../../services/services-api.service';
|
||||||
|
|
||||||
export type AccelerationEstimate = {
|
export type AccelerationEstimate = {
|
||||||
txSummary: TxSummary;
|
txSummary: TxSummary;
|
||||||
|
@ -61,7 +62,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||||
maxRateOptions: RateOption[] = [];
|
maxRateOptions: RateOption[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private servicesApiService: ServicesApiServices,
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private cd: ChangeDetectorRef
|
private cd: ChangeDetectorRef
|
||||||
) { }
|
) { }
|
||||||
|
@ -81,7 +82,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.user = this.storageService.getAuth()?.user ?? null;
|
this.user = this.storageService.getAuth()?.user ?? null;
|
||||||
|
|
||||||
this.estimateSubscription = this.apiService.estimate$(this.tx.txid).pipe(
|
this.estimateSubscription = this.servicesApiService.estimate$(this.tx.txid).pipe(
|
||||||
tap((response) => {
|
tap((response) => {
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
this.estimate = undefined;
|
this.estimate = undefined;
|
||||||
|
@ -181,7 +182,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||||
if (this.accelerationSubscription) {
|
if (this.accelerationSubscription) {
|
||||||
this.accelerationSubscription.unsubscribe();
|
this.accelerationSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
this.accelerationSubscription = this.apiService.accelerate$(
|
this.accelerationSubscription = this.servicesApiService.accelerate$(
|
||||||
this.tx.txid,
|
this.tx.txid,
|
||||||
this.userBid
|
this.userBid
|
||||||
).subscribe({
|
).subscribe({
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, LOCALE_ID, OnDestroy, OnInit } from '@angular/core';
|
||||||
import { EChartsOption, graphic } from 'echarts';
|
import { EChartsOption } from 'echarts';
|
||||||
import { Observable, Subscription, combineLatest } from 'rxjs';
|
import { Observable, Subscription, combineLatest } from 'rxjs';
|
||||||
import { map, max, startWith, switchMap, tap } from 'rxjs/operators';
|
import { map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
import { ApiService } from '../../../services/api.service';
|
|
||||||
import { SeoService } from '../../../services/seo.service';
|
import { SeoService } from '../../../services/seo.service';
|
||||||
import { formatNumber } from '@angular/common';
|
import { formatNumber } from '@angular/common';
|
||||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||||
|
@ -11,6 +10,8 @@ import { StorageService } from '../../../services/storage.service';
|
||||||
import { MiningService } from '../../../services/mining.service';
|
import { MiningService } from '../../../services/mining.service';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { Acceleration } from '../../../interfaces/node-api.interface';
|
import { Acceleration } from '../../../interfaces/node-api.interface';
|
||||||
|
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||||
|
import { ApiService } from '../../../services/api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-acceleration-fees-graph',
|
selector: 'app-acceleration-fees-graph',
|
||||||
|
@ -54,6 +55,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||||
@Inject(LOCALE_ID) public locale: string,
|
@Inject(LOCALE_ID) public locale: string,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
|
private servicesApiService: ServicesApiServices,
|
||||||
private formBuilder: UntypedFormBuilder,
|
private formBuilder: UntypedFormBuilder,
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private miningService: MiningService,
|
private miningService: MiningService,
|
||||||
|
@ -73,7 +75,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||||
this.timespan = this.miningWindowPreference;
|
this.timespan = this.miningWindowPreference;
|
||||||
|
|
||||||
this.statsObservable$ = combineLatest([
|
this.statsObservable$ = combineLatest([
|
||||||
(this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })),
|
(this.accelerations$ || this.servicesApiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })),
|
||||||
this.apiService.getHistoricalBlockFees$(this.miningWindowPreference),
|
this.apiService.getHistoricalBlockFees$(this.miningWindowPreference),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
tap(([accelerations, blockFeesResponse]) => {
|
tap(([accelerations, blockFeesResponse]) => {
|
||||||
|
@ -101,7 +103,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
this.storageService.setValue('miningWindowPreference', timespan);
|
this.storageService.setValue('miningWindowPreference', timespan);
|
||||||
this.timespan = timespan;
|
this.timespan = timespan;
|
||||||
return this.apiService.getAccelerationHistory$({});
|
return this.servicesApiService.getAccelerationHistory$({});
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core';
|
import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core';
|
||||||
import { Observable, catchError, of, switchMap, tap } from 'rxjs';
|
import { Observable, catchError, of, switchMap, tap } from 'rxjs';
|
||||||
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
||||||
import { ApiService } from '../../../services/api.service';
|
|
||||||
import { StateService } from '../../../services/state.service';
|
import { StateService } from '../../../services/state.service';
|
||||||
import { WebsocketService } from '../../../services/websocket.service';
|
import { WebsocketService } from '../../../services/websocket.service';
|
||||||
|
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-accelerations-list',
|
selector: 'app-accelerations-list',
|
||||||
|
@ -26,7 +26,7 @@ export class AccelerationsListComponent implements OnInit {
|
||||||
skeletonLines: number[] = [];
|
skeletonLines: number[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private servicesApiService: ServicesApiServices,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
public stateService: StateService,
|
public stateService: StateService,
|
||||||
private cd: ChangeDetectorRef,
|
private cd: ChangeDetectorRef,
|
||||||
|
@ -41,7 +41,7 @@ export class AccelerationsListComponent implements OnInit {
|
||||||
this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()];
|
this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()];
|
||||||
this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5;
|
this.paginationMaxSize = window.matchMedia('(max-width: 670px)').matches ? 3 : 5;
|
||||||
|
|
||||||
const accelerationObservable$ = this.accelerations$ || (this.pending ? this.apiService.getAccelerations$() : this.apiService.getAccelerationHistory$({ timeframe: '1m' }));
|
const accelerationObservable$ = this.accelerations$ || (this.pending ? this.servicesApiService.getAccelerations$() : this.servicesApiService.getAccelerationHistory$({ timeframe: '1m' }));
|
||||||
this.accelerationList$ = accelerationObservable$.pipe(
|
this.accelerationList$ = accelerationObservable$.pipe(
|
||||||
switchMap(accelerations => {
|
switchMap(accelerations => {
|
||||||
if (this.pending) {
|
if (this.pending) {
|
||||||
|
|
|
@ -3,12 +3,12 @@ import { SeoService } from '../../../services/seo.service';
|
||||||
import { WebsocketService } from '../../../services/websocket.service';
|
import { WebsocketService } from '../../../services/websocket.service';
|
||||||
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
||||||
import { StateService } from '../../../services/state.service';
|
import { StateService } from '../../../services/state.service';
|
||||||
import { Observable, Subject, catchError, combineLatest, distinctUntilChanged, interval, map, of, share, startWith, switchMap, tap } from 'rxjs';
|
import { Observable, catchError, combineLatest, distinctUntilChanged, interval, map, of, share, startWith, switchMap, tap } from 'rxjs';
|
||||||
import { ApiService } from '../../../services/api.service';
|
|
||||||
import { Color } from '../../block-overview-graph/sprite-types';
|
import { Color } from '../../block-overview-graph/sprite-types';
|
||||||
import { hexToColor } from '../../block-overview-graph/utils';
|
import { hexToColor } from '../../block-overview-graph/utils';
|
||||||
import TxView from '../../block-overview-graph/tx-view';
|
import TxView from '../../block-overview-graph/tx-view';
|
||||||
import { feeLevels, mempoolFeeColors } from '../../../app.constants';
|
import { feeLevels, mempoolFeeColors } from '../../../app.constants';
|
||||||
|
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||||
|
|
||||||
const acceleratedColor: Color = hexToColor('8F5FF6');
|
const acceleratedColor: Color = hexToColor('8F5FF6');
|
||||||
const normalColors = mempoolFeeColors.map(hex => hexToColor(hex + '5F'));
|
const normalColors = mempoolFeeColors.map(hex => hexToColor(hex + '5F'));
|
||||||
|
@ -33,7 +33,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||||
constructor(
|
constructor(
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private apiService: ApiService,
|
private serviceApiServices: ServicesApiServices,
|
||||||
private stateService: StateService,
|
private stateService: StateService,
|
||||||
) {
|
) {
|
||||||
this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`);
|
this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`);
|
||||||
|
@ -45,7 +45,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||||
this.pendingAccelerations$ = interval(30000).pipe(
|
this.pendingAccelerations$ = interval(30000).pipe(
|
||||||
startWith(true),
|
startWith(true),
|
||||||
switchMap(() => {
|
switchMap(() => {
|
||||||
return this.apiService.getAccelerations$();
|
return this.serviceApiServices.getAccelerations$();
|
||||||
}),
|
}),
|
||||||
catchError((e) => {
|
catchError((e) => {
|
||||||
return of([]);
|
return of([]);
|
||||||
|
@ -56,7 +56,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||||
this.accelerations$ = this.stateService.chainTip$.pipe(
|
this.accelerations$ = this.stateService.chainTip$.pipe(
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged(),
|
||||||
switchMap((chainTip) => {
|
switchMap((chainTip) => {
|
||||||
return this.apiService.getAccelerationHistory$({ timeframe: '1m' });
|
return this.serviceApiServices.getAccelerationHistory$({ timeframe: '1m' });
|
||||||
}),
|
}),
|
||||||
catchError((e) => {
|
catchError((e) => {
|
||||||
return of([]);
|
return of([]);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||||
import { Observable, of } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { switchMap } from 'rxjs/operators';
|
import { switchMap } from 'rxjs/operators';
|
||||||
import { ApiService } from '../../../services/api.service';
|
|
||||||
import { Acceleration } from '../../../interfaces/node-api.interface';
|
import { Acceleration } from '../../../interfaces/node-api.interface';
|
||||||
|
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-pending-stats',
|
selector: 'app-pending-stats',
|
||||||
|
@ -15,11 +15,11 @@ export class PendingStatsComponent implements OnInit {
|
||||||
public accelerationStats$: Observable<any>;
|
public accelerationStats$: Observable<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private servicesApiService: ServicesApiServices,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.accelerationStats$ = (this.accelerations$ || this.apiService.getAccelerations$()).pipe(
|
this.accelerationStats$ = (this.accelerations$ || this.servicesApiService.getAccelerations$()).pipe(
|
||||||
switchMap(accelerations => {
|
switchMap(accelerations => {
|
||||||
let totalAccelerations = 0;
|
let totalAccelerations = 0;
|
||||||
let totalFeeDelta = 0;
|
let totalFeeDelta = 0;
|
||||||
|
|
|
@ -2,8 +2,19 @@
|
||||||
<div class="d-flex menu-click">
|
<div class="d-flex menu-click">
|
||||||
|
|
||||||
<nav class="scrollable menu-click">
|
<nav class="scrollable menu-click">
|
||||||
<span *ngIf="userAuth" class="menu-click">
|
<span *ngIf="user$ | async as user" class="menu-click">
|
||||||
<strong class="menu-click text-nowrap ellipsis">@ {{ userAuth.user.username }}</strong>
|
<span class="menu-click text-nowrap ellipsis">
|
||||||
|
<strong>
|
||||||
|
<span *ngIf="user.username.includes('@'); else usernamenospace">{{ user.username }}</span>
|
||||||
|
<ng-template #usernamenospace>@{{ user.username }}</ng-template>
|
||||||
|
</strong>
|
||||||
|
</span>
|
||||||
|
<span class="badge mr-1 badge-og" *ngIf="user.ogRank">
|
||||||
|
OG #{{ user.ogRank }}
|
||||||
|
</span>
|
||||||
|
<span class="badge mr-1 badge-default" [class]="'badge-' + user.subscription_tag" *ngIf="user.subscription_tag !== 'free'">
|
||||||
|
{{ user.subscription_tag.toUpperCase() }}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<a *ngIf="!userAuth" class="d-flex justify-content-center align-items-center nav-link m-0 menu-click" routerLink="/login" role="tab" (click)="onLinkClick('/login')">
|
<a *ngIf="!userAuth" class="d-flex justify-content-center align-items-center nav-link m-0 menu-click" routerLink="/login" role="tab" (click)="onLinkClick('/login')">
|
||||||
<fa-icon class="menu-click" [icon]="['fas', 'user-circle']" [fixedWidth]="true" style="font-size: 25px;margin-right: 15px;"></fa-icon>
|
<fa-icon class="menu-click" [icon]="['fas', 'user-circle']" [fixedWidth]="true" style="font-size: 25px;margin-right: 15px;"></fa-icon>
|
||||||
|
|
|
@ -55,4 +55,36 @@
|
||||||
|
|
||||||
@media screen and (max-height: 450px) {
|
@media screen and (max-height: 450px) {
|
||||||
.sidenav a {font-size: 18px;}
|
.sidenav a {font-size: 18px;}
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-default {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-og {
|
||||||
|
background-color: #4a68b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-pleb {
|
||||||
|
background-color: #3ccbe3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-chad {
|
||||||
|
background-color: #957d0b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-whale {
|
||||||
|
background-color: #653b9c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-silver {
|
||||||
|
background-color: #95a5a6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-gold {
|
||||||
|
background-color: #f1c40f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-platinium {
|
||||||
|
background-color: #653b9c;
|
||||||
}
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
import { Component, OnInit, Input, Output, EventEmitter, HostListener, OnDestroy } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter, HostListener, OnDestroy } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { ApiService } from '../../services/api.service';
|
|
||||||
import { MenuGroup } from '../../interfaces/services.interface';
|
import { MenuGroup } from '../../interfaces/services.interface';
|
||||||
import { StorageService } from '../../services/storage.service';
|
import { StorageService } from '../../services/storage.service';
|
||||||
import { Router, NavigationStart } from '@angular/router';
|
import { Router, NavigationStart } from '@angular/router';
|
||||||
import { StateService } from '../../services/state.service';
|
import { StateService } from '../../services/state.service';
|
||||||
|
import { IUser, ServicesApiServices } from '../../services/services-api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-menu',
|
selector: 'app-menu',
|
||||||
|
@ -18,11 +18,12 @@ export class MenuComponent implements OnInit, OnDestroy {
|
||||||
@Output() menuToggled = new EventEmitter<boolean>();
|
@Output() menuToggled = new EventEmitter<boolean>();
|
||||||
|
|
||||||
userMenuGroups$: Observable<MenuGroup[]> | undefined;
|
userMenuGroups$: Observable<MenuGroup[]> | undefined;
|
||||||
|
user$: Observable<IUser | null>;
|
||||||
userAuth: any | undefined;
|
userAuth: any | undefined;
|
||||||
isServicesPage = false;
|
isServicesPage = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private servicesApiServices: ServicesApiServices,
|
||||||
private storageService: StorageService,
|
private storageService: StorageService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private stateService: StateService
|
private stateService: StateService
|
||||||
|
@ -32,7 +33,8 @@ export class MenuComponent implements OnInit, OnDestroy {
|
||||||
this.userAuth = this.storageService.getAuth();
|
this.userAuth = this.storageService.getAuth();
|
||||||
|
|
||||||
if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) {
|
if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) {
|
||||||
this.userMenuGroups$ = this.apiService.getUserMenuGroups$();
|
this.userMenuGroups$ = this.servicesApiServices.getUserMenuGroups$();
|
||||||
|
this.user$ = this.servicesApiServices.userSubject$;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isServicesPage = this.router.url.includes('/services/');
|
this.isServicesPage = this.router.url.includes('/services/');
|
||||||
|
@ -55,10 +57,10 @@ export class MenuComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
logout(): void {
|
logout(): void {
|
||||||
this.apiService.logout$().subscribe(() => {
|
this.servicesApiServices.logout$().subscribe(() => {
|
||||||
this.loggedOut.emit(true);
|
this.loggedOut.emit(true);
|
||||||
if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) {
|
if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) {
|
||||||
this.userMenuGroups$ = this.apiService.getUserMenuGroups$();
|
this.userMenuGroups$ = this.servicesApiServices.getUserMenuGroups$();
|
||||||
this.router.navigateByUrl('/');
|
this.router.navigateByUrl('/');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
catchError,
|
catchError,
|
||||||
retryWhen,
|
retryWhen,
|
||||||
delay,
|
delay,
|
||||||
map,
|
|
||||||
mergeMap,
|
mergeMap,
|
||||||
tap
|
tap
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
@ -26,6 +25,7 @@ import { LiquidUnblinding } from './liquid-ublinding';
|
||||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||||
import { Price, PriceService } from '../../services/price.service';
|
import { Price, PriceService } from '../../services/price.service';
|
||||||
import { isFeatureActive } from '../../bitcoin.utils';
|
import { isFeatureActive } from '../../bitcoin.utils';
|
||||||
|
import { ServicesApiServices } from '../../services/services-api.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-transaction',
|
selector: 'app-transaction',
|
||||||
|
@ -113,6 +113,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private audioService: AudioService,
|
private audioService: AudioService,
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
|
private servicesApiService: ServicesApiServices,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
private priceService: PriceService,
|
private priceService: PriceService,
|
||||||
private storageService: StorageService
|
private storageService: StorageService
|
||||||
|
@ -246,7 +247,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.accelerationInfo = null;
|
this.accelerationInfo = null;
|
||||||
}),
|
}),
|
||||||
switchMap((blockHash: string) => {
|
switchMap((blockHash: string) => {
|
||||||
return this.apiService.getAccelerationHistory$({ blockHash });
|
return this.servicesApiService.getAccelerationHistory$({ blockHash });
|
||||||
}),
|
}),
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
return of(null);
|
return of(null);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ChangeDetectionStrategy, Component, OnInit, ChangeDetectorRef } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
import { Observable, of, EMPTY } from 'rxjs';
|
import { Observable, of } from 'rxjs';
|
||||||
import { catchError, map, switchMap, tap, share } from 'rxjs/operators';
|
import { catchError, map, switchMap, tap, share } from 'rxjs/operators';
|
||||||
import { SeoService } from '../../services/seo.service';
|
import { SeoService } from '../../services/seo.service';
|
||||||
import { ApiService } from '../../services/api.service';
|
import { ApiService } from '../../services/api.service';
|
||||||
|
@ -8,6 +8,7 @@ import { LightningApiService } from '../lightning-api.service';
|
||||||
import { GeolocationData } from '../../shared/components/geolocation/geolocation.component';
|
import { GeolocationData } from '../../shared/components/geolocation/geolocation.component';
|
||||||
import { ILiquidityAd, parseLiquidityAdHex } from './liquidity-ad';
|
import { ILiquidityAd, parseLiquidityAdHex } from './liquidity-ad';
|
||||||
import { haversineDistance, kmToMiles } from '../../../app/shared/common.utils';
|
import { haversineDistance, kmToMiles } from '../../../app/shared/common.utils';
|
||||||
|
import { ServicesApiServices } from '../../services/services-api.service';
|
||||||
|
|
||||||
interface CustomRecord {
|
interface CustomRecord {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -43,6 +44,7 @@ export class NodeComponent implements OnInit {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private apiService: ApiService,
|
private apiService: ApiService,
|
||||||
|
private servicesApiService: ServicesApiServices,
|
||||||
private lightningApiService: LightningApiService,
|
private lightningApiService: LightningApiService,
|
||||||
private activatedRoute: ActivatedRoute,
|
private activatedRoute: ActivatedRoute,
|
||||||
private seoService: SeoService,
|
private seoService: SeoService,
|
||||||
|
@ -155,7 +157,7 @@ export class NodeComponent implements OnInit {
|
||||||
this.nodeOwner$ = this.activatedRoute.paramMap
|
this.nodeOwner$ = this.activatedRoute.paramMap
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((params: ParamMap) => {
|
switchMap((params: ParamMap) => {
|
||||||
return this.apiService.getNodeOwner$(params.get('public_key')).pipe(
|
return this.servicesApiService.getNodeOwner$(params.get('public_key')).pipe(
|
||||||
switchMap((response) => {
|
switchMap((response) => {
|
||||||
if (response.status === 204) {
|
if (response.status === 204) {
|
||||||
return of(false);
|
return of(false);
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
import { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';
|
||||||
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
|
import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITranslators,
|
||||||
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface';
|
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit } from '../interfaces/node-api.interface';
|
||||||
import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
|
import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
|
||||||
import { StateService } from './state.service';
|
import { StateService } from './state.service';
|
||||||
import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface';
|
import { Transaction } from '../interfaces/electrs.interface';
|
||||||
import { Outspend, Transaction } from '../interfaces/electrs.interface';
|
|
||||||
import { Conversion } from './price.service';
|
import { Conversion } from './price.service';
|
||||||
import { MenuGroup } from '../interfaces/services.interface';
|
|
||||||
import { StorageService } from './storage.service';
|
import { StorageService } from './storage.service';
|
||||||
|
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||||
// Todo - move to config.json
|
|
||||||
const SERVICES_API_PREFIX = `/api/v1/services`;
|
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@ -38,12 +34,6 @@ export class ApiService {
|
||||||
}
|
}
|
||||||
this.apiBasePath = network ? '/' + network : '';
|
this.apiBasePath = network ? '/' + network : '';
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) {
|
|
||||||
this.getServicesBackendInfo$().subscribe(version => {
|
|
||||||
this.stateService.servicesBackendInfo$.next(version);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private generateCacheKey(functionName: string, params: any[]): string {
|
private generateCacheKey(functionName: string, params: any[]): string {
|
||||||
|
@ -378,62 +368,4 @@ export class ApiService {
|
||||||
(timestamp ? `?timestamp=${timestamp}` : '')
|
(timestamp ? `?timestamp=${timestamp}` : '')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Services
|
|
||||||
*/
|
|
||||||
|
|
||||||
getNodeOwner$(publicKey: string): Observable<any> {
|
|
||||||
let params = new HttpParams()
|
|
||||||
.set('node_public_key', publicKey);
|
|
||||||
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/lightning/claim/current`, { params, observe: 'response' });
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserMenuGroups$(): Observable<MenuGroup[]> {
|
|
||||||
const auth = this.storageService.getAuth();
|
|
||||||
if (!auth) {
|
|
||||||
return of(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.httpClient.get<MenuGroup[]>(`${SERVICES_API_PREFIX}/account/menu`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUserInfo$(): Observable<any> {
|
|
||||||
const auth = this.storageService.getAuth();
|
|
||||||
if (!auth) {
|
|
||||||
return of(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/account`);
|
|
||||||
}
|
|
||||||
|
|
||||||
logout$(): Observable<any> {
|
|
||||||
const auth = this.storageService.getAuth();
|
|
||||||
if (!auth) {
|
|
||||||
return of(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.removeItem('auth');
|
|
||||||
return this.httpClient.post(`${SERVICES_API_PREFIX}/auth/logout`, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
getServicesBackendInfo$(): Observable<IBackendInfo> {
|
|
||||||
return this.httpClient.get<IBackendInfo>(`${SERVICES_API_PREFIX}/version`);
|
|
||||||
}
|
|
||||||
|
|
||||||
estimate$(txInput: string) {
|
|
||||||
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/estimate`, { txInput: txInput }, { observe: 'response' });
|
|
||||||
}
|
|
||||||
|
|
||||||
accelerate$(txInput: string, userBid: number) {
|
|
||||||
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid });
|
|
||||||
}
|
|
||||||
|
|
||||||
getAccelerations$(): Observable<Acceleration[]> {
|
|
||||||
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`);
|
|
||||||
}
|
|
||||||
|
|
||||||
getAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
|
||||||
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params } });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
141
frontend/src/app/services/services-api.service.ts
Normal file
141
frontend/src/app/services/services-api.service.ts
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { StateService } from './state.service';
|
||||||
|
import { StorageService } from './storage.service';
|
||||||
|
import { MenuGroup } from '../interfaces/services.interface';
|
||||||
|
import { Observable, of, ReplaySubject, tap, catchError, share } from 'rxjs';
|
||||||
|
import { IBackendInfo } from '../interfaces/websocket.interface';
|
||||||
|
import { Acceleration, AccelerationHistoryParams } from '../interfaces/node-api.interface';
|
||||||
|
|
||||||
|
export type ProductType = 'enterprise' | 'community' | 'mining_pool' | 'custom';
|
||||||
|
export interface IUser {
|
||||||
|
username: string;
|
||||||
|
email: string | null;
|
||||||
|
passwordIsSet: boolean;
|
||||||
|
snsId: string;
|
||||||
|
type: ProductType;
|
||||||
|
subscription_tag: string;
|
||||||
|
status: 'pending' | 'verified' | 'disabled';
|
||||||
|
features: string | null;
|
||||||
|
fullName: string | null;
|
||||||
|
countryCode: string | null;
|
||||||
|
imageMd5: string;
|
||||||
|
ogRank: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Todo - move to config.json
|
||||||
|
const SERVICES_API_PREFIX = `/api/v1/services`;
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class ServicesApiServices {
|
||||||
|
private apiBaseUrl: string; // base URL is protocol, hostname, and port
|
||||||
|
private apiBasePath: string; // network path is /testnet, etc. or '' for mainnet
|
||||||
|
|
||||||
|
userSubject$ = new ReplaySubject<IUser | null>(1);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private httpClient: HttpClient,
|
||||||
|
private stateService: StateService,
|
||||||
|
private storageService: StorageService
|
||||||
|
) {
|
||||||
|
this.apiBaseUrl = ''; // use relative URL by default
|
||||||
|
if (!stateService.isBrowser) { // except when inside AU SSR process
|
||||||
|
this.apiBaseUrl = this.stateService.env.NGINX_PROTOCOL + '://' + this.stateService.env.NGINX_HOSTNAME + ':' + this.stateService.env.NGINX_PORT;
|
||||||
|
}
|
||||||
|
this.apiBasePath = ''; // assume mainnet by default
|
||||||
|
this.stateService.networkChanged$.subscribe((network) => {
|
||||||
|
if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) {
|
||||||
|
network = '';
|
||||||
|
}
|
||||||
|
this.apiBasePath = network ? '/' + network : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) {
|
||||||
|
this.getServicesBackendInfo$().subscribe(version => {
|
||||||
|
this.stateService.servicesBackendInfo$.next(version);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getUserInfo$().subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not call directly, userSubject$ instead
|
||||||
|
*/
|
||||||
|
private getUserInfo$() {
|
||||||
|
return this.getUserInfoApi$().pipe(
|
||||||
|
tap((user) => {
|
||||||
|
this.userSubject$.next(user);
|
||||||
|
}),
|
||||||
|
catchError((e) => {
|
||||||
|
if (e.error === 'User does not exists') {
|
||||||
|
this.userSubject$.next(null);
|
||||||
|
this.logout$().subscribe();
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
this.userSubject$.next(null);
|
||||||
|
return of(null);
|
||||||
|
}),
|
||||||
|
share(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not call directly, userSubject$ instead
|
||||||
|
*/
|
||||||
|
private getUserInfoApi$(): Observable<any> {
|
||||||
|
const auth = this.storageService.getAuth();
|
||||||
|
if (!auth) {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/account`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNodeOwner$(publicKey: string): Observable<any> {
|
||||||
|
let params = new HttpParams()
|
||||||
|
.set('node_public_key', publicKey);
|
||||||
|
return this.httpClient.get<any>(`${SERVICES_API_PREFIX}/lightning/claim/current`, { params, observe: 'response' });
|
||||||
|
}
|
||||||
|
|
||||||
|
getUserMenuGroups$(): Observable<MenuGroup[]> {
|
||||||
|
const auth = this.storageService.getAuth();
|
||||||
|
if (!auth) {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpClient.get<MenuGroup[]>(`${SERVICES_API_PREFIX}/account/menu`);
|
||||||
|
}
|
||||||
|
|
||||||
|
logout$(): Observable<any> {
|
||||||
|
const auth = this.storageService.getAuth();
|
||||||
|
if (!auth) {
|
||||||
|
return of(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.removeItem('auth');
|
||||||
|
return this.httpClient.post(`${SERVICES_API_PREFIX}/auth/logout`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
getServicesBackendInfo$(): Observable<IBackendInfo> {
|
||||||
|
return this.httpClient.get<IBackendInfo>(`${SERVICES_API_PREFIX}/version`);
|
||||||
|
}
|
||||||
|
|
||||||
|
estimate$(txInput: string) {
|
||||||
|
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/estimate`, { txInput: txInput }, { observe: 'response' });
|
||||||
|
}
|
||||||
|
|
||||||
|
accelerate$(txInput: string, userBid: number) {
|
||||||
|
return this.httpClient.post<any>(`${SERVICES_API_PREFIX}/accelerator/accelerate`, { txInput: txInput, userBid: userBid });
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccelerations$(): Observable<Acceleration[]> {
|
||||||
|
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations`);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAccelerationHistory$(params: AccelerationHistoryParams): Observable<Acceleration[]> {
|
||||||
|
return this.httpClient.get<Acceleration[]>(`${SERVICES_API_PREFIX}/accelerator/accelerations/history`, { params: { ...params } });
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue