Merge branch 'master' into hunicus/msop-r

This commit is contained in:
wiz
2023-07-28 17:29:25 +09:00
committed by GitHub
250 changed files with 20681 additions and 9896 deletions

View File

@@ -5,12 +5,15 @@
<ng-template #confirmationPlural let-i i18n="shared.confirmation-count.plural|Transaction plural confirmation count">{{ i }} confirmations</ng-template>
</button>
</ng-template>
<ng-template [ngIf]="!confirmations && height != null">
<button type="button" class="btn btn-sm btn-success {{buttonClass}}" i18n="transaction.confirmed|Transaction confirmed state">Confirmed</button>
</ng-template>
<ng-template [ngIf]="!hideUnconfirmed && !confirmations && replaced">
<button type="button" class="btn btn-sm btn-warning {{buttonClass}}" i18n="transaction.replaced|Transaction replaced state">Replaced</button>
</ng-template>
<ng-template [ngIf]="!hideUnconfirmed && !confirmations && !replaced && removed">
<button type="button" class="btn btn-sm btn-warning {{buttonClass}}" i18n="transaction.audit.removed|Transaction removed state">Removed</button>
</ng-template>
<ng-template [ngIf]="!hideUnconfirmed && !confirmations && !replaced && !removed">
<ng-template [ngIf]="!hideUnconfirmed && chainTip != null && !confirmations && !replaced && !removed">
<button type="button" class="btn btn-sm btn-danger {{buttonClass}}" i18n="transaction.unconfirmed|Transaction unconfirmed state">Unconfirmed</button>
</ng-template>

View File

@@ -0,0 +1,4 @@
<ng-container *ngIf="rateUnits$ | async as units">
<ng-container *ngIf="units !== 'wu'">{{ fee / (weight / 4) | feeRounding:rounding }} <span *ngIf="showUnit" [class]="unitClass" [style]="unitStyle">sat/vB</span></ng-container>
<ng-container *ngIf="units === 'wu'">{{ fee / weight | feeRounding:rounding }} <span *ngIf="showUnit" [class]="unitClass" [style]="unitStyle">sat/WU</span></ng-container>
</ng-container>

View File

@@ -0,0 +1,27 @@
import { Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { StateService } from '../../../services/state.service';
@Component({
selector: 'app-fee-rate',
templateUrl: './fee-rate.component.html',
styleUrls: ['./fee-rate.component.scss']
})
export class FeeRateComponent implements OnInit {
@Input() fee: number;
@Input() weight: number = 4;
@Input() rounding: string = null;
@Input() showUnit: boolean = true;
@Input() unitClass: string = 'symbol';
@Input() unitStyle: any;
rateUnits$: Observable<string>;
constructor(
private stateService: StateService,
) { }
ngOnInit() {
this.rateUnits$ = this.stateService.rateUnits$;
}
}

View File

@@ -10,6 +10,9 @@
<div class="selector">
<app-fiat-selector></app-fiat-selector>
</div>
<div class="selector">
<app-rate-unit-selector></app-rate-unit-selector>
</div>
<ng-template #temporaryHidden>
<a *ngIf="officialMempoolSpace" class="cta btn btn-purple sponsor" [routerLink]="['/signup' | relativeUrl]">Support the Project</a>
<p *ngIf="officialMempoolSpace && env.BASE_MODULE === 'mempool'" class="cta-secondary"><a [routerLink]="['/signin' | relativeUrl]" i18n="shared.broadcast-transaction|Broadcast Transaction">Sign In</a></p>

View File

@@ -2,6 +2,6 @@
<span *ngIf="seconds !== undefined">
&lrm;{{ seconds * 1000 | date: customFormat ?? 'yyyy-MM-dd HH:mm' }}
<div class="lg-inline" *ngIf="!hideTimeSince">
<i class="symbol">(<app-time kind="since" [time]="seconds" [fastRender]="true"></app-time>)</i>
<i class="symbol">(<app-time kind="since" [time]="seconds" [fastRender]="true" [precision]="precision" [minUnit]="minUnit"></app-time>)</i>
</div>
</span>

View File

@@ -11,6 +11,8 @@ export class TimestampComponent implements OnChanges {
@Input() dateString: string;
@Input() customFormat: string;
@Input() hideTimeSince: boolean = false;
@Input() precision: number = 0;
@Input() minUnit: 'year' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' = 'second';
seconds: number | undefined = undefined;

View File

@@ -1,4 +1,4 @@
<span class="truncate" [style.max-width]="maxWidth ? maxWidth + 'px' : null">
<span class="truncate" [style.max-width]="maxWidth ? maxWidth + 'px' : null" [style.justify-content]="textAlign" [class.inline]="inline">
<ng-container *ngIf="link">
<a [routerLink]="link" class="truncate-link">
<ng-container *ngIf="rtl; then rtlTruncated; else ltrTruncated;"></ng-container>

View File

@@ -23,4 +23,8 @@
flex-shrink: 0;
flex-grow: 0;
}
&.inline {
display: inline-flex;
}
}

View File

@@ -11,6 +11,8 @@ export class TruncateComponent {
@Input() link: any = null;
@Input() lastChars: number = 4;
@Input() maxWidth: number = null;
@Input() inline: boolean = false;
@Input() textAlign: 'start' | 'end' = 'start';
rtl: boolean;
constructor(

View File

@@ -0,0 +1,45 @@
import { Directive, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core';
import { Subscription } from 'rxjs';
import { StateService } from '../../../services/state.service';
function createRateUnitDirective(checkFn: (rateUnit: string) => boolean): any {
@Directive()
class RateUnitDirective implements OnDestroy {
private subscription: Subscription;
private enabled: boolean;
private hasView: boolean = false;
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private stateService: StateService
) {
this.subscription = this.stateService.rateUnits$.subscribe(rateUnit => {
this.enabled = checkFn(rateUnit);
this.updateView();
});
}
updateView(): void {
if (this.enabled && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (!this.enabled && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
return RateUnitDirective;
}
@Directive({ selector: '[only-vsize]' })
export class OnlyVsizeDirective extends createRateUnitDirective(rateUnit => rateUnit !== 'wu') {}
@Directive({ selector: '[only-weight]' })
export class OnlyWeightDirective extends createRateUnitDirective(rateUnit => rateUnit === 'wu') {}

View File

@@ -0,0 +1,28 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Pipe({
name: 'bitcoinsatoshis'
})
export class BitcoinsatoshisPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(value: string): SafeHtml {
const newValue = this.insertSpaces(parseFloat(value || '0').toFixed(8));
const position = (newValue || '0').search(/[1-9]/);
const firstPart = newValue.slice(0, position);
const secondPart = newValue.slice(position);
return this.sanitizer.bypassSecurityTrustHtml(
`<span class="text-secondary">${firstPart}</span>${secondPart}`
);
}
insertSpaces(str: string): string {
const length = str.length;
return str.slice(0, length - 6) + ' ' + str.slice(length - 6, length - 3) + ' ' + str.slice(length - 3);
}
}

View File

@@ -309,3 +309,28 @@ export function takeWhile(input: any[], predicate: CollectionPredicate) {
return takeUntil(input, (item: any, index: number | undefined, collection: any[] | undefined) =>
!predicate(item, index, collection));
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent
export function hasTouchScreen(): boolean {
let hasTouchScreen = false;
if ('maxTouchPoints' in navigator) {
hasTouchScreen = navigator.maxTouchPoints > 0;
} else if ('msMaxTouchPoints' in navigator) {
// @ts-ignore
hasTouchScreen = navigator.msMaxTouchPoints > 0;
} else {
const mQ = matchMedia?.('(pointer:coarse)');
if (mQ?.media === '(pointer:coarse)') {
hasTouchScreen = !!mQ.matches;
} else if ('orientation' in window) {
hasTouchScreen = true; // deprecated, but good fallback
} else {
// @ts-ignore - Only as a last resort, fall back to user agent sniffing
const UA = navigator.userAgent;
hasTouchScreen =
/\b(BlackBerry|webOS|iPhone|IEMobile)\b/i.test(UA) ||
/\b(Android|Windows Phone|iPad|iPod)\b/i.test(UA);
}
}
return hasTouchScreen;
}

View File

@@ -17,7 +17,7 @@ export class WuBytesPipe implements PipeTransform {
'TWU': {max: Number.MAX_SAFE_INTEGER, prev: 'GWU'}
};
transform(input: any, decimal: number = 0, from: ByteUnit = 'WU', to?: ByteUnit): any {
transform(input: any, decimal: number = 0, from: ByteUnit = 'WU', to?: ByteUnit, plainText?: boolean): any {
if (!(isNumberFinite(input) &&
isNumberFinite(decimal) &&
@@ -38,7 +38,7 @@ export class WuBytesPipe implements PipeTransform {
const result = toDecimal(WuBytesPipe.calculateResult(format, bytes), decimal);
return WuBytesPipe.formatResult(result, to);
return WuBytesPipe.formatResult(result, to, plainText);
}
for (const key in WuBytesPipe.formats) {
@@ -47,12 +47,15 @@ export class WuBytesPipe implements PipeTransform {
const result = toDecimal(WuBytesPipe.calculateResult(format, bytes), decimal);
return WuBytesPipe.formatResult(result, key);
return WuBytesPipe.formatResult(result, key, plainText);
}
}
}
static formatResult(result: number, unit: string): string {
static formatResult(result: number, unit: string, plainText: boolean): string {
if (plainText){
return `${result} ${unit}`;
}
return `${result} <span class="symbol">${unit}</span>`;
}

View File

@@ -9,7 +9,11 @@ export class FeeRoundingPipe implements PipeTransform {
@Inject(LOCALE_ID) private locale: string,
) {}
transform(fee: number): string {
transform(fee: number, rounding = null): string {
if (rounding) {
return formatNumber(fee, this.locale, rounding);
}
if (fee >= 100) {
return formatNumber(fee, this.locale, '1.0-0')
} else if (fee < 10) {

View File

@@ -35,6 +35,7 @@ import { TxFeeRatingComponent } from '../components/tx-fee-rating/tx-fee-rating.
import { ReactiveFormsModule } from '@angular/forms';
import { LanguageSelectorComponent } from '../components/language-selector/language-selector.component';
import { FiatSelectorComponent } from '../components/fiat-selector/fiat-selector.component';
import { RateUnitSelectorComponent } from '../components/rate-unit-selector/rate-unit-selector.component';
import { ColoredPriceDirective } from './directives/colored-price.directive';
import { NoSanitizePipe } from './pipes/no-sanitize.pipe';
import { MempoolBlocksComponent } from '../components/mempool-blocks/mempool-blocks.component';
@@ -82,6 +83,7 @@ import { IndexingProgressComponent } from '../components/indexing-progress/index
import { SvgImagesComponent } from '../components/svg-images/svg-images.component';
import { ChangeComponent } from '../components/change/change.component';
import { SatsComponent } from './components/sats/sats.component';
import { FeeRateComponent } from './components/fee-rate/fee-rate.component';
import { TruncateComponent } from './components/truncate/truncate.component';
import { SearchResultsComponent } from '../components/search-form/search-results/search-results.component';
import { TimestampComponent } from './components/timestamp/timestamp.component';
@@ -95,6 +97,10 @@ import { MempoolBlockOverviewComponent } from '../components/mempool-block-overv
import { ClockchainComponent } from '../components/clockchain/clockchain.component';
import { ClockFaceComponent } from '../components/clock-face/clock-face.component';
import { ClockComponent } from '../components/clock/clock.component';
import { CalculatorComponent } from '../components/calculator/calculator.component';
import { BitcoinsatoshisPipe } from '../shared/pipes/bitcoinsatoshis.pipe';
import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-directives/weight-directives';
@NgModule({
declarations: [
@@ -106,6 +112,7 @@ import { ClockComponent } from '../components/clock/clock.component';
TxFeeRatingComponent,
LanguageSelectorComponent,
FiatSelectorComponent,
RateUnitSelectorComponent,
ScriptpubkeyTypePipe,
RelativeUrlPipe,
NoSanitizePipe,
@@ -171,6 +178,7 @@ import { ClockComponent } from '../components/clock/clock.component';
SvgImagesComponent,
ChangeComponent,
SatsComponent,
FeeRateComponent,
TruncateComponent,
SearchResultsComponent,
TimestampComponent,
@@ -179,11 +187,14 @@ import { ClockComponent } from '../components/clock/clock.component';
GeolocationComponent,
TestnetAlertComponent,
GlobalFooterComponent,
CalculatorComponent,
BitcoinsatoshisPipe,
MempoolBlockOverviewComponent,
ClockchainComponent,
ClockComponent,
ClockFaceComponent,
OnlyVsizeDirective,
OnlyWeightDirective
],
imports: [
CommonModule,
@@ -200,6 +211,7 @@ import { ClockComponent } from '../components/clock/clock.component';
],
providers: [
VbytesPipe,
WuBytesPipe,
RelativeUrlPipe,
NoSanitizePipe,
ShortenStringPipe,
@@ -225,6 +237,7 @@ import { ClockComponent } from '../components/clock/clock.component';
TxFeeRatingComponent,
LanguageSelectorComponent,
FiatSelectorComponent,
RateUnitSelectorComponent,
ScriptpubkeyTypePipe,
RelativeUrlPipe,
Hex2asciiPipe,
@@ -284,6 +297,7 @@ import { ClockComponent } from '../components/clock/clock.component';
SvgImagesComponent,
ChangeComponent,
SatsComponent,
FeeRateComponent,
TruncateComponent,
SearchResultsComponent,
TimestampComponent,
@@ -297,6 +311,9 @@ import { ClockComponent } from '../components/clock/clock.component';
ClockchainComponent,
ClockComponent,
ClockFaceComponent,
OnlyVsizeDirective,
OnlyWeightDirective
]
})
export class SharedModule {