From ef5c8ddcdffa90aea4f8705b324980c6acb2e163 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 16 May 2024 07:35:55 +0000 Subject: [PATCH 1/7] Add testnet4 faucet --- .../components/faucet/faucet.component.html | 91 ++++++++++++ .../components/faucet/faucet.component.scss | 38 +++++ .../app/components/faucet/faucet.component.ts | 135 ++++++++++++++++++ frontend/src/app/master-page.module.ts | 16 ++- .../src/app/services/services-api.service.ts | 8 ++ frontend/src/app/services/state.service.ts | 2 + frontend/src/app/shared/shared.module.ts | 2 + 7 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 frontend/src/app/components/faucet/faucet.component.html create mode 100644 frontend/src/app/components/faucet/faucet.component.scss create mode 100644 frontend/src/app/components/faucet/faucet.component.ts diff --git a/frontend/src/app/components/faucet/faucet.component.html b/frontend/src/app/components/faucet/faucet.component.html new file mode 100644 index 000000000..f1787505b --- /dev/null +++ b/frontend/src/app/components/faucet/faucet.component.html @@ -0,0 +1,91 @@ +
+ +
+

Testnet4 Faucet

+
+ + @if (error) { +
+ @switch (error) { + @case ('faucet_too_soon') { + Too many requests! Try again later. + } + @case ('faucet_maximum_reached') { + You have exceeded your testnet4 allowance. Try again later. + } + @case ('faucet_not_available') { + The faucet is not available right now. Try again later. + } + @default { + Sorry, something went wrong! Try again later. + } + } +
+ } + +
+ @if (txid) { + + } @else if (loading || !status) { +

Waiting for faucet...

+
+ } @else { +
+
+
+
+
+
+ Amount (sats) +
+ +
+ + + +
+
+
+
Amount is required
+
Minimum is {{ amount?.errors?.['min'].min }}
+
Maximum is {{ amount?.errors?.['max'].max }}
+
+
+
+ Address +
+ + +
+
+ @if (address?.errors?.['required']) { +
Address is required
+ } @else { +
Must be a valid testnet4 address
+ } +
+
+
Too many requests! Try again later.
+
+
+
+
+
+ @if (!user) { +
+ To limit abuse, please log in or sign up and link your Twitter account to use the faucet. +
+ } @else if (!status?.access) { +
+ To use this feature, please link your Twitter account. +
+ } + } +
+
+ If you no longer need your testnet4 coins, please consider sending them back to {{ recycleAddress }} to replenish the faucet. +
+
+
diff --git a/frontend/src/app/components/faucet/faucet.component.scss b/frontend/src/app/components/faucet/faucet.component.scss new file mode 100644 index 000000000..a8cf295f3 --- /dev/null +++ b/frontend/src/app/components/faucet/faucet.component.scss @@ -0,0 +1,38 @@ +.formGroup { + width: 100%; +} + +.input-group { + display: flex; + flex-wrap: wrap; + align-items: stretch; + justify-content: flex-end; + row-gap: 0.5rem; + + .form-control { + min-width: 200px; + } + + .button-group { + display: flex; + align-items: stretch; + } + + #satoshis::after { + content: 'sats'; + position: absolute; + right: 0.5em; + top: 0; + bottom: 0; + } +} + +.faucet-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + width: 100%; + max-width: 800px; + margin: auto; +} \ No newline at end of file diff --git a/frontend/src/app/components/faucet/faucet.component.ts b/frontend/src/app/components/faucet/faucet.component.ts new file mode 100644 index 000000000..d021a1427 --- /dev/null +++ b/frontend/src/app/components/faucet/faucet.component.ts @@ -0,0 +1,135 @@ +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, ValidationErrors, Validators } from "@angular/forms"; +import { StorageService } from '../../services/storage.service'; +import { ServicesApiServices } from '../../services/services-api.service'; +import { AudioService } from '../../services/audio.service'; +import { StateService } from '../../services/state.service'; +import { Subscription, tap } from "rxjs"; +import { HttpErrorResponse } from "@angular/common/http"; +import { getRegex } from "../../shared/regex.utils"; +import { WebsocketService } from "../../services/websocket.service"; + +@Component({ + selector: 'app-faucet', + templateUrl: './faucet.component.html', + styleUrls: ['./faucet.component.scss'] +}) +export class FaucetComponent implements OnInit, OnDestroy { + user: any; + loading: boolean = true; + status: { + access: boolean + min: number, + user_max: number, + user_requests: number, + } | null = null; + error = ''; + faucetForm: FormGroup; + txid = ''; + recycleAddress = this.stateService.env.TESTNET4_FAUCET_ADDRESS || 'tb1q548z58kqvwyjqwy8vc2ntmg33d7s2wyfv7ukq4'; + + mempoolPositionSubscription: Subscription; + confirmationSubscription: Subscription; + + constructor( + private stateService: StateService, + private storageService: StorageService, + private servicesApiService: ServicesApiServices, + private websocketService: WebsocketService, + private audioService: AudioService, + private formBuilder: FormBuilder, + ) { + } + + ngOnInit(): void { + this.user = this.storageService.getAuth()?.user ?? null; + console.log(this.user); + if (this.user) { + this.servicesApiService.getFaucetStatus$().subscribe(status => { + this.status = status; + this.initForm(this.status.min, this.status.user_max); + }) + } else { + this.status = { + access: false, + min: 5000, + user_max: 500000, + user_requests: 1, + } + this.initForm(5000, 500000); + } + + this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => { + if (txPosition && txPosition.txid === this.txid) { + this.stateService.markBlock$.next({ + txid: txPosition.txid, + mempoolPosition: txPosition.position, + }); + } + }); + + this.confirmationSubscription = this.stateService.txConfirmed$.subscribe(([txConfirmed, block]) => { + if (txConfirmed && txConfirmed === this.txid) { + this.stateService.markBlock$.next({ blockHeight: block.height }); + } + }); + } + + initForm(min: number, max: number): void { + this.faucetForm = this.formBuilder.group({ + 'address': ['', [Validators.required, Validators.pattern(getRegex('address', 'testnet4'))]], + 'satoshis': ['', [Validators.required, Validators.min(min), Validators.max(max)]] + }, { validators: (formGroup): ValidationErrors | null => { + if (!this.status?.user_requests) { + return { customError: 'You have used the faucet too many times already! Come back later.'} + } + return null; + }}); + this.faucetForm.get('satoshis').setValue(min); + this.loading = false; + } + + setAmount(value: number): void { + if (this.faucetForm) { + this.faucetForm.get('satoshis').setValue(value); + } + } + + requestCoins(): void { + this.error = null; + this.stateService.markBlock$.next({}); + this.servicesApiService.requestTestnet4Coins$(this.faucetForm.get('address')?.value, parseInt(this.faucetForm.get('satoshis')?.value)) + .subscribe({ + next: ((response) => { + this.txid = response.txid; + this.websocketService.startTrackTransaction(this.txid); + this.audioService.playSound('cha-ching'); + }), + error: (response: HttpErrorResponse) => { + this.error = response.error; + }, + }); + } + + ngOnDestroy(): void { + this.stateService.markBlock$.next({}); + this.websocketService.stopTrackingTransaction(); + if (this.mempoolPositionSubscription) { + this.mempoolPositionSubscription.unsubscribe(); + } + if (this.confirmationSubscription) { + this.confirmationSubscription.unsubscribe(); + } + } + + get amount() { return this.faucetForm.get('satoshis')!; } + get address() { return this.faucetForm.get('address')!; } + get invalidAmount() { + const amount = this.faucetForm.get('satoshis')!; + return amount?.invalid && (amount.dirty || amount.touched) + } + get invalidAddress() { + const address = this.faucetForm.get('address')!; + return address?.invalid && (address.dirty || address.touched) + } +} diff --git a/frontend/src/app/master-page.module.ts b/frontend/src/app/master-page.module.ts index 2d3c34a56..68a902e56 100644 --- a/frontend/src/app/master-page.module.ts +++ b/frontend/src/app/master-page.module.ts @@ -1,6 +1,6 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { Routes, RouterModule } from '@angular/router'; +import { Routes, RouterModule, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'; import { MasterPageComponent } from './components/master-page/master-page.component'; import { SharedModule } from './shared/shared.module'; @@ -12,6 +12,7 @@ import { BlocksList } from './components/blocks-list/blocks-list.component'; import { RbfList } from './components/rbf-list/rbf-list.component'; import { ServerHealthComponent } from './components/server-health/server-health.component'; import { ServerStatusComponent } from './components/server-health/server-status.component'; +import { FaucetComponent } from './components/faucet/faucet.component' const browserWindow = window || {}; // @ts-ignore @@ -104,6 +105,19 @@ if (window['__env']?.OFFICIAL_MEMPOOL_SPACE) { data: { networks: ['bitcoin', 'liquid'] }, component: ServerStatusComponent }); + routes[0].children.push({ + path: 'faucet', + canActivate: [(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => { + return state.url.startsWith('/testnet4/'); + }], + component: StartComponent, + data: { preload: true, networkSpecific: true }, + children: [{ + path: '', + data: { networks: ['bitcoin'] }, + component: FaucetComponent, + }] + }) } @NgModule({ diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts index 89ea9a603..f0e68ca53 100644 --- a/frontend/src/app/services/services-api.service.ts +++ b/frontend/src/app/services/services-api.service.ts @@ -159,4 +159,12 @@ export class ServicesApiServices { setupSquare$(): Observable<{squareAppId: string, squareLocationId: string}> { return this.httpClient.get<{squareAppId: string, squareLocationId: string}>(`${SERVICES_API_PREFIX}/square/setup`); } + + getFaucetStatus$() { + return this.httpClient.get<{access: boolean, min: number, user_max: number, user_requests: number }>(`${SERVICES_API_PREFIX}/testnet4/faucet/status`, { responseType: 'json' }); + } + + requestTestnet4Coins$(address: string, sats: number) { + return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request/${address}?sats=${sats}`, { responseType: 'json' }); + } } diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index 742ca7ab1..dccb34b79 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -72,6 +72,7 @@ export interface Env { ADDITIONAL_CURRENCIES: boolean; GIT_COMMIT_HASH_MEMPOOL_SPACE?: string; PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; + TESTNET4_FAUCET_ADDRESS: string; customize?: Customization; } @@ -104,6 +105,7 @@ const defaultEnv: Env = { 'ACCELERATOR': false, 'PUBLIC_ACCELERATIONS': false, 'ADDITIONAL_CURRENCIES': false, + 'TESTNET4_FAUCET_ADDRESS': '', }; @Injectable({ diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index 0e882d12a..ff8fb9043 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -114,6 +114,7 @@ import { CalculatorComponent } from '../components/calculator/calculator.compone import { BitcoinsatoshisPipe } from '../shared/pipes/bitcoinsatoshis.pipe'; import { HttpErrorComponent } from '../shared/components/http-error/http-error.component'; import { TwitterWidgetComponent } from '../components/twitter-widget/twitter-widget.component'; +import { FaucetComponent } from '../components/faucet/faucet.component'; import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-directives/weight-directives'; @@ -228,6 +229,7 @@ import { OnlyVsizeDirective, OnlyWeightDirective } from './components/weight-dir PendingStatsComponent, HttpErrorComponent, TwitterWidgetComponent, + FaucetComponent, ], imports: [ CommonModule, From bbf8aed38fb91a56cd3e5c18fc3b86f9cb356315 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 16 May 2024 07:36:13 +0000 Subject: [PATCH 2/7] Add faucet navbar item on official testnet4 --- .../src/app/components/master-page/master-page.component.html | 3 +++ frontend/src/app/shared/shared.module.ts | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/components/master-page/master-page.component.html b/frontend/src/app/components/master-page/master-page.component.html index 169dd24a3..4b431138c 100644 --- a/frontend/src/app/components/master-page/master-page.component.html +++ b/frontend/src/app/components/master-page/master-page.component.html @@ -102,6 +102,9 @@ + diff --git a/frontend/src/app/shared/shared.module.ts b/frontend/src/app/shared/shared.module.ts index ff8fb9043..89d62b375 100644 --- a/frontend/src/app/shared/shared.module.ts +++ b/frontend/src/app/shared/shared.module.ts @@ -4,7 +4,7 @@ import { NgbCollapseModule, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstra import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome'; import { faFilter, faAngleDown, faAngleUp, faAngleRight, faAngleLeft, faBolt, faChartArea, faCogs, faCubes, faHammer, faDatabase, faExchangeAlt, faInfoCircle, faLink, faList, faSearch, faCaretUp, faCaretDown, faTachometerAlt, faThList, faTint, faTv, faClock, faAngleDoubleDown, faSortUp, faAngleDoubleUp, faChevronDown, - faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles } from '@fortawesome/free-solid-svg-icons'; + faFileAlt, faRedoAlt, faArrowAltCircleRight, faExternalLinkAlt, faBook, faListUl, faDownload, faQrcode, faArrowRightArrowLeft, faArrowsRotate, faCircleLeft, faFastForward, faWallet, faUserClock, faWrench, faUserFriends, faQuestionCircle, faHistory, faSignOutAlt, faKey, faSuitcase, faIdCardAlt, faNetworkWired, faUserCheck, faCircleCheck, faUserCircle, faCheck, faRocket, faScaleBalanced, faHourglassStart, faHourglassHalf, faHourglassEnd, faWandMagicSparkles, faFaucetDrip } from '@fortawesome/free-solid-svg-icons'; import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { MenuComponent } from '../components/menu/menu.component'; import { PreviewTitleComponent } from '../components/master-page-preview/preview-title.component'; @@ -434,5 +434,6 @@ export class SharedModule { library.addIcons(faHourglassHalf); library.addIcons(faHourglassEnd); library.addIcons(faWandMagicSparkles); + library.addIcons(faFaucetDrip); } } From f8144bd9a0a03c94c119dfd33075708ff4e9e11b Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 16 May 2024 18:36:33 +0000 Subject: [PATCH 3/7] Fetch faucet address from services backend --- frontend/src/app/components/faucet/faucet.component.html | 4 ++-- frontend/src/app/components/faucet/faucet.component.ts | 2 +- frontend/src/app/services/services-api.service.ts | 2 +- frontend/src/app/services/state.service.ts | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/components/faucet/faucet.component.html b/frontend/src/app/components/faucet/faucet.component.html index f1787505b..b9f1161a3 100644 --- a/frontend/src/app/components/faucet/faucet.component.html +++ b/frontend/src/app/components/faucet/faucet.component.html @@ -84,8 +84,8 @@ } }
-
- If you no longer need your testnet4 coins, please consider sending them back to {{ recycleAddress }} to replenish the faucet. +
+ If you no longer need your testnet4 coins, please consider sending them back to {{ status.address }} to replenish the faucet.
diff --git a/frontend/src/app/components/faucet/faucet.component.ts b/frontend/src/app/components/faucet/faucet.component.ts index d021a1427..19269cb2a 100644 --- a/frontend/src/app/components/faucet/faucet.component.ts +++ b/frontend/src/app/components/faucet/faucet.component.ts @@ -18,6 +18,7 @@ export class FaucetComponent implements OnInit, OnDestroy { user: any; loading: boolean = true; status: { + address?: string, access: boolean min: number, user_max: number, @@ -26,7 +27,6 @@ export class FaucetComponent implements OnInit, OnDestroy { error = ''; faucetForm: FormGroup; txid = ''; - recycleAddress = this.stateService.env.TESTNET4_FAUCET_ADDRESS || 'tb1q548z58kqvwyjqwy8vc2ntmg33d7s2wyfv7ukq4'; mempoolPositionSubscription: Subscription; confirmationSubscription: Subscription; diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts index f0e68ca53..480d39f92 100644 --- a/frontend/src/app/services/services-api.service.ts +++ b/frontend/src/app/services/services-api.service.ts @@ -161,7 +161,7 @@ export class ServicesApiServices { } getFaucetStatus$() { - return this.httpClient.get<{access: boolean, min: number, user_max: number, user_requests: number }>(`${SERVICES_API_PREFIX}/testnet4/faucet/status`, { responseType: 'json' }); + return this.httpClient.get<{ address?: string, access: boolean, min: number, user_max: number, user_requests: number }>(`${SERVICES_API_PREFIX}/testnet4/faucet/status`, { responseType: 'json' }); } requestTestnet4Coins$(address: string, sats: number) { diff --git a/frontend/src/app/services/state.service.ts b/frontend/src/app/services/state.service.ts index dccb34b79..742ca7ab1 100644 --- a/frontend/src/app/services/state.service.ts +++ b/frontend/src/app/services/state.service.ts @@ -72,7 +72,6 @@ export interface Env { ADDITIONAL_CURRENCIES: boolean; GIT_COMMIT_HASH_MEMPOOL_SPACE?: string; PACKAGE_JSON_VERSION_MEMPOOL_SPACE?: string; - TESTNET4_FAUCET_ADDRESS: string; customize?: Customization; } @@ -105,7 +104,6 @@ const defaultEnv: Env = { 'ACCELERATOR': false, 'PUBLIC_ACCELERATIONS': false, 'ADDITIONAL_CURRENCIES': false, - 'TESTNET4_FAUCET_ADDRESS': '', }; @Injectable({ From 72f4d811c1543ad2369d214f5348ea3307fddb8b Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 16 May 2024 19:16:53 +0000 Subject: [PATCH 4/7] Add missing websocket subscription to faucet component --- frontend/src/app/components/faucet/faucet.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/components/faucet/faucet.component.ts b/frontend/src/app/components/faucet/faucet.component.ts index 19269cb2a..9a5dc654c 100644 --- a/frontend/src/app/components/faucet/faucet.component.ts +++ b/frontend/src/app/components/faucet/faucet.component.ts @@ -59,6 +59,7 @@ export class FaucetComponent implements OnInit, OnDestroy { this.initForm(5000, 500000); } + this.websocketService.want(['blocks', 'mempool-blocks']); this.mempoolPositionSubscription = this.stateService.mempoolTxPosition$.subscribe(txPosition => { if (txPosition && txPosition.txid === this.txid) { this.stateService.markBlock$.next({ From d9197e28beab993a405dd459b710dd814ea6b41e Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 16 May 2024 21:09:47 +0000 Subject: [PATCH 5/7] Fix faucet 403 handling --- .../components/faucet/faucet.component.html | 6 ++-- .../app/components/faucet/faucet.component.ts | 28 ++++++++++--------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/components/faucet/faucet.component.html b/frontend/src/app/components/faucet/faucet.component.html index b9f1161a3..ac79b24ea 100644 --- a/frontend/src/app/components/faucet/faucet.component.html +++ b/frontend/src/app/components/faucet/faucet.component.html @@ -28,7 +28,7 @@ - } @else if (loading || !status) { + } @else if (loading) {

Waiting for faucet...

} @else { @@ -49,8 +49,8 @@
Amount is required
-
Minimum is {{ amount?.errors?.['min'].min }}
-
Maximum is {{ amount?.errors?.['max'].max }}
+
Minimum is {{ amount?.errors?.['min'].min }}
+
Maximum is {{ amount?.errors?.['max'].max }}
diff --git a/frontend/src/app/components/faucet/faucet.component.ts b/frontend/src/app/components/faucet/faucet.component.ts index 9a5dc654c..98d7a0c57 100644 --- a/frontend/src/app/components/faucet/faucet.component.ts +++ b/frontend/src/app/components/faucet/faucet.component.ts @@ -43,20 +43,22 @@ export class FaucetComponent implements OnInit, OnDestroy { ngOnInit(): void { this.user = this.storageService.getAuth()?.user ?? null; - console.log(this.user); + this.initForm(5000, 500000); if (this.user) { - this.servicesApiService.getFaucetStatus$().subscribe(status => { - this.status = status; - this.initForm(this.status.min, this.status.user_max); - }) - } else { - this.status = { - access: false, - min: 5000, - user_max: 500000, - user_requests: 1, + try { + this.servicesApiService.getFaucetStatus$().subscribe(status => { + this.status = status; + this.initForm(this.status.min, this.status.user_max); + }) + } catch (e) { + if (e?.status !== 403) { + this.error = 'faucet_not_available'; + } + } finally { + this.loading = false; } - this.initForm(5000, 500000); + } else { + this.loading = false; } this.websocketService.want(['blocks', 'mempool-blocks']); @@ -81,7 +83,7 @@ export class FaucetComponent implements OnInit, OnDestroy { 'address': ['', [Validators.required, Validators.pattern(getRegex('address', 'testnet4'))]], 'satoshis': ['', [Validators.required, Validators.min(min), Validators.max(max)]] }, { validators: (formGroup): ValidationErrors | null => { - if (!this.status?.user_requests) { + if (this.status && !this.status?.user_requests) { return { customError: 'You have used the faucet too many times already! Come back later.'} } return null; From 987def9baa8de482b16223aa9b4dc1a089f2b7b8 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 16 May 2024 21:10:15 +0000 Subject: [PATCH 6/7] Improve faucet mobile layout --- frontend/src/app/components/faucet/faucet.component.html | 4 ++-- frontend/src/app/components/faucet/faucet.component.scss | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/components/faucet/faucet.component.html b/frontend/src/app/components/faucet/faucet.component.html index ac79b24ea..1030087b1 100644 --- a/frontend/src/app/components/faucet/faucet.component.html +++ b/frontend/src/app/components/faucet/faucet.component.html @@ -42,7 +42,7 @@
- +
@@ -57,7 +57,7 @@ Address
- +
@if (address?.errors?.['required']) { diff --git a/frontend/src/app/components/faucet/faucet.component.scss b/frontend/src/app/components/faucet/faucet.component.scss index a8cf295f3..084168ca4 100644 --- a/frontend/src/app/components/faucet/faucet.component.scss +++ b/frontend/src/app/components/faucet/faucet.component.scss @@ -8,9 +8,11 @@ align-items: stretch; justify-content: flex-end; row-gap: 0.5rem; + gap: 0.5rem; .form-control { - min-width: 200px; + min-width: 160px; + flex-grow: 100; } .button-group { @@ -18,6 +20,10 @@ align-items: stretch; } + .submit-button, .button-group, .button-group .btn { + flex-grow: 1; + } + #satoshis::after { content: 'sats'; position: absolute; From e8dbd777d01fc37a7e292f3f393d8d982af08f1c Mon Sep 17 00:00:00 2001 From: Mononaut Date: Thu, 16 May 2024 21:17:39 +0000 Subject: [PATCH 7/7] Faucet arguments as query params --- frontend/src/app/services/services-api.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/services/services-api.service.ts b/frontend/src/app/services/services-api.service.ts index 480d39f92..aec4be089 100644 --- a/frontend/src/app/services/services-api.service.ts +++ b/frontend/src/app/services/services-api.service.ts @@ -165,6 +165,6 @@ export class ServicesApiServices { } requestTestnet4Coins$(address: string, sats: number) { - return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request/${address}?sats=${sats}`, { responseType: 'json' }); + return this.httpClient.get<{txid: string}>(`${SERVICES_API_PREFIX}/testnet4/faucet/request?address=${address}&sats=${sats}`, { responseType: 'json' }); } }