mirror of
https://github.com/mempool/mempool.git
synced 2025-04-07 19:38:32 +02:00
Merge pull request #4509 from mempool/nymkappa/mega-branch
[Mega branch] Various fixes and improvements
This commit is contained in:
commit
1e3650594c
@ -22,6 +22,7 @@ import { FiatCurrencyPipe } from './shared/pipes/fiat-currency.pipe';
|
||||
import { ShortenStringPipe } from './shared/pipes/shorten-string-pipe/shorten-string.pipe';
|
||||
import { CapAddressPipe } from './shared/pipes/cap-address-pipe/cap-address-pipe';
|
||||
import { AppPreloadingStrategy } from './app.preloading-strategy';
|
||||
import { ServicesApiServices } from './services/services-api.service';
|
||||
|
||||
const providers = [
|
||||
ElectrsApiService,
|
||||
@ -40,6 +41,7 @@ const providers = [
|
||||
FiatCurrencyPipe,
|
||||
CapAddressPipe,
|
||||
AppPreloadingStrategy,
|
||||
ServicesApiServices,
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: HttpCacheInterceptor, multi: true }
|
||||
];
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
<div id="become-sponsor-container">
|
||||
<div class="become-sponsor community">
|
||||
<p style="font-weight: 700; font-size: 18px;">If you're an individual...</p>
|
||||
<a href="https://mempool.space/sponsor" class="btn" style="background-color: rgba(152, 88, 255, 0.75); box-shadow: 0px 0px 50px 5px rgba(152, 88, 255, 0.75)" i18n="about.community-sponsor-button" (click)="onSponsorClick($event)">Become a Community Sponsor</a>
|
||||
<a [href]="host + '/sponsor'" class="btn" style="background-color: rgba(152, 88, 255, 0.75); box-shadow: 0px 0px 50px 5px rgba(152, 88, 255, 0.75)" i18n="about.community-sponsor-button" (click)="onSponsorClick($event)">Become a Community Sponsor</a>
|
||||
<p class="sponsor-feature"><fa-icon [icon]="['fas', 'check']"></fa-icon> Exclusive swag</p>
|
||||
<p class="sponsor-feature"><fa-icon [icon]="['fas', 'check']"></fa-icon> Your avatar on the About page</p>
|
||||
<p class="sponsor-feature"><fa-icon [icon]="['fas', 'check']"></fa-icon> And more coming soon :)</p>
|
||||
</div>
|
||||
<div class="become-sponsor enterprise">
|
||||
<p style="font-weight: 700; font-size: 18px;">If you're a business...</p>
|
||||
<a href="https://mempool.space/enterprise" class="btn" style="background-color: rgba(152, 88, 255, 0.75); box-shadow: 0px 0px 50px 5px rgba(152, 88, 255, 0.75)" i18n="about.enterprise-sponsor-button" (click)="onEnterpriseClick($event)">Become an Enterprise Sponsor</a>
|
||||
<a [href]="host + '/enterprise'" class="btn" style="background-color: rgba(152, 88, 255, 0.75); box-shadow: 0px 0px 50px 5px rgba(152, 88, 255, 0.75)" i18n="about.enterprise-sponsor-button" (click)="onEnterpriseClick($event)">Become an Enterprise Sponsor</a>
|
||||
<p class="sponsor-feature"><fa-icon [icon]="['fas', 'check']"></fa-icon> Increased API limits</p>
|
||||
<p class="sponsor-feature"><fa-icon [icon]="['fas', 'check']"></fa-icon> Co-branded instance</p>
|
||||
<p class="sponsor-feature"><fa-icon [icon]="['fas', 'check']"></fa-icon> 99% service-level agreement</p>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { EnterpriseService } from '../../services/enterprise.service';
|
||||
|
||||
@Component({
|
||||
@ -7,6 +7,8 @@ import { EnterpriseService } from '../../services/enterprise.service';
|
||||
styleUrls: ['./about-sponsors.component.scss'],
|
||||
})
|
||||
export class AboutSponsorsComponent {
|
||||
@Input() host = 'https://mempool.space';
|
||||
|
||||
constructor(private enterpriseService: EnterpriseService) {
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import { Subscription, catchError, of, tap } from 'rxjs';
|
||||
import { StorageService } from '../../services/storage.service';
|
||||
import { Transaction } from '../../interfaces/electrs.interface';
|
||||
import { nextRoundNumber } from '../../shared/common.utils';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
import { AudioService } from '../../services/audio.service';
|
||||
|
||||
export type AccelerationEstimate = {
|
||||
@ -62,7 +63,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
maxRateOptions: RateOption[] = [];
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
private storageService: StorageService,
|
||||
private audioService: AudioService,
|
||||
private cd: ChangeDetectorRef
|
||||
@ -83,7 +84,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
ngOnInit() {
|
||||
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) => {
|
||||
if (response.status === 204) {
|
||||
this.estimate = undefined;
|
||||
@ -183,7 +184,7 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
if (this.accelerationSubscription) {
|
||||
this.accelerationSubscription.unsubscribe();
|
||||
}
|
||||
this.accelerationSubscription = this.apiService.accelerate$(
|
||||
this.accelerationSubscription = this.servicesApiService.accelerate$(
|
||||
this.tx.txid,
|
||||
this.userBid
|
||||
).subscribe({
|
||||
@ -213,4 +214,4 @@ export class AcceleratePreviewComponent implements OnInit, OnDestroy, OnChanges
|
||||
onResize(): void {
|
||||
this.isMobile = window.innerWidth <= 767.98;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, L
|
||||
import { EChartsOption, graphic } from 'echarts';
|
||||
import { Observable, Subscription, combineLatest, fromEvent } from 'rxjs';
|
||||
import { map, max, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { ApiService } from '../../../services/api.service';
|
||||
import { SeoService } from '../../../services/seo.service';
|
||||
import { formatNumber } from '@angular/common';
|
||||
import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms';
|
||||
@ -11,6 +10,8 @@ import { StorageService } from '../../../services/storage.service';
|
||||
import { MiningService } from '../../../services/mining.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Acceleration } from '../../../interfaces/node-api.interface';
|
||||
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||
import { ApiService } from '../../../services/api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-acceleration-fees-graph',
|
||||
@ -55,6 +56,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
@Inject(LOCALE_ID) public locale: string,
|
||||
private seoService: SeoService,
|
||||
private apiService: ApiService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private storageService: StorageService,
|
||||
private miningService: MiningService,
|
||||
@ -73,7 +75,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
this.timespan = this.miningWindowPreference;
|
||||
|
||||
this.statsObservable$ = combineLatest([
|
||||
(this.accelerations$ || this.apiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })),
|
||||
(this.accelerations$ || this.servicesApiService.getAccelerationHistory$({ timeframe: this.miningWindowPreference })),
|
||||
this.apiService.getHistoricalBlockFees$(this.miningWindowPreference),
|
||||
fromEvent(window, 'resize').pipe(startWith(null)),
|
||||
]).pipe(
|
||||
@ -103,7 +105,7 @@ export class AccelerationFeesGraphComponent implements OnInit, OnDestroy {
|
||||
this.isLoading = true;
|
||||
this.storageService.setValue('miningWindowPreference', timespan);
|
||||
this.timespan = timespan;
|
||||
return this.apiService.getAccelerationHistory$({});
|
||||
return this.servicesApiService.getAccelerationHistory$({});
|
||||
})
|
||||
),
|
||||
this.radioGroupForm.get('dateSpan').valueChanges.pipe(
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy, Input, ChangeDetectorRef } from '@angular/core';
|
||||
import { Observable, catchError, of, switchMap, tap } from 'rxjs';
|
||||
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
||||
import { ApiService } from '../../../services/api.service';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { WebsocketService } from '../../../services/websocket.service';
|
||||
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accelerations-list',
|
||||
@ -26,7 +26,7 @@ export class AccelerationsListComponent implements OnInit {
|
||||
skeletonLines: number[] = [];
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
private websocketService: WebsocketService,
|
||||
public stateService: StateService,
|
||||
private cd: ChangeDetectorRef,
|
||||
@ -41,7 +41,7 @@ export class AccelerationsListComponent implements OnInit {
|
||||
this.skeletonLines = this.widget === true ? [...Array(6).keys()] : [...Array(15).keys()];
|
||||
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(
|
||||
switchMap(accelerations => {
|
||||
if (this.pending) {
|
||||
|
@ -3,12 +3,12 @@ import { SeoService } from '../../../services/seo.service';
|
||||
import { WebsocketService } from '../../../services/websocket.service';
|
||||
import { Acceleration, BlockExtended } from '../../../interfaces/node-api.interface';
|
||||
import { StateService } from '../../../services/state.service';
|
||||
import { Observable, Subject, catchError, combineLatest, distinctUntilChanged, interval, map, of, share, startWith, switchMap, tap } from 'rxjs';
|
||||
import { ApiService } from '../../../services/api.service';
|
||||
import { Observable, catchError, combineLatest, distinctUntilChanged, interval, map, of, share, startWith, switchMap, tap } from 'rxjs';
|
||||
import { Color } from '../../block-overview-graph/sprite-types';
|
||||
import { hexToColor } from '../../block-overview-graph/utils';
|
||||
import TxView from '../../block-overview-graph/tx-view';
|
||||
import { feeLevels, mempoolFeeColors } from '../../../app.constants';
|
||||
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||
|
||||
const acceleratedColor: Color = hexToColor('8F5FF6');
|
||||
const normalColors = mempoolFeeColors.map(hex => hexToColor(hex + '5F'));
|
||||
@ -35,7 +35,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
constructor(
|
||||
private seoService: SeoService,
|
||||
private websocketService: WebsocketService,
|
||||
private apiService: ApiService,
|
||||
private serviceApiServices: ServicesApiServices,
|
||||
private stateService: StateService,
|
||||
) {
|
||||
this.seoService.setTitle($localize`:@@a681a4e2011bb28157689dbaa387de0dd0aa0c11:Accelerator Dashboard`);
|
||||
@ -48,7 +48,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
this.pendingAccelerations$ = interval(30000).pipe(
|
||||
startWith(true),
|
||||
switchMap(() => {
|
||||
return this.apiService.getAccelerations$().pipe(
|
||||
return this.serviceApiServices.getAccelerations$().pipe(
|
||||
catchError(() => {
|
||||
return of([]);
|
||||
}),
|
||||
@ -60,7 +60,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
this.accelerations$ = this.stateService.chainTip$.pipe(
|
||||
distinctUntilChanged(),
|
||||
switchMap(() => {
|
||||
return this.apiService.getAccelerationHistory$({ timeframe: '1m' }).pipe(
|
||||
return this.serviceApiServices.getAccelerationHistory$({ timeframe: '1m' }).pipe(
|
||||
catchError(() => {
|
||||
return of([]);
|
||||
}),
|
||||
@ -71,7 +71,7 @@ export class AcceleratorDashboardComponent implements OnInit {
|
||||
|
||||
this.minedAccelerations$ = this.accelerations$.pipe(
|
||||
map(accelerations => {
|
||||
return accelerations.filter(acc => ['mined', 'completed'].includes(acc.status));
|
||||
return accelerations.filter(acc => ['mined', 'completed', 'failed'].includes(acc.status));
|
||||
})
|
||||
);
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { switchMap } from 'rxjs/operators';
|
||||
import { ApiService } from '../../../services/api.service';
|
||||
import { Acceleration } from '../../../interfaces/node-api.interface';
|
||||
import { ServicesApiServices } from '../../../services/services-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pending-stats',
|
||||
@ -15,11 +15,11 @@ export class PendingStatsComponent implements OnInit {
|
||||
public accelerationStats$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.accelerationStats$ = (this.accelerations$ || this.apiService.getAccelerations$()).pipe(
|
||||
this.accelerationStats$ = (this.accelerations$ || this.servicesApiService.getAccelerations$()).pipe(
|
||||
switchMap(accelerations => {
|
||||
let totalAccelerations = 0;
|
||||
let totalFeeDelta = 0;
|
||||
|
@ -10,6 +10,7 @@ import { BlockExtended, TransactionStripped } from '../../interfaces/node-api.in
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||
import { BlockOverviewGraphComponent } from '../../components/block-overview-graph/block-overview-graph.component';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block-preview',
|
||||
@ -42,7 +43,8 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
|
||||
public stateService: StateService,
|
||||
private seoService: SeoService,
|
||||
private openGraphService: OpenGraphService,
|
||||
private apiService: ApiService
|
||||
private apiService: ApiService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
) { }
|
||||
|
||||
ngOnInit() {
|
||||
@ -134,7 +136,7 @@ export class BlockPreviewComponent implements OnInit, OnDestroy {
|
||||
return of(transactions);
|
||||
})
|
||||
),
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.apiService.getAccelerationHistory$({ blockHash: block.id }) : of([])
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHash: block.id }) : of([])
|
||||
]);
|
||||
}
|
||||
),
|
||||
|
@ -16,6 +16,7 @@ import { detectWebGL } from '../../shared/graphs.utils';
|
||||
import { seoDescriptionNetwork } from '../../shared/common.utils';
|
||||
import { PriceService, Price } from '../../services/price.service';
|
||||
import { CacheService } from '../../services/cache.service';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-block',
|
||||
@ -103,6 +104,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
private apiService: ApiService,
|
||||
private priceService: PriceService,
|
||||
private cacheService: CacheService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
) {
|
||||
this.webGlEnabled = detectWebGL();
|
||||
}
|
||||
@ -329,7 +331,7 @@ export class BlockComponent implements OnInit, OnDestroy {
|
||||
return of(null);
|
||||
})
|
||||
),
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.apiService.getAccelerationHistory$({ blockHash: block.id }) : of([])
|
||||
this.stateService.env.ACCELERATOR === true && block.height > 819500 ? this.servicesApiService.getAccelerationHistory$({ blockHash: block.id }) : of([])
|
||||
]);
|
||||
})
|
||||
)
|
||||
|
@ -2,8 +2,19 @@
|
||||
<div class="d-flex menu-click">
|
||||
|
||||
<nav class="scrollable menu-click">
|
||||
<span *ngIf="userAuth" class="menu-click">
|
||||
<strong class="menu-click text-nowrap ellipsis">@ {{ userAuth.user.username }}</strong>
|
||||
<span *ngIf="user$ | async as user" class="menu-click">
|
||||
<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>
|
||||
<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>
|
||||
|
@ -55,4 +55,36 @@
|
||||
|
||||
@media screen and (max-height: 450px) {
|
||||
.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 { Observable } from 'rxjs';
|
||||
import { ApiService } from '../../services/api.service';
|
||||
import { MenuGroup } from '../../interfaces/services.interface';
|
||||
import { StorageService } from '../../services/storage.service';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { IUser, ServicesApiServices } from '../../services/services-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-menu',
|
||||
@ -18,11 +18,12 @@ export class MenuComponent implements OnInit, OnDestroy {
|
||||
@Output() menuToggled = new EventEmitter<boolean>();
|
||||
|
||||
userMenuGroups$: Observable<MenuGroup[]> | undefined;
|
||||
user$: Observable<IUser | null>;
|
||||
userAuth: any | undefined;
|
||||
isServicesPage = false;
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private servicesApiServices: ServicesApiServices,
|
||||
private storageService: StorageService,
|
||||
private router: Router,
|
||||
private stateService: StateService
|
||||
@ -32,7 +33,8 @@ export class MenuComponent implements OnInit, OnDestroy {
|
||||
this.userAuth = this.storageService.getAuth();
|
||||
|
||||
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/');
|
||||
@ -55,10 +57,10 @@ export class MenuComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.apiService.logout$().subscribe(() => {
|
||||
this.servicesApiServices.logout$().subscribe(() => {
|
||||
this.loggedOut.emit(true);
|
||||
if (this.stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE) {
|
||||
this.userMenuGroups$ = this.apiService.getUserMenuGroups$();
|
||||
this.userMenuGroups$ = this.servicesApiServices.getUserMenuGroups$();
|
||||
this.router.navigateByUrl('/');
|
||||
}
|
||||
});
|
||||
|
@ -7,7 +7,6 @@ import {
|
||||
catchError,
|
||||
retryWhen,
|
||||
delay,
|
||||
map,
|
||||
mergeMap,
|
||||
tap
|
||||
} from 'rxjs/operators';
|
||||
@ -26,6 +25,7 @@ import { LiquidUnblinding } from './liquid-ublinding';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
import { Price, PriceService } from '../../services/price.service';
|
||||
import { isFeatureActive } from '../../bitcoin.utils';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-transaction',
|
||||
@ -113,6 +113,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
private websocketService: WebsocketService,
|
||||
private audioService: AudioService,
|
||||
private apiService: ApiService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
private seoService: SeoService,
|
||||
private priceService: PriceService,
|
||||
private storageService: StorageService
|
||||
@ -247,7 +248,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.accelerationInfo = null;
|
||||
}),
|
||||
switchMap((blockHash: string) => {
|
||||
return this.apiService.getAccelerationHistory$({ blockHash });
|
||||
return this.servicesApiService.getAccelerationHistory$({ blockHash });
|
||||
}),
|
||||
catchError(() => {
|
||||
return of(null);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, OnInit, ChangeDetectorRef } from '@angular/core';
|
||||
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 { SeoService } from '../../services/seo.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 { ILiquidityAd, parseLiquidityAdHex } from './liquidity-ad';
|
||||
import { haversineDistance, kmToMiles } from '../../../app/shared/common.utils';
|
||||
import { ServicesApiServices } from '../../services/services-api.service';
|
||||
|
||||
interface CustomRecord {
|
||||
type: string;
|
||||
@ -43,6 +44,7 @@ export class NodeComponent implements OnInit {
|
||||
|
||||
constructor(
|
||||
private apiService: ApiService,
|
||||
private servicesApiService: ServicesApiServices,
|
||||
private lightningApiService: LightningApiService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private seoService: SeoService,
|
||||
@ -155,7 +157,7 @@ export class NodeComponent implements OnInit {
|
||||
this.nodeOwner$ = this.activatedRoute.paramMap
|
||||
.pipe(
|
||||
switchMap((params: ParamMap) => {
|
||||
return this.apiService.getNodeOwner$(params.get('public_key')).pipe(
|
||||
return this.servicesApiService.getNodeOwner$(params.get('public_key')).pipe(
|
||||
switchMap((response) => {
|
||||
if (response.status === 204) {
|
||||
return of(false);
|
||||
|
@ -4,14 +4,10 @@ import { CpfpInfo, OptimizedMempoolStats, AddressInformation, LiquidPegs, ITrans
|
||||
PoolStat, BlockExtended, TransactionStripped, RewardStats, AuditScore, BlockSizesAndWeights, RbfTree, BlockAudit, Acceleration, AccelerationHistoryParams, CurrentPegs, AuditStatus, FederationAddress, FederationUtxo, RecentPeg, PegsVolume } from '../interfaces/node-api.interface';
|
||||
import { BehaviorSubject, Observable, catchError, filter, of, shareReplay, take, tap } from 'rxjs';
|
||||
import { StateService } from './state.service';
|
||||
import { IBackendInfo, WebsocketResponse } from '../interfaces/websocket.interface';
|
||||
import { Outspend, Transaction } from '../interfaces/electrs.interface';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { Conversion } from './price.service';
|
||||
import { MenuGroup } from '../interfaces/services.interface';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
// Todo - move to config.json
|
||||
const SERVICES_API_PREFIX = `/api/v1/services`;
|
||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -38,12 +34,6 @@ export class ApiService {
|
||||
}
|
||||
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 {
|
||||
@ -418,62 +408,4 @@ export class ApiService {
|
||||
(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 } });
|
||||
}
|
||||
}
|
@ -94,7 +94,7 @@
|
||||
<div class="col-sm-12">
|
||||
<p *ngIf="officialMempoolSpace">
|
||||
<span>{{ (backendInfo$ | async)?.hostname }} (v{{ (backendInfo$ | async )?.version }}) [<a target="_blank" href="https://github.com/mempool/mempool/commit/{{ (backendInfo$ | async )?.gitCommit | slice:0:8 }}">{{ (backendInfo$ | async )?.gitCommit | slice:0:8 }}</a>]</span>
|
||||
<span *ngIf="stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE"> - (v{{ (servicesBackendInfo$ | async )?.version }}) [{{ (servicesBackendInfo$ | async )?.gitCommit | slice:0:8 }}]</span>
|
||||
<span *ngIf="stateService.env.GIT_COMMIT_HASH_MEMPOOL_SPACE">[{{ (servicesBackendInfo$ | async )?.gitCommit | slice:0:8 }}]</span>
|
||||
</p>
|
||||
<p *ngIf="!officialMempoolSpace">v{{ packetJsonVersion }} [<a target="_blank" href="https://github.com/mempool/mempool/commit/{{ frontendGitCommitHash }}">{{ frontendGitCommitHash }}</a>]</p>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user