Merge branch 'master' into fix-unfurler-stray-slashes
This commit is contained in:
commit
23a4ab461e
@ -4,7 +4,7 @@ import logger from '../logger';
|
||||
import { Common } from './common';
|
||||
|
||||
class DatabaseMigration {
|
||||
private static currentVersion = 40;
|
||||
private static currentVersion = 41;
|
||||
private queryTimeout = 120000;
|
||||
private statisticsAddedIndexed = false;
|
||||
private uniqueLogs: string[] = [];
|
||||
@ -348,6 +348,10 @@ class DatabaseMigration {
|
||||
await this.$executeQuery('ALTER TABLE `nodes` ADD channels int(11) unsigned DEFAULT NULL');
|
||||
await this.$executeQuery('ALTER TABLE `nodes` ADD INDEX `capacity` (`capacity`);');
|
||||
}
|
||||
|
||||
if (databaseSchemaVersion < 41 && isBitcoin === true) {
|
||||
await this.$executeQuery('UPDATE channels SET closing_reason = NULL WHERE closing_reason = 1');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -289,6 +289,24 @@ class NetworkSyncService {
|
||||
1. Mutually closed
|
||||
2. Forced closed
|
||||
3. Forced closed with penalty
|
||||
|
||||
┌────────────────────────────────────┐ ┌────────────────────────────┐
|
||||
│ outputs contain revocation script? ├──yes──► force close w/ penalty = 3 │
|
||||
└──────────────┬─────────────────────┘ └────────────────────────────┘
|
||||
no
|
||||
┌──────────────▼──────────────────────────┐
|
||||
│ outputs contain other lightning script? ├──┐
|
||||
└──────────────┬──────────────────────────┘ │
|
||||
no yes
|
||||
┌──────────────▼─────────────┐ │
|
||||
│ sequence starts with 0x80 │ ┌────────▼────────┐
|
||||
│ and ├──────► force close = 2 │
|
||||
│ locktime starts with 0x20? │ └─────────────────┘
|
||||
└──────────────┬─────────────┘
|
||||
no
|
||||
┌─────────▼────────┐
|
||||
│ mutual close = 1 │
|
||||
└──────────────────┘
|
||||
*/
|
||||
|
||||
private async $runClosedChannelsForensics(): Promise<void> {
|
||||
@ -326,36 +344,31 @@ class NetworkSyncService {
|
||||
lightningScriptReasons.push(lightningScript);
|
||||
}
|
||||
}
|
||||
if (lightningScriptReasons.length === outspends.length
|
||||
&& lightningScriptReasons.filter((r) => r === 1).length === outspends.length) {
|
||||
reason = 1;
|
||||
} else {
|
||||
const filteredReasons = lightningScriptReasons.filter((r) => r !== 1);
|
||||
if (filteredReasons.length) {
|
||||
if (filteredReasons.some((r) => r === 2 || r === 4)) {
|
||||
reason = 3;
|
||||
} else {
|
||||
reason = 2;
|
||||
}
|
||||
const filteredReasons = lightningScriptReasons.filter((r) => r !== 1);
|
||||
if (filteredReasons.length) {
|
||||
if (filteredReasons.some((r) => r === 2 || r === 4)) {
|
||||
reason = 3;
|
||||
} else {
|
||||
/*
|
||||
We can detect a commitment transaction (force close) by reading Sequence and Locktime
|
||||
https://github.com/lightning/bolts/blob/master/03-transactions.md#commitment-transaction
|
||||
*/
|
||||
let closingTx: IEsploraApi.Transaction | undefined;
|
||||
try {
|
||||
closingTx = await bitcoinApi.$getRawTransaction(channel.closing_transaction_id);
|
||||
} catch (e) {
|
||||
logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + channel.closing_transaction_id}. Reason ${e instanceof Error ? e.message : e}`);
|
||||
continue;
|
||||
}
|
||||
const sequenceHex: string = closingTx.vin[0].sequence.toString(16);
|
||||
const locktimeHex: string = closingTx.locktime.toString(16);
|
||||
if (sequenceHex.substring(0, 2) === '80' && locktimeHex.substring(0, 2) === '20') {
|
||||
reason = 2; // Here we can't be sure if it's a penalty or not
|
||||
} else {
|
||||
reason = 1;
|
||||
}
|
||||
reason = 2;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
We can detect a commitment transaction (force close) by reading Sequence and Locktime
|
||||
https://github.com/lightning/bolts/blob/master/03-transactions.md#commitment-transaction
|
||||
*/
|
||||
let closingTx: IEsploraApi.Transaction | undefined;
|
||||
try {
|
||||
closingTx = await bitcoinApi.$getRawTransaction(channel.closing_transaction_id);
|
||||
} catch (e) {
|
||||
logger.err(`Failed to call ${config.ESPLORA.REST_API_URL + '/tx/' + channel.closing_transaction_id}. Reason ${e instanceof Error ? e.message : e}`);
|
||||
continue;
|
||||
}
|
||||
const sequenceHex: string = closingTx.vin[0].sequence.toString(16);
|
||||
const locktimeHex: string = closingTx.locktime.toString(16);
|
||||
if (sequenceHex.substring(0, 2) === '80' && locktimeHex.substring(0, 2) === '20') {
|
||||
reason = 2; // Here we can't be sure if it's a penalty or not
|
||||
} else {
|
||||
reason = 1;
|
||||
}
|
||||
}
|
||||
if (reason) {
|
||||
|
@ -11,11 +11,15 @@
|
||||
[showZoom]="false"
|
||||
></app-mempool-graph>
|
||||
</div>
|
||||
<div class="blockchain-wrapper">
|
||||
<div class="blockchain-wrapper" [dir]="timeLtr ? 'rtl' : 'ltr'" [class.time-ltr]="timeLtr">
|
||||
<div class="position-container">
|
||||
<app-mempool-blocks></app-mempool-blocks>
|
||||
<app-blockchain-blocks></app-blockchain-blocks>
|
||||
<div id="divider"></div>
|
||||
<span>
|
||||
<div class="blocks-wrapper">
|
||||
<app-mempool-blocks></app-mempool-blocks>
|
||||
<app-blockchain-blocks></app-blockchain-blocks>
|
||||
</div>
|
||||
<div id="divider"></div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,8 +31,9 @@
|
||||
|
||||
.position-container {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
left: 0;
|
||||
bottom: 170px;
|
||||
transform: translateX(50vw);
|
||||
}
|
||||
|
||||
#divider {
|
||||
@ -47,9 +48,33 @@
|
||||
top: -28px;
|
||||
}
|
||||
}
|
||||
|
||||
&.time-ltr {
|
||||
.blocks-wrapper {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.ltr-layout) {
|
||||
.blockchain-wrapper.time-ltr .blocks-wrapper,
|
||||
.blockchain-wrapper .blocks-wrapper {
|
||||
direction: ltr;
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.rtl-layout) {
|
||||
.blockchain-wrapper.time-ltr .blocks-wrapper,
|
||||
.blockchain-wrapper .blocks-wrapper {
|
||||
direction: rtl;
|
||||
}
|
||||
}
|
||||
|
||||
.tv-container {
|
||||
display: flex;
|
||||
margin-top: 0px;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { WebsocketService } from '../../services/websocket.service';
|
||||
import { OptimizedMempoolStats } from '../../interfaces/node-api.interface';
|
||||
import { StateService } from '../../services/state.service';
|
||||
@ -6,7 +6,7 @@ import { ApiService } from '../../services/api.service';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { map, scan, startWith, switchMap, tap } from 'rxjs/operators';
|
||||
import { interval, merge, Observable } from 'rxjs';
|
||||
import { interval, merge, Observable, Subscription } from 'rxjs';
|
||||
import { ChangeDetectionStrategy } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
@ -15,11 +15,13 @@ import { ChangeDetectionStrategy } from '@angular/core';
|
||||
styleUrls: ['./television.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class TelevisionComponent implements OnInit {
|
||||
export class TelevisionComponent implements OnInit, OnDestroy {
|
||||
|
||||
mempoolStats: OptimizedMempoolStats[] = [];
|
||||
statsSubscription$: Observable<OptimizedMempoolStats[]>;
|
||||
fragment: string;
|
||||
timeLtrSubscription: Subscription;
|
||||
timeLtr: boolean = this.stateService.timeLtr.value;
|
||||
|
||||
constructor(
|
||||
private websocketService: WebsocketService,
|
||||
@ -37,6 +39,10 @@ export class TelevisionComponent implements OnInit {
|
||||
this.seoService.setTitle($localize`:@@46ce8155c9ab953edeec97e8950b5a21e67d7c4e:TV view`);
|
||||
this.websocketService.want(['blocks', 'live-2h-chart', 'mempool-blocks']);
|
||||
|
||||
this.timeLtrSubscription = this.stateService.timeLtr.subscribe((ltr) => {
|
||||
this.timeLtr = !!ltr;
|
||||
});
|
||||
|
||||
this.statsSubscription$ = merge(
|
||||
this.stateService.live2Chart$.pipe(map(stats => [stats])),
|
||||
this.route.fragment
|
||||
@ -70,4 +76,8 @@ export class TelevisionComponent implements OnInit {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.timeLtrSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
@ -210,8 +210,6 @@
|
||||
[network]="network"
|
||||
[tooltip]="true"
|
||||
[inputIndex]="inputIndex" [outputIndex]="outputIndex"
|
||||
(selectInput)="selectInput($event)"
|
||||
(selectOutput)="selectOutput($event)"
|
||||
>
|
||||
</tx-bowtie-graph>
|
||||
</div>
|
||||
|
@ -18,6 +18,7 @@ import { ApiService } from '../../services/api.service';
|
||||
import { SeoService } from '../../services/seo.service';
|
||||
import { BlockExtended, CpfpInfo } from '../../interfaces/node-api.interface';
|
||||
import { LiquidUnblinding } from './liquid-ublinding';
|
||||
import { RelativeUrlPipe } from '../../shared/pipes/relative-url/relative-url.pipe';
|
||||
|
||||
@Component({
|
||||
selector: 'app-transaction',
|
||||
@ -40,6 +41,8 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
txReplacedSubscription: Subscription;
|
||||
blocksSubscription: Subscription;
|
||||
queryParamsSubscription: Subscription;
|
||||
urlFragmentSubscription: Subscription;
|
||||
fragmentParams: URLSearchParams;
|
||||
rbfTransaction: undefined | Transaction;
|
||||
cpfpInfo: CpfpInfo | null;
|
||||
showCpfpDetails = false;
|
||||
@ -67,6 +70,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private relativeUrlPipe: RelativeUrlPipe,
|
||||
private electrsApiService: ElectrsApiService,
|
||||
private stateService: StateService,
|
||||
private websocketService: WebsocketService,
|
||||
@ -93,6 +97,14 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
map((da) => da.timeAvg)
|
||||
);
|
||||
|
||||
this.urlFragmentSubscription = this.route.fragment.subscribe((fragment) => {
|
||||
this.fragmentParams = new URLSearchParams(fragment || '');
|
||||
const vin = parseInt(this.fragmentParams.get('vin'), 10);
|
||||
const vout = parseInt(this.fragmentParams.get('vout'), 10);
|
||||
this.inputIndex = (!isNaN(vin) && vin >= 0) ? vin : null;
|
||||
this.outputIndex = (!isNaN(vout) && vout >= 0) ? vout : null;
|
||||
});
|
||||
|
||||
this.fetchCpfpSubscription = this.fetchCpfp$
|
||||
.pipe(
|
||||
switchMap((txId) =>
|
||||
@ -132,13 +144,29 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
switchMap((params: ParamMap) => {
|
||||
const urlMatch = (params.get('id') || '').split(':');
|
||||
if (urlMatch.length === 2 && urlMatch[1].length === 64) {
|
||||
this.inputIndex = parseInt(urlMatch[0], 10);
|
||||
this.outputIndex = null;
|
||||
const vin = parseInt(urlMatch[0], 10);
|
||||
this.txId = urlMatch[1];
|
||||
// rewrite legacy vin syntax
|
||||
if (!isNaN(vin)) {
|
||||
this.fragmentParams.set('vin', vin.toString());
|
||||
this.fragmentParams.delete('vout');
|
||||
}
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.txId], {
|
||||
queryParamsHandling: 'merge',
|
||||
fragment: this.fragmentParams.toString(),
|
||||
});
|
||||
} else {
|
||||
this.txId = urlMatch[0];
|
||||
this.outputIndex = urlMatch[1] === undefined ? null : parseInt(urlMatch[1], 10);
|
||||
this.inputIndex = null;
|
||||
const vout = parseInt(urlMatch[1], 10);
|
||||
if (urlMatch.length > 1 && !isNaN(vout)) {
|
||||
// rewrite legacy vout syntax
|
||||
this.fragmentParams.set('vout', vout.toString());
|
||||
this.fragmentParams.delete('vin');
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.txId], {
|
||||
queryParamsHandling: 'merge',
|
||||
fragment: this.fragmentParams.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
this.seoService.setTitle(
|
||||
$localize`:@@bisq.transaction.browser-title:Transaction: ${this.txId}:INTERPOLATION:`
|
||||
@ -222,6 +250,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.fetchCpfp$.next(this.tx.txid);
|
||||
}
|
||||
}
|
||||
setTimeout(() => { this.applyFragment(); }, 0);
|
||||
},
|
||||
(error) => {
|
||||
this.error = error;
|
||||
@ -359,14 +388,15 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.graphExpanded = false;
|
||||
}
|
||||
|
||||
selectInput(input) {
|
||||
this.inputIndex = input;
|
||||
this.outputIndex = null;
|
||||
}
|
||||
|
||||
selectOutput(output) {
|
||||
this.outputIndex = output;
|
||||
this.inputIndex = null;
|
||||
// simulate normal anchor fragment behavior
|
||||
applyFragment(): void {
|
||||
const anchor = Array.from(this.fragmentParams.entries()).find(([frag, value]) => value === '');
|
||||
if (anchor) {
|
||||
const anchorElement = document.getElementById(anchor[0]);
|
||||
if (anchorElement) {
|
||||
anchorElement.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
@ -383,6 +413,7 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
this.blocksSubscription.unsubscribe();
|
||||
this.queryParamsSubscription.unsubscribe();
|
||||
this.flowPrefSubscription.unsubscribe();
|
||||
this.urlFragmentSubscription.unsubscribe();
|
||||
this.leaveTransaction();
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
<ng-template #defaultPrevout>
|
||||
<a [routerLink]="['/tx/' | relativeUrl, vin.txid + ':' + vin.vout]" class="red">
|
||||
<a [routerLink]="['/tx/' | relativeUrl, vin.txid]" [fragment]="'vout=' + vin.vout" class="red">
|
||||
<fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
|
||||
</a>
|
||||
</ng-template>
|
||||
@ -220,7 +220,7 @@
|
||||
<fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
|
||||
</span>
|
||||
<ng-template #spent>
|
||||
<a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].vin + ':' + tx._outspends[vindex].txid]" class="red">
|
||||
<a *ngIf="tx._outspends[vindex].txid else outputNoTxId" [routerLink]="['/tx/' | relativeUrl, tx._outspends[vindex].txid]" [fragment]="'vin=' + tx._outspends[vindex].vin" class="red">
|
||||
<fa-icon [icon]="['fas', 'arrow-alt-circle-right']" [fixedWidth]="true"></fa-icon>
|
||||
</a>
|
||||
<ng-template #outputNoTxId>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Component, OnInit, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core';
|
||||
import { Component, OnInit, Input, OnChanges, HostListener } from '@angular/core';
|
||||
import { StateService } from '../../services/state.service';
|
||||
import { Outspend, Transaction } from '../../interfaces/electrs.interface';
|
||||
import { Router } from '@angular/router';
|
||||
@ -43,9 +43,6 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
||||
@Input() inputIndex: number;
|
||||
@Input() outputIndex: number;
|
||||
|
||||
@Output() selectInput = new EventEmitter<number>();
|
||||
@Output() selectOutput = new EventEmitter<number>();
|
||||
|
||||
inputData: Xput[];
|
||||
outputData: Xput[];
|
||||
inputs: SvgLine[];
|
||||
@ -368,24 +365,42 @@ export class TxBowtieGraphComponent implements OnInit, OnChanges {
|
||||
onClick(event, side, index): void {
|
||||
if (side === 'input') {
|
||||
const input = this.tx.vin[index];
|
||||
if (input && input.txid && input.vout != null) {
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid + ':' + input.vout], {
|
||||
if (input && !input.is_coinbase && !input.is_pegin && input.txid && input.vout != null) {
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), input.txid], {
|
||||
queryParamsHandling: 'merge',
|
||||
fragment: 'flow'
|
||||
fragment: (new URLSearchParams({
|
||||
flow: '',
|
||||
vout: input.vout.toString(),
|
||||
})).toString(),
|
||||
});
|
||||
} else if (index != null) {
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.tx.txid], {
|
||||
queryParamsHandling: 'merge',
|
||||
fragment: (new URLSearchParams({
|
||||
flow: '',
|
||||
vin: index.toString(),
|
||||
})).toString(),
|
||||
});
|
||||
} else {
|
||||
this.selectInput.emit(index);
|
||||
}
|
||||
} else {
|
||||
const output = this.tx.vout[index];
|
||||
const outspend = this.outspends[index];
|
||||
if (output && outspend && outspend.spent && outspend.txid) {
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.vin + ':' + outspend.txid], {
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), outspend.txid], {
|
||||
queryParamsHandling: 'merge',
|
||||
fragment: 'flow'
|
||||
fragment: (new URLSearchParams({
|
||||
flow: '',
|
||||
vin: outspend.vin.toString(),
|
||||
})).toString(),
|
||||
});
|
||||
} else if (index != null) {
|
||||
this.router.navigate([this.relativeUrlPipe.transform('/tx'), this.tx.txid], {
|
||||
queryParamsHandling: 'merge',
|
||||
fragment: (new URLSearchParams({
|
||||
flow: '',
|
||||
vout: index.toString(),
|
||||
})).toString(),
|
||||
});
|
||||
} else {
|
||||
this.selectOutput.emit(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,10 @@
|
||||
<ng-template #notFullyTaproot>
|
||||
<span *ngIf="segwitGains.realizedTaprootGains && segwitGains.potentialTaprootGains; else noTaproot" class="badge badge-warning mr-1" i18n-ngbTooltip="Tooltip about fees that saved and could be saved with taproot" ngbTooltip="This transaction uses Taproot and already saved at least {{ segwitGains.realizedTaprootGains * 100 | number: '1.0-0' }}% on fees, but could save an additional {{ segwitGains.potentialTaprootGains * 100 | number: '1.0-0' }}% by fully using Taproot" placement="bottom" i18n="tx-features.tag.taproot|Taproot">Taproot</span>
|
||||
<ng-template #noTaproot>
|
||||
<span *ngIf="segwitGains.potentialTaprootGains; else taprootButNoGains" class="badge badge-danger mr-1" i18n-ngbTooltip="Tooltip about fees that could be saved with taproot" ngbTooltip="This transaction could save {{ segwitGains.potentialTaprootGains * 100 | number: '1.0-0' }}% on fees by using Taproot" placement="bottom"><del i18n="tx-features.tag.taproot|Taproot">Taproot</del></span>
|
||||
<span *ngIf="segwitGains.potentialTaprootGains && segwitGains.potentialTaprootGains > 0; else negativeTaprootGains" class="badge badge-danger mr-1" i18n-ngbTooltip="Tooltip about fees that could be saved with taproot" ngbTooltip="This transaction could save {{ segwitGains.potentialTaprootGains * 100 | number: '1.0-0' }}% on fees by using Taproot" placement="bottom"><del i18n="tx-features.tag.taproot|Taproot">Taproot</del></span>
|
||||
<ng-template #negativeTaprootGains>
|
||||
<span *ngIf="!isTaproot; else taprootButNoGains" class="badge badge-danger mr-1" i18n-ngbTooltip="Tooltip about using taproot" ngbTooltip="This transaction does not use Taproot" placement="bottom"><del i18n="tx-features.tag.taproot|Taproot">Taproot</del></span>
|
||||
</ng-template>
|
||||
<ng-template #taprootButNoGains>
|
||||
<span *ngIf="isTaproot" class="badge badge-success mr-1" i18n-ngbTooltip="Tooltip about taproot" ngbTooltip="This transaction uses Taproot" placement="bottom" i18n="tx-features.tag.taproot|Taproot">Taproot</span>
|
||||
</ng-template>
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6180
frontend/src/locale/messages.lt.xlf
Normal file
6180
frontend/src/locale/messages.lt.xlf
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -16,7 +16,8 @@
|
||||
],
|
||||
"lib": [
|
||||
"es2018",
|
||||
"dom"
|
||||
"dom",
|
||||
"dom.iterable"
|
||||
]
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user