Merge pull request #5091 from mempool/mononaut/faucet-ui-changes
Requested changes to testnet4 faucet UI
This commit is contained in:
commit
2341b1d79e
@ -13,16 +13,16 @@
|
|||||||
<a class="text-primary" [href]="'/testnet4/tx/' + txid">{{ txid }}</a>
|
<a class="text-primary" [href]="'/testnet4/tx/' + txid">{{ txid }}</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@else if (loading) {
|
@if (loading) {
|
||||||
<p>Loading faucet...</p>
|
<p>Loading faucet...</p>
|
||||||
<div class="spinner-border text-light"></div>
|
<div class="spinner-border text-light"></div>
|
||||||
} @else if (!user) {
|
} @else if (!user) {
|
||||||
<!-- User not logged in -->
|
<!-- User not logged in -->
|
||||||
<div class="alert alert-mempool d-block text-center w-100">
|
<div class="alert alert-mempool d-block text-center w-100">
|
||||||
<div class="d-inline align-middle">
|
<div class="d-inline align-middle">
|
||||||
<span>To limit abuse, </span>
|
<span>To use the faucet, please </span>
|
||||||
<a routerLink="/login" [queryParams]="{'redirectTo': '/testnet4/faucet'}">authenticate </a>
|
<a routerLink="/login" [queryParams]="{'redirectTo': '/testnet4/faucet'}">login</a>
|
||||||
<span class="mr-2">or</span>
|
<span class="mr-2"> or</span>
|
||||||
</div>
|
</div>
|
||||||
<app-twitter-login customClass="btn btn-sm" width="220px" redirectTo="/testnet4/faucet" buttonString="Sign up with Twitter"></app-twitter-login>
|
<app-twitter-login customClass="btn btn-sm" width="220px" redirectTo="/testnet4/faucet" buttonString="Sign up with Twitter"></app-twitter-login>
|
||||||
</div>
|
</div>
|
||||||
@ -31,19 +31,18 @@
|
|||||||
<!-- User logged in but not a paid user or did not link its Twitter account -->
|
<!-- User logged in but not a paid user or did not link its Twitter account -->
|
||||||
<div class="alert alert-mempool d-block text-center w-100">
|
<div class="alert alert-mempool d-block text-center w-100">
|
||||||
<div class="d-inline align-middle">
|
<div class="d-inline align-middle">
|
||||||
<span class="mb-2 mr-2">To limit abuse</span>
|
<span class="mb-2 mr-2">To use the faucet, please</span>
|
||||||
</div>
|
</div>
|
||||||
<app-twitter-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your Twitter"></app-twitter-login>
|
<app-twitter-login customClass="btn btn-sm" width="180px" redirectTo="/testnet4/faucet" buttonString="Link your Twitter"></app-twitter-login>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@else if (error) {
|
@else if (error) {
|
||||||
<!-- User can request -->
|
<!-- User can request -->
|
||||||
<app-mempool-error class="w-100" [error]="error"></app-mempool-error>
|
<app-mempool-error class="w-100" [error]="error"></app-mempool-error>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!loading) {
|
@if (!loading) {
|
||||||
<form [formGroup]="faucetForm" class="formGroup" (submit)="requestCoins()" [style]="(error || !this.user) ? 'opacity: 0.3; pointer-events: none' : ''">
|
<form [formGroup]="faucetForm" class="formGroup" (submit)="requestCoins()">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="form-group mb-0">
|
<div class="form-group mb-0">
|
||||||
@ -68,7 +67,7 @@
|
|||||||
<span class="input-group-text" i18n="address">Address</span>
|
<span class="input-group-text" i18n="address">Address</span>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" class="form-control" [class]="{invalid: invalidAddress}" formControlName="address" id="address" placeholder="tb1q...">
|
<input type="text" class="form-control" [class]="{invalid: invalidAddress}" formControlName="address" id="address" placeholder="tb1q...">
|
||||||
<button type="submit" class="btn btn-primary submit-button" [disabled]="!faucetForm.valid || !faucetForm.get('address')?.dirty" i18n="testnet4.request-coins">Request Testnet4 Coins</button>
|
<button type="submit" class="btn btn-primary submit-button" [disabled]="!faucetForm.valid || !faucetForm.get('address')?.dirty || isDisabled()" i18n="testnet4.request-coins">Request Testnet4 Coins</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-danger text-left" *ngIf="invalidAddress">
|
<div class="text-danger text-left" *ngIf="invalidAddress">
|
||||||
<div *ngIf="address?.errors?.['required']">Address is required</div>
|
<div *ngIf="address?.errors?.['required']">Address is required</div>
|
||||||
@ -83,7 +82,7 @@
|
|||||||
|
|
||||||
<!-- Send back coins -->
|
<!-- Send back coins -->
|
||||||
@if (status?.address) {
|
@if (status?.address) {
|
||||||
<div class="mt-2 alert alert-info w-100">If you no longer need your testnet4 coins, please consider <a class="text-primary" [routerLink]="['/address/' | relativeUrl, status.address]"><u>sending them back</u></a> to replenish the faucet.</div>
|
<div class="mt-4 alert alert-info w-100">If you no longer need your testnet4 coins, please consider <a class="text-primary" [routerLink]="['/address/' | relativeUrl, status.address]"><u>sending them back</u></a> to replenish the faucet.</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
.submit-button:disabled {
|
.submit-button:disabled {
|
||||||
pointer-events: none;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
#satoshis::after {
|
#satoshis::after {
|
||||||
|
@ -41,10 +41,7 @@ export class FaucetComponent implements OnInit, OnDestroy {
|
|||||||
private websocketService: WebsocketService,
|
private websocketService: WebsocketService,
|
||||||
private audioService: AudioService
|
private audioService: AudioService
|
||||||
) {
|
) {
|
||||||
this.faucetForm = this.formBuilder.group({
|
this.initForm(5000, 500_000, null);
|
||||||
'address': ['', [Validators.required, Validators.pattern(getRegex('address', 'testnet4'))]],
|
|
||||||
'satoshis': [0, [Validators.required, Validators.min(0), Validators.max(0)]]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
@ -66,38 +63,7 @@ export class FaucetComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup form
|
// Setup form
|
||||||
this.faucetStatusSubscription = this.servicesApiService.getFaucetStatus$().subscribe({
|
this.updateFaucetStatus();
|
||||||
next: (status) => {
|
|
||||||
if (!status) {
|
|
||||||
this.error = 'internal_server_error';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.status = status;
|
|
||||||
|
|
||||||
const notFaucetAddressValidator = (faucetAddress: string): ValidatorFn => {
|
|
||||||
return (control: AbstractControl): ValidationErrors | null => {
|
|
||||||
const forbidden = control.value === faucetAddress;
|
|
||||||
return forbidden ? { forbiddenAddress: { value: control.value } } : null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
this.faucetForm = this.formBuilder.group({
|
|
||||||
'address': ['', [Validators.required, Validators.pattern(getRegex('address', 'testnet4')), notFaucetAddressValidator(this.status.address)]],
|
|
||||||
'satoshis': [this.status.min, [Validators.required, Validators.min(this.status.min), Validators.max(this.status.max)]]
|
|
||||||
});
|
|
||||||
|
|
||||||
if (this.status.code !== 'ok') {
|
|
||||||
this.error = this.status.code;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.loading = false;
|
|
||||||
this.cd.markForCheck();
|
|
||||||
},
|
|
||||||
error: (response) => {
|
|
||||||
this.loading = false;
|
|
||||||
this.error = response.error;
|
|
||||||
this.cd.markForCheck();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Track transaction
|
// Track transaction
|
||||||
this.websocketService.want(['blocks', 'mempool-blocks']);
|
this.websocketService.want(['blocks', 'mempool-blocks']);
|
||||||
@ -117,7 +83,34 @@ export class FaucetComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateFaucetStatus(): void {
|
||||||
|
this.servicesApiService.getFaucetStatus$().subscribe({
|
||||||
|
next: (status) => {
|
||||||
|
if (!status) {
|
||||||
|
this.error = 'internal_server_error';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.status = status;
|
||||||
|
if (this.status.code !== 'ok') {
|
||||||
|
this.error = this.status.code;
|
||||||
|
this.updateForm(this.status.min ?? 5000, this.status.max ?? 500_000, this.status.address);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// update the form with the proper validation parameters
|
||||||
|
this.updateForm(this.status.min, this.status.max, this.status.address);
|
||||||
|
},
|
||||||
|
error: (response) => {
|
||||||
|
this.loading = false;
|
||||||
|
this.error = response.error;
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
requestCoins(): void {
|
requestCoins(): void {
|
||||||
|
if (this.isDisabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.error = null;
|
this.error = null;
|
||||||
this.txid = '';
|
this.txid = '';
|
||||||
this.stateService.markBlock$.next({});
|
this.stateService.markBlock$.next({});
|
||||||
@ -127,6 +120,7 @@ export class FaucetComponent implements OnInit, OnDestroy {
|
|||||||
this.txid = response.txid;
|
this.txid = response.txid;
|
||||||
this.websocketService.startTrackTransaction(this.txid);
|
this.websocketService.startTrackTransaction(this.txid);
|
||||||
this.audioService.playSound('cha-ching');
|
this.audioService.playSound('cha-ching');
|
||||||
|
this.updateFaucetStatus();
|
||||||
this.cd.markForCheck();
|
this.cd.markForCheck();
|
||||||
}),
|
}),
|
||||||
error: (response: HttpErrorResponse) => {
|
error: (response: HttpErrorResponse) => {
|
||||||
@ -135,9 +129,44 @@ export class FaucetComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isDisabled(): boolean {
|
||||||
|
return !(this.user && this.status?.code === 'ok' && !this.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
getNotFaucetAddressValidator(faucetAddress: string): ValidatorFn {
|
||||||
|
return faucetAddress ? (control: AbstractControl): ValidationErrors | null => {
|
||||||
|
const forbidden = control.value === faucetAddress;
|
||||||
|
return forbidden ? { forbiddenAddress: { value: control.value } } : null;
|
||||||
|
}: () => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
initForm(min: number, max: number, faucetAddress: string): void {
|
||||||
|
this.faucetForm = this.formBuilder.group({
|
||||||
|
'address': ['', [Validators.required, Validators.pattern(getRegex('address', 'testnet4')), this.getNotFaucetAddressValidator(faucetAddress)]],
|
||||||
|
'satoshis': [min, [Validators.required, Validators.min(min), Validators.max(max)]]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.loading = false;
|
||||||
|
this.cd.markForCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateForm(min, max, faucetAddress: string): void {
|
||||||
|
if (!this.faucetForm) {
|
||||||
|
this.initForm(min, max, faucetAddress);
|
||||||
|
} else {
|
||||||
|
this.faucetForm.get('address').setValidators([Validators.required, Validators.pattern(getRegex('address', 'testnet4')), this.getNotFaucetAddressValidator(faucetAddress)]);
|
||||||
|
this.faucetForm.get('satoshis').setValidators([Validators.required, Validators.min(min), Validators.max(max)]);
|
||||||
|
this.faucetForm.get('satoshis').setValue(Math.max(min, this.faucetForm.get('satoshis').value));
|
||||||
|
this.faucetForm.get('satoshis').updateValueAndValidity();
|
||||||
|
this.faucetForm.get('satoshis').markAsDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setAmount(value: number): void {
|
setAmount(value: number): void {
|
||||||
if (this.faucetForm) {
|
if (this.faucetForm) {
|
||||||
this.faucetForm.get('satoshis').setValue(value);
|
this.faucetForm.get('satoshis').setValue(value);
|
||||||
|
this.faucetForm.get('satoshis').updateValueAndValidity();
|
||||||
|
this.faucetForm.get('satoshis').markAsDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user