diff --git a/src/app/backend.service.ts b/src/app/backend.service.ts index 7b6a801..38e8a51 100644 --- a/src/app/backend.service.ts +++ b/src/app/backend.service.ts @@ -1,6 +1,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Socket } from 'ngx-socket-io'; +import { BehaviorSubject } from 'rxjs'; /* * The following interfaces are copied from the backend. @@ -26,17 +27,16 @@ export interface ICart { export interface IPaymentMethod { method: any; - amount: number + amount: number; } export enum PaymentStatus { + CANCELLED = -2, + REQUESTED = -1, PENDING = 0, - PARTIALLY = 1, - UNCONFIRMED = 2, - DONE = 3, - CANCELLED = 4 + UNCONFIRMED = 1, + DONE = 2, } - export interface IInvoice { selector: string; paymentMethods: IPaymentMethod[]; @@ -62,33 +62,132 @@ export class BackendService { SERVER_URL = 'http://localhost:2009'; + invoice: IInvoice | null = null; + invoiceUpdate: BehaviorSubject; + constructor( private socket: Socket, private http: HttpClient ) { + this.invoiceUpdate = new BehaviorSubject(null); this.socket.on('status', (data: any) => { console.log('Status has been updated to: ', data); }); this.socket.on('subscribe', (success: boolean) => { if (success) { console.log('We\'re getting the progress of this invoice!'); } - else { console.log('Subscription failed'); } + else { console.log('Subscribtion failed'); } }); } - subscribeTo(selector: string) { + getSocket(): Socket { + return this.socket; + } + + subscribeTo(selector: string): void { + this.socket.on('subscribe', (status: boolean) => { + if (status) { + this.updateInvoice(); + console.log('Successfully subscribed to this invoice'); + } + else { console.log('Failed to subscribe'); } + }); + this.socket.emit('subscribe', { selector }); } - getInvoice(selector: string): Promise { + updateInvoice(): void { + if (this.invoice !== undefined || this.invoice !== null) { + this.setInvoice(this.invoice?.selector!); + } + } + + setInvoice(selector: string): Promise { return new Promise(async (resolve, reject) => { this.http.get(this.SERVER_URL + '/invoice/' + selector, { observe: 'body', responseType: 'json' }).toPromise().then((invoice) => { - resolve(invoice as IInvoice); + this.invoice = invoice as IInvoice; + this.invoiceUpdate.next(this.invoice); + resolve(this.invoice); }).catch(err => { reject(err); }); }); } + + setPaymentMethod(method: CryptoUnits): Promise { + return new Promise(async (resolve, reject) => { + this.http.post(`${this.SERVER_URL}/invoice/${this.invoice?.selector}/setmethod`, { method }, { + responseType: 'json' + }).toPromise().then(() => { + this.setInvoice(this.invoice!!.selector); + }).catch(err => { + reject(err); + }); + }); + } + + /** + * @returns Path to icon + */ + getIcon(unit: CryptoUnits): string { + switch (unit) { + case CryptoUnits.BITCOIN: + return 'assets/Bitcoin.svg'; + case CryptoUnits.BITCOINCASH: + return 'assets/BitcoinCash.svg'; + case CryptoUnits.DOGECOIN: + return 'assets/Dogecoin.png'; + case CryptoUnits.ETHEREUM: + return 'assets/Ethereum.svg'; + case CryptoUnits.LITECOIN: + return 'assets/Litecoin.svg'; + case CryptoUnits.MONERO: + return 'assets/Monero.svg'; + } + } + + findCryptoBySymbol(symbol: string): string | null { + for (const coin in CryptoUnits) { + // @ts-ignore: This actually works but I thing it's too hacky for TS. Allow me this one, please? + if (CryptoUnits[coin] === symbol.toUpperCase()) { + return coin.charAt(0).toUpperCase() + coin.toLowerCase().slice(1); + } + } + return null; + } + + getAmount(): string | undefined { + return this.invoice?.paymentMethods.find(item => { + return item.method === CryptoUnits.BITCOIN; + })?.amount.toFixed(8); + } + + getStatus(): string { + switch (this.invoice?.status) { + case PaymentStatus.PENDING: + return 'Pending'; + case PaymentStatus.UNCONFIRMED: + return 'Unconfirmed'; + case PaymentStatus.DONE: + return 'Paid'; + case PaymentStatus.CANCELLED: + return 'Cancelled'; + default: + return 'Unknown'; + } + } + + isInvoiceDone(): boolean { + return this.invoice?.status === PaymentStatus.DONE; + } + + isInvoicePending(): boolean { + return this.invoice?.status === PaymentStatus.PENDING; + } + + isInvoiceRequested(): boolean { + return this.invoice?.status === PaymentStatus.REQUESTED; + } } diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 43ac051..26b262b 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index a093fe7..64cc15e 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { BackendService } from '../backend.service'; @Component({ selector: 'app-header', @@ -7,7 +8,7 @@ import { Component, OnInit } from '@angular/core'; }) export class HeaderComponent implements OnInit { - constructor() { } + constructor(public backend: BackendService) { } ngOnInit(): void { } diff --git a/src/app/pay/pay.component.css b/src/app/pay/pay.component.css index be5bc05..3547da4 100644 --- a/src/app/pay/pay.component.css +++ b/src/app/pay/pay.component.css @@ -19,6 +19,17 @@ /* box-shadow: 1px 1px 112px 23px rgba(0,0,0,0.75); */ } +.smaller { + min-width: 200px; + width: 40vw !important; +} + +@media (max-width: 800px) { + .content { + width: 100vw !important; + } +} + .content * { width: 100%; } diff --git a/src/app/pay/pay.component.html b/src/app/pay/pay.component.html index 963bcd1..f4158ab 100644 --- a/src/app/pay/pay.component.html +++ b/src/app/pay/pay.component.html @@ -6,7 +6,7 @@
-
+
\ No newline at end of file diff --git a/src/app/pay/pay.component.ts b/src/app/pay/pay.component.ts index efa012b..521577e 100644 --- a/src/app/pay/pay.component.ts +++ b/src/app/pay/pay.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { BackendService } from '../backend.service'; @Component({ selector: 'app-pay', @@ -7,7 +8,7 @@ import { Component, OnInit } from '@angular/core'; }) export class PayComponent implements OnInit { - constructor() { } + constructor(public backend: BackendService) { } ngOnInit(): void { } diff --git a/src/app/payment/payment.component.css b/src/app/payment/payment.component.css index 47a9258..4e09810 100644 --- a/src/app/payment/payment.component.css +++ b/src/app/payment/payment.component.css @@ -1,15 +1,23 @@ .payment { - display: grid; - grid-template-columns: 1fr 1fr; margin: 0; padding: 0; width: 100%; - height: 400px; + height: 500px; background-color: hsl(0, 0%, 11%); border-radius: 8px; transform: translateY(-8px); } +.request { + transform: translateY(-40px); +} + +.main { + display: grid; + height: 400px; + grid-template-columns: 1fr 1fr; +} + .qr { position: relative; transform: translateY(25%); @@ -42,12 +50,12 @@ } /* Data */ -.data { +.main .data { display: grid; grid-template-columns: 1fr 50px; grid-template-rows: 1fr 4rem 4rem 4rem 1fr; } -#target { +.main #target { grid-row: 2; } #amount { @@ -57,6 +65,65 @@ grid-row: 4; } -h3 { +.main h3 { line-height: 0; +} + +#title { + padding-top: 2rem; + line-height: 1; + font-size: 20pt; + font-weight: normal; + text-align: center; +} + +#price { + text-align: center; + font-size: 16pt; +} + +#list { + overflow-y: scroll; + scroll-behavior: smooth; + -ms-overflow-style: none; + scrollbar-width: none; + margin: 0; + padding: 0; +} +#list::-webkit-scrollbar { + display: none; +} + +#list li { + padding-top: 10px; + padding-left: 5%; + padding-right: 5%; + border-radius: 8px; + display: grid; + cursor: pointer; + grid-template-columns: 75px 1fr; + grid-row: 1fr; + list-style: none; + padding-bottom: 1rem; + transition: .3s ease; + line-height: 0; +} +#list li:hover { + box-shadow: 0px 0px 61px 3px rgba(0,0,0,0.3) inset; +} +#list li img { + height: 42px; + width: 42px; + margin: 0 auto; + padding-top: 12px; + grid-row-start: 1; + grid-row-end: 2; +} +#list li p { + grid-row: 1; + grid-column: 2; +} +#list li h4 { + grid-row: 2; + grid-column: 2; } \ No newline at end of file diff --git a/src/app/payment/payment.component.html b/src/app/payment/payment.component.html index 8c1d87b..2b59911 100644 --- a/src/app/payment/payment.component.html +++ b/src/app/payment/payment.component.html @@ -1,9 +1,23 @@ -
+
+

Choose your
payment method

+

{{ this.backend.invoice!!.totalPrice!!.toFixed(2) }} €

+ +
    +
  • + +
    +

    {{ this.backend.findCryptoBySymbol(coin.method) }}

    +

    {{ coin.amount }} {{ coin.method }}

    +
    +
  • +
+
+
-
+
Send to -

{{ invoice!!.receiveAddress }}

+

{{ this.backend.invoice!.receiveAddress }}

Amount -

{{ getAmount() }} BTC

+

{{ this.backend.getAmount() }} BTC

Status -

{{ getStatus() }}

+

{{ this.backend.getStatus() }}

\ No newline at end of file diff --git a/src/app/payment/payment.component.ts b/src/app/payment/payment.component.ts index 4305c82..24cb7b1 100644 --- a/src/app/payment/payment.component.ts +++ b/src/app/payment/payment.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { BackendService, IInvoice, CryptoUnits, PaymentStatus } from '../backend.service'; +import { BackendService, IInvoice, CryptoUnits, PaymentStatus, IPaymentMethod } from '../backend.service'; @Component({ selector: 'app-payment', @@ -12,10 +12,9 @@ export class PaymentComponent implements OnInit { paymentSelector = ''; choosenPaymentMethod = CryptoUnits.BITCOIN; ready = false; - invoice: IInvoice | null = null; constructor( - private backend: BackendService, + public backend: BackendService, private route: ActivatedRoute ) { } @@ -27,32 +26,13 @@ export class PaymentComponent implements OnInit { }); } - async get() { - this.invoice = await this.backend.getInvoice(this.paymentSelector); + chooseMethod(coin: CryptoUnits) { + this.backend.setPaymentMethod(coin); + } + + async get(): Promise { + await this.backend.setInvoice(this.paymentSelector); this.ready = true; } - getAmount() { - return this.invoice?.paymentMethods.find(item => { - return item.method === CryptoUnits.BITCOIN; - })?.amount.toFixed(8); - } - - getStatus() { - switch (this.invoice?.status) { - case PaymentStatus.PENDING: - return 'Pending'; - case PaymentStatus.PARTIALLY: - return 'Partly'; - case PaymentStatus.UNCONFIRMED: - return 'Unconfirmed'; - case PaymentStatus.DONE: - return 'Paid'; - case PaymentStatus.CANCELLED: - return 'Cancelled'; - default: - return 'Unknown'; - } - } - } diff --git a/src/assets/BitcoinCash.svg b/src/assets/BitcoinCash.svg new file mode 100644 index 0000000..2ecd40b --- /dev/null +++ b/src/assets/BitcoinCash.svg @@ -0,0 +1,63 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/src/assets/Dogecoin.png b/src/assets/Dogecoin.png new file mode 100644 index 0000000..d212c1b Binary files /dev/null and b/src/assets/Dogecoin.png differ diff --git a/src/assets/Ethereum.svg b/src/assets/Ethereum.svg new file mode 100644 index 0000000..14407cf --- /dev/null +++ b/src/assets/Ethereum.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/Litecoin.svg b/src/assets/Litecoin.svg new file mode 100644 index 0000000..109d98d --- /dev/null +++ b/src/assets/Litecoin.svg @@ -0,0 +1 @@ +Litecoin \ No newline at end of file diff --git a/src/assets/Monero.svg b/src/assets/Monero.svg new file mode 100644 index 0000000..179cda8 --- /dev/null +++ b/src/assets/Monero.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/src/styles.css b/src/styles.css index 8503a63..4772b3e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,8 +1,13 @@ /* You can add global styles to this file, and also import other style files */ -*{ +* { font-family: 'Inter', sans-serif; } +body, html { + margin: 0; + padding: 0; +} + :host{ -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;