Merge branch 'master' into nymkappa/prepaid-acceleration
This commit is contained in:
@@ -29,9 +29,6 @@ export class ApiService {
|
||||
}
|
||||
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 : '';
|
||||
});
|
||||
}
|
||||
@@ -228,8 +225,9 @@ export class ApiService {
|
||||
return this.httpClient.get<any>(this.apiBaseUrl + this.apiBasePath + '/api/v1/liquid/reserves/utxos/emergency-spent/stats');
|
||||
}
|
||||
|
||||
listFeaturedAssets$(): Observable<any[]> {
|
||||
return this.httpClient.get<any[]>(this.apiBaseUrl + '/api/v1/assets/featured');
|
||||
listFeaturedAssets$(network: string = 'liquid'): Observable<any[]> {
|
||||
if (network === 'liquid') return this.httpClient.get<any[]>(this.apiBaseUrl + '/api/v1/assets/featured');
|
||||
return of([]);
|
||||
}
|
||||
|
||||
getAssetGroup$(id: string): Observable<any> {
|
||||
|
||||
@@ -25,9 +25,6 @@ export class ElectrsApiService {
|
||||
}
|
||||
this.apiBasePath = ''; // assume mainnet by default
|
||||
this.stateService.networkChanged$.subscribe((network) => {
|
||||
if (network === 'bisq') {
|
||||
network = '';
|
||||
}
|
||||
this.apiBasePath = network ? '/' + network : '';
|
||||
});
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ export class EnterpriseService {
|
||||
this.subdomain = subdomain;
|
||||
this.fetchSubdomainInfo();
|
||||
this.disableSubnetworks();
|
||||
this.stateService.env.ACCELERATOR = false;
|
||||
} else {
|
||||
this.insertMatomo();
|
||||
}
|
||||
@@ -43,7 +44,6 @@ export class EnterpriseService {
|
||||
this.stateService.env.LIQUID_ENABLED = false;
|
||||
this.stateService.env.LIQUID_TESTNET_ENABLED = false;
|
||||
this.stateService.env.SIGNET_ENABLED = false;
|
||||
this.stateService.env.BISQ_ENABLED = false;
|
||||
}
|
||||
|
||||
fetchSubdomainInfo(): void {
|
||||
@@ -80,14 +80,6 @@ export class EnterpriseService {
|
||||
siteId = 10;
|
||||
statsUrl = '//stats.liquid.network/';
|
||||
break;
|
||||
case 'bisq.markets':
|
||||
siteId = 7;
|
||||
statsUrl = '//stats.bisq.markets/';
|
||||
break;
|
||||
case 'bisq.ninja':
|
||||
statsUrl = '//stats.bisq.markets/';
|
||||
siteId = 11;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
|
||||
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpResponse } from '@angular/common/http';
|
||||
import { Inject, Injectable, PLATFORM_ID, makeStateKey, TransferState } from '@angular/core';
|
||||
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpResponse, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { TransferState, makeStateKey } from '@angular/platform-browser';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
|
||||
@Injectable()
|
||||
@@ -17,14 +17,18 @@ export class HttpCacheInterceptor implements HttpInterceptor {
|
||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
if (this.isBrowser && request.method === 'GET') {
|
||||
|
||||
const cachedResponse = this.transferState.get<any>(makeStateKey(request.url), null);
|
||||
if (cachedResponse) {
|
||||
const { response, headers } = this.transferState.get<any>(makeStateKey(request.url), null) || {};
|
||||
if (response) {
|
||||
const httpHeaders = new HttpHeaders();
|
||||
for (const [k,v] of Object.entries(headers)) {
|
||||
httpHeaders.set(k,v as string[]);
|
||||
}
|
||||
const modifiedResponse = new HttpResponse<any>({
|
||||
headers: cachedResponse.headers,
|
||||
body: cachedResponse.body,
|
||||
status: cachedResponse.status,
|
||||
statusText: cachedResponse.statusText,
|
||||
url: cachedResponse.url
|
||||
headers: httpHeaders,
|
||||
body: response.body,
|
||||
status: response.status,
|
||||
statusText: response.statusText,
|
||||
url: response.url
|
||||
});
|
||||
this.transferState.remove(makeStateKey(request.url));
|
||||
return of(modifiedResponse);
|
||||
@@ -32,11 +36,41 @@ export class HttpCacheInterceptor implements HttpInterceptor {
|
||||
}
|
||||
|
||||
return next.handle(request)
|
||||
.pipe(tap((event: HttpEvent<any>) => {
|
||||
if (!this.isBrowser && event instanceof HttpResponse) {
|
||||
let keyId = request.url.split('/').slice(3).join('/');
|
||||
this.transferState.set<any>(makeStateKey('/' + keyId), event);
|
||||
}
|
||||
}));
|
||||
.pipe(
|
||||
tap((event: HttpEvent<any>) => {
|
||||
if (!this.isBrowser && event instanceof HttpResponse) {
|
||||
let keyId = request.url.split('/').slice(3).join('/');
|
||||
const headers = {};
|
||||
for (const k of event.headers.keys()) {
|
||||
headers[k] = event.headers.getAll(k);
|
||||
}
|
||||
this.transferState.set<any>(makeStateKey('/' + keyId), { response: event, headers });
|
||||
}
|
||||
}),
|
||||
catchError((e) => {
|
||||
if (e instanceof HttpErrorResponse) {
|
||||
if (e.status === 0) {
|
||||
throw new HttpErrorResponse({
|
||||
error: 'Unknown error',
|
||||
headers: e.headers,
|
||||
status: 0,
|
||||
statusText: 'Unknown error',
|
||||
url: e.url,
|
||||
});
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} else {
|
||||
const msg = e?.['message'] || 'Unknown error';
|
||||
throw new HttpErrorResponse({
|
||||
error: msg,
|
||||
headers: new HttpHeaders(),
|
||||
status: 0,
|
||||
statusText: msg,
|
||||
url: '',
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,7 @@ const networkModules = {
|
||||
{ name: 'liquid', path: '' },
|
||||
{ name: 'liquidtestnet', path: '/testnet' },
|
||||
],
|
||||
},
|
||||
bisq: {
|
||||
subnets: [
|
||||
{ name: 'bisq', path: '' },
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
const networks = Object.keys(networkModules);
|
||||
|
||||
@@ -44,7 +39,7 @@ export class NavigationService {
|
||||
});
|
||||
}
|
||||
|
||||
// For each network (bitcoin/liquid/bisq), find and save the longest url path compatible with the current route
|
||||
// For each network (bitcoin/liquid), find and save the longest url path compatible with the current route
|
||||
updateSubnetPaths(root: ActivatedRouteSnapshot): void {
|
||||
let path = '';
|
||||
const networkPaths = {};
|
||||
|
||||
@@ -71,8 +71,6 @@ export class SeoService {
|
||||
let domain = 'mempool.space';
|
||||
if (this.stateService.env.BASE_MODULE === 'liquid') {
|
||||
domain = 'liquid.network';
|
||||
} else if (this.stateService.env.BASE_MODULE === 'bisq') {
|
||||
domain = 'bisq.markets';
|
||||
}
|
||||
this.canonicalLink.setAttribute('href', 'https://' + domain + path);
|
||||
}
|
||||
@@ -86,8 +84,6 @@ export class SeoService {
|
||||
return this.baseTitle + ' - Liquid Network';
|
||||
if (this.network === 'liquidtestnet')
|
||||
return this.baseTitle + ' - Liquid Testnet';
|
||||
if (this.network === 'bisq')
|
||||
return this.baseTitle + ' - Bisq Markets';
|
||||
return this.baseTitle + ' - ' + (this.network ? this.ucfirst(this.network) : 'Bitcoin') + ' Explorer';
|
||||
}
|
||||
|
||||
@@ -96,8 +92,6 @@ export class SeoService {
|
||||
return this.baseDescription + ' See the real-time status of your transactions, browse network stats, and more.';
|
||||
if ( (this.network === 'liquid') || (this.network === 'liquidtestnet') )
|
||||
return this.baseDescription + ' See Liquid transactions & assets, get network info, and more.';
|
||||
if (this.network === 'bisq')
|
||||
return this.baseDescription + ' See Bisq market prices, trading activity, and more.';
|
||||
}
|
||||
|
||||
ucfirst(str: string) {
|
||||
|
||||
@@ -52,9 +52,6 @@ export class ServicesApiServices {
|
||||
}
|
||||
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 : '';
|
||||
});
|
||||
|
||||
@@ -104,12 +101,6 @@ export class ServicesApiServices {
|
||||
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) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Inject, Injectable, PLATFORM_ID, LOCALE_ID } from '@angular/core';
|
||||
import { ReplaySubject, BehaviorSubject, Subject, fromEvent, Observable, merge } from 'rxjs';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo, TransactionStripped } from '../interfaces/websocket.interface';
|
||||
import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree } from '../interfaces/node-api.interface';
|
||||
import { HealthCheckHost, IBackendInfo, MempoolBlock, MempoolBlockDelta, MempoolInfo, Recommendedfees, ReplacedTransaction, ReplacementInfo } from '../interfaces/websocket.interface';
|
||||
import { BlockExtended, CpfpInfo, DifficultyAdjustment, MempoolPosition, OptimizedMempoolStats, RbfTree, TransactionStripped } from '../interfaces/node-api.interface';
|
||||
import { Router, NavigationStart } from '@angular/router';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { filter, map, scan, shareReplay } from 'rxjs/operators';
|
||||
@@ -25,8 +25,6 @@ export interface Env {
|
||||
SIGNET_ENABLED: boolean;
|
||||
LIQUID_ENABLED: boolean;
|
||||
LIQUID_TESTNET_ENABLED: boolean;
|
||||
BISQ_ENABLED: boolean;
|
||||
BISQ_SEPARATE_BACKEND: boolean;
|
||||
ITEMS_PER_PAGE: number;
|
||||
KEEP_BLOCKS_AMOUNT: number;
|
||||
OFFICIAL_MEMPOOL_SPACE: boolean;
|
||||
@@ -40,7 +38,6 @@ export interface Env {
|
||||
PACKAGE_JSON_VERSION: string;
|
||||
MEMPOOL_WEBSITE_URL: string;
|
||||
LIQUID_WEBSITE_URL: string;
|
||||
BISQ_WEBSITE_URL: string;
|
||||
MINING_DASHBOARD: boolean;
|
||||
LIGHTNING: boolean;
|
||||
AUDIT: boolean;
|
||||
@@ -60,8 +57,6 @@ const defaultEnv: Env = {
|
||||
'LIQUID_ENABLED': false,
|
||||
'LIQUID_TESTNET_ENABLED': false,
|
||||
'BASE_MODULE': 'mempool',
|
||||
'BISQ_ENABLED': false,
|
||||
'BISQ_SEPARATE_BACKEND': false,
|
||||
'ITEMS_PER_PAGE': 10,
|
||||
'KEEP_BLOCKS_AMOUNT': 8,
|
||||
'OFFICIAL_MEMPOOL_SPACE': false,
|
||||
@@ -74,7 +69,6 @@ const defaultEnv: Env = {
|
||||
'PACKAGE_JSON_VERSION': '',
|
||||
'MEMPOOL_WEBSITE_URL': 'https://mempool.space',
|
||||
'LIQUID_WEBSITE_URL': 'https://liquid.network',
|
||||
'BISQ_WEBSITE_URL': 'https://bisq.markets',
|
||||
'MINING_DASHBOARD': true,
|
||||
'LIGHTNING': false,
|
||||
'AUDIT': false,
|
||||
@@ -92,6 +86,7 @@ const defaultEnv: Env = {
|
||||
export class StateService {
|
||||
isBrowser: boolean = isPlatformBrowser(this.platformId);
|
||||
isMempoolSpaceBuild = window['isMempoolSpaceBuild'] ?? false;
|
||||
backend: 'esplora' | 'electrum' | 'none' = 'esplora';
|
||||
network = '';
|
||||
lightning = false;
|
||||
blockVSize: number;
|
||||
@@ -99,6 +94,7 @@ export class StateService {
|
||||
latestBlockHeight = -1;
|
||||
blocks: BlockExtended[] = [];
|
||||
|
||||
backend$ = new BehaviorSubject<'esplora' | 'electrum' | 'none'>('esplora');
|
||||
networkChanged$ = new ReplaySubject<string>(1);
|
||||
lightningChanged$ = new ReplaySubject<boolean>(1);
|
||||
blocksSubject$ = new BehaviorSubject<BlockExtended[]>([]);
|
||||
@@ -151,6 +147,7 @@ export class StateService {
|
||||
hideAudit: BehaviorSubject<boolean>;
|
||||
fiatCurrency$: BehaviorSubject<string>;
|
||||
rateUnits$: BehaviorSubject<string>;
|
||||
showMiningInfo$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
searchFocus$: Subject<boolean> = new Subject<boolean>();
|
||||
menuOpen$: BehaviorSubject<boolean> = new BehaviorSubject(false);
|
||||
@@ -213,11 +210,6 @@ export class StateService {
|
||||
}
|
||||
}, {}));
|
||||
|
||||
if (this.env.BASE_MODULE === 'bisq') {
|
||||
this.network = this.env.BASE_MODULE;
|
||||
this.networkChanged$.next(this.env.BASE_MODULE);
|
||||
}
|
||||
|
||||
this.networkChanged$.subscribe((network) => {
|
||||
this.transactions$ = new BehaviorSubject<TransactionStripped[]>(null);
|
||||
this.blocksSubject$.next([]);
|
||||
@@ -256,6 +248,10 @@ export class StateService {
|
||||
|
||||
const rateUnitPreference = this.storageService.getValue('rate-unit-preference');
|
||||
this.rateUnits$ = new BehaviorSubject<string>(rateUnitPreference || 'vb');
|
||||
|
||||
this.backend$.subscribe(backend => {
|
||||
this.backend = backend;
|
||||
});
|
||||
}
|
||||
|
||||
setNetworkBasedonUrl(url: string) {
|
||||
@@ -266,9 +262,9 @@ export class StateService {
|
||||
// /^\/ starts with a forward slash...
|
||||
// (?:[a-z]{2}(?:-[A-Z]{2})?\/)? optional locale prefix (non-capturing)
|
||||
// (?:preview\/)? optional "preview" prefix (non-capturing)
|
||||
// (bisq|testnet|liquidtestnet|liquid|signet)/ network string (captured as networkMatches[1])
|
||||
// (testnet|liquidtestnet|liquid|signet)/ network string (captured as networkMatches[1])
|
||||
// ($|\/) network string must end or end with a slash
|
||||
const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(bisq|testnet|liquidtestnet|liquid|signet)($|\/)/);
|
||||
const networkMatches = url.match(/^\/(?:[a-z]{2}(?:-[A-Z]{2})?\/)?(?:preview\/)?(testnet|liquidtestnet|liquid|signet)($|\/)/);
|
||||
switch (networkMatches && networkMatches[1]) {
|
||||
case 'liquid':
|
||||
if (this.network !== 'liquid') {
|
||||
@@ -299,12 +295,6 @@ export class StateService {
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 'bisq':
|
||||
if (this.network !== 'bisq') {
|
||||
this.network = 'bisq';
|
||||
this.networkChanged$.next('bisq');
|
||||
}
|
||||
return;
|
||||
default:
|
||||
if (this.env.BASE_MODULE !== 'mempool') {
|
||||
if (this.network !== this.env.BASE_MODULE) {
|
||||
|
||||
@@ -3,10 +3,10 @@ import { webSocket, WebSocketSubject } from 'rxjs/webSocket';
|
||||
import { WebsocketResponse } from '../interfaces/websocket.interface';
|
||||
import { StateService } from './state.service';
|
||||
import { Transaction } from '../interfaces/electrs.interface';
|
||||
import { Subscription } from 'rxjs';
|
||||
import { firstValueFrom, Subscription } from 'rxjs';
|
||||
import { ApiService } from './api.service';
|
||||
import { take } from 'rxjs/operators';
|
||||
import { TransferState, makeStateKey } from '@angular/platform-browser';
|
||||
import { TransferState, makeStateKey } from '@angular/core';
|
||||
import { CacheService } from './cache.service';
|
||||
import { uncompressDeltaChange, uncompressTx } from '../shared/common.utils';
|
||||
|
||||
@@ -54,11 +54,16 @@ export class WebsocketService {
|
||||
.pipe(take(1))
|
||||
.subscribe((response) => this.handleResponse(response));
|
||||
} else {
|
||||
this.network = this.stateService.network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND ? '' : this.stateService.network;
|
||||
this.network = this.stateService.network;
|
||||
this.websocketSubject = webSocket<WebsocketResponse>(this.webSocketUrl.replace('{network}', this.network ? '/' + this.network : ''));
|
||||
|
||||
const theInitData = this.transferState.get<any>(initData, null);
|
||||
const { response: theInitData } = this.transferState.get<any>(initData, null) || {};
|
||||
if (theInitData) {
|
||||
if (theInitData.body.blocks) {
|
||||
theInitData.body.blocks = theInitData.body.blocks.reverse();
|
||||
}
|
||||
this.stateService.backend$.next(theInitData.backend);
|
||||
this.stateService.isLoadingWebSocket$.next(false);
|
||||
this.handleResponse(theInitData.body);
|
||||
this.startSubscription(false, true);
|
||||
} else {
|
||||
@@ -66,9 +71,6 @@ export class WebsocketService {
|
||||
}
|
||||
|
||||
this.stateService.networkChanged$.subscribe((network) => {
|
||||
if (network === 'bisq' && !this.stateService.env.BISQ_SEPARATE_BACKEND) {
|
||||
network = '';
|
||||
}
|
||||
if (network === this.network) {
|
||||
return;
|
||||
}
|
||||
@@ -223,6 +225,7 @@ export class WebsocketService {
|
||||
}
|
||||
|
||||
startTrackRbfSummary() {
|
||||
this.initRbfSummary();
|
||||
this.websocketSubject.next({ 'track-rbf-summary': true });
|
||||
this.isTrackingRbfSummary = true;
|
||||
}
|
||||
@@ -232,14 +235,6 @@ export class WebsocketService {
|
||||
this.isTrackingRbfSummary = false;
|
||||
}
|
||||
|
||||
startTrackBisqMarket(market: string) {
|
||||
this.websocketSubject.next({ 'track-bisq-market': market });
|
||||
}
|
||||
|
||||
stopTrackingBisqMarket() {
|
||||
this.websocketSubject.next({ 'track-bisq-market': 'stop' });
|
||||
}
|
||||
|
||||
fetchStatistics(historicalDate: string) {
|
||||
this.websocketSubject.next({ historicalDate });
|
||||
}
|
||||
@@ -285,6 +280,10 @@ export class WebsocketService {
|
||||
handleResponse(response: WebsocketResponse) {
|
||||
let reinitBlocks = false;
|
||||
|
||||
if (response.backend) {
|
||||
this.stateService.backend$.next(response.backend);
|
||||
}
|
||||
|
||||
if (response.blocks && response.blocks.length) {
|
||||
const blocks = response.blocks;
|
||||
this.stateService.resetBlocks(blocks);
|
||||
@@ -445,4 +444,30 @@ export class WebsocketService {
|
||||
this.websocketSubject.next({'refresh-blocks': true});
|
||||
}
|
||||
}
|
||||
|
||||
async initRbfSummary(): Promise<void> {
|
||||
if (!this.stateService.isBrowser) {
|
||||
const rbfList = await firstValueFrom(this.apiService.getRbfList$(false));
|
||||
if (rbfList) {
|
||||
const rbfSummary = rbfList.slice(0, 6).map(rbfTree => {
|
||||
let oldFee = 0;
|
||||
let oldVsize = 0;
|
||||
for (const replaced of rbfTree.replaces) {
|
||||
oldFee += replaced.tx.fee;
|
||||
oldVsize += replaced.tx.vsize;
|
||||
}
|
||||
return {
|
||||
txid: rbfTree.tx.txid,
|
||||
mined: !!rbfTree.tx.mined,
|
||||
fullRbf: !!rbfTree.tx.fullRbf,
|
||||
oldFee,
|
||||
oldVsize,
|
||||
newFee: rbfTree.tx.fee,
|
||||
newVsize: rbfTree.tx.vsize,
|
||||
};
|
||||
});
|
||||
this.stateService.rbfLatestSummary$.next(rbfSummary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
14
frontend/src/app/services/zone-shim.service.ts
Normal file
14
frontend/src/app/services/zone-shim.service.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ZoneService {
|
||||
|
||||
constructor() { }
|
||||
|
||||
wrapObservable<T>(obs: Observable<T>): Observable<T> {
|
||||
return obs;
|
||||
}
|
||||
}
|
||||
60
frontend/src/app/services/zone.service.ts
Normal file
60
frontend/src/app/services/zone.service.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ApplicationRef, Injectable, NgZone } from '@angular/core';
|
||||
import { Observable, Subscriber } from 'rxjs';
|
||||
|
||||
// global Zone object provided by zone.js
|
||||
declare const Zone: any;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ZoneService {
|
||||
|
||||
constructor(
|
||||
private ngZone: NgZone,
|
||||
private appRef: ApplicationRef,
|
||||
) { }
|
||||
|
||||
wrapObservable<T>(obs: Observable<T>): Observable<T> {
|
||||
return new Observable((subscriber: Subscriber<T>) => {
|
||||
let task: any;
|
||||
|
||||
this.ngZone.run(() => {
|
||||
task = Zone.current.scheduleMacroTask('wrapObservable', () => {}, {}, () => {}, () => {});
|
||||
});
|
||||
|
||||
const subscription = obs.subscribe(
|
||||
value => {
|
||||
subscriber.next(value);
|
||||
if (task) {
|
||||
this.ngZone.run(() => {
|
||||
this.appRef.tick();
|
||||
});
|
||||
task.invoke();
|
||||
}
|
||||
},
|
||||
err => {
|
||||
subscriber.error(err);
|
||||
if (task) {
|
||||
this.appRef.tick();
|
||||
task.invoke();
|
||||
}
|
||||
},
|
||||
() => {
|
||||
subscriber.complete();
|
||||
if (task) {
|
||||
this.appRef.tick();
|
||||
task.invoke();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
subscription.unsubscribe();
|
||||
if (task) {
|
||||
this.appRef.tick();
|
||||
task.invoke();
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user