Crypto currency isn't static anymore
- Notification when transaction got confirmed
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -7527,6 +7527,14 @@
|
|||||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ng-push-ivy": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/ng-push-ivy/-/ng-push-ivy-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-uUzIKBc6LA9Bw0sl7aj6x3eUr2UcCbXEw1PKpLFZ2OxzbnAhqh3IVX4ah0PRiDpfscFhmGUR2amLo19njAbMVg==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ngx-socket-io": {
|
"ngx-socket-io": {
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/ngx-socket-io/-/ngx-socket-io-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/ngx-socket-io/-/ngx-socket-io-3.2.0.tgz",
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
"@angular/platform-browser-dynamic": "~11.0.5",
|
"@angular/platform-browser-dynamic": "~11.0.5",
|
||||||
"@angular/router": "~11.0.5",
|
"@angular/router": "~11.0.5",
|
||||||
"angularx-qrcode": "^10.0.11",
|
"angularx-qrcode": "^10.0.11",
|
||||||
|
"ng-push-ivy": "^1.0.7",
|
||||||
"ngx-socket-io": "^3.2.0",
|
"ngx-socket-io": "^3.2.0",
|
||||||
"rxjs": "~6.6.0",
|
"rxjs": "~6.6.0",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { HttpClientModule } from '@angular/common/http';
|
|||||||
import { AppRoutingModule } from 'src/routes';
|
import { AppRoutingModule } from 'src/routes';
|
||||||
import { NotFoundComponent } from './not-found/not-found.component';
|
import { NotFoundComponent } from './not-found/not-found.component';
|
||||||
import { CartComponent } from './cart/cart.component';
|
import { CartComponent } from './cart/cart.component';
|
||||||
|
import { PushNotificationsModule } from 'ng-push-ivy';
|
||||||
|
|
||||||
const config: SocketIoConfig = { url: 'http://localhost:2009', options: {} };
|
const config: SocketIoConfig = { url: 'http://localhost:2009', options: {} };
|
||||||
|
|
||||||
@@ -31,7 +32,8 @@ const config: SocketIoConfig = { url: 'http://localhost:2009', options: {} };
|
|||||||
QRCodeModule,
|
QRCodeModule,
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
AppRoutingModule,
|
AppRoutingModule,
|
||||||
SocketIoModule.forRoot(config)
|
SocketIoModule.forRoot(config),
|
||||||
|
PushNotificationsModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export interface IInvoice {
|
|||||||
})
|
})
|
||||||
export class BackendService {
|
export class BackendService {
|
||||||
|
|
||||||
SERVER_URL = 'http://localhost:2009';
|
SERVER_URL = 'http://192.168.178.26:2009';
|
||||||
|
|
||||||
// Fill with empty data
|
// Fill with empty data
|
||||||
invoice: IInvoice = {
|
invoice: IInvoice = {
|
||||||
@@ -138,7 +138,7 @@ export class BackendService {
|
|||||||
setInvoice(selector: string): Promise<IInvoice> {
|
setInvoice(selector: string): Promise<IInvoice> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
if (selector === undefined || selector === 'undefined' || selector === '') {
|
if (selector === undefined || selector === 'undefined' || selector === '') {
|
||||||
reject();
|
reject('There is no selector. Please set one before calling setInvoice(...)');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +147,6 @@ export class BackendService {
|
|||||||
responseType: 'json'
|
responseType: 'json'
|
||||||
}).toPromise().then((invoice) => {
|
}).toPromise().then((invoice) => {
|
||||||
this.invoice = invoice as IInvoice;
|
this.invoice = invoice as IInvoice;
|
||||||
this.invoiceUpdate.next(this.invoice);
|
|
||||||
resolve(this.invoice);
|
resolve(this.invoice);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
reject(err);
|
reject(err);
|
||||||
@@ -224,7 +223,14 @@ export class BackendService {
|
|||||||
/**
|
/**
|
||||||
* @returns Path to icon in assets folder
|
* @returns Path to icon in assets folder
|
||||||
*/
|
*/
|
||||||
getIcon(unit: CryptoUnits): string {
|
getIcon(unit?: CryptoUnits): string {
|
||||||
|
if (unit === undefined) {
|
||||||
|
if (this.invoice.paymentMethod === undefined) {
|
||||||
|
return 'assets/Bitcoin.svg';
|
||||||
|
}
|
||||||
|
|
||||||
|
unit = this.invoice.paymentMethod;
|
||||||
|
}
|
||||||
switch (unit) {
|
switch (unit) {
|
||||||
case CryptoUnits.BITCOIN:
|
case CryptoUnits.BITCOIN:
|
||||||
return 'assets/Bitcoin.svg';
|
return 'assets/Bitcoin.svg';
|
||||||
@@ -241,7 +247,15 @@ export class BackendService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findCryptoBySymbol(symbol: string): string | null {
|
findCryptoBySymbol(symbol?: string): string | null {
|
||||||
|
if (symbol === undefined) {
|
||||||
|
if (this.invoice.paymentMethod === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
symbol = this.invoice.paymentMethod;
|
||||||
|
}
|
||||||
|
|
||||||
for (const coin in CryptoUnits) {
|
for (const coin in CryptoUnits) {
|
||||||
// @ts-ignore: This actually works but I think it's too hacky for TS. Allow me this one, please.
|
// @ts-ignore: This actually works but I think it's too hacky for TS. Allow me this one, please.
|
||||||
if (CryptoUnits[coin] === symbol.toUpperCase()) {
|
if (CryptoUnits[coin] === symbol.toUpperCase()) {
|
||||||
|
|||||||
@@ -47,4 +47,9 @@
|
|||||||
.price {
|
.price {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quantity {
|
||||||
|
font-weight: lighter;
|
||||||
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let item of this.backend.invoice.cart;let indexOfelement=index;" [ngClass]="{'xyz-in': this.state.showCart.value, 'xyz-out': !this.state.showCart.value}">
|
<li *ngFor="let item of this.backend.invoice.cart;let indexOfelement=index;" [ngClass]="{'xyz-in': this.state.showCart.value, 'xyz-out': !this.state.showCart.value}">
|
||||||
<img [src]="item.image" class="image">
|
<img [src]="item.image" class="image">
|
||||||
<h5 class="name">{{ item.name }}</h5>
|
<h5 class="name">{{ item.name }}<span class="quantity" *ngIf="item.quantity !== 1"> x{{ item.quantity }}</span></h5>
|
||||||
<span class="price"><b>{{ item.price.toFixed(2) }} {{ this.backend.currencyPrefix() }}</b><br>
|
<span class="price"><b>{{ (item.price * item.quantity).toFixed(2) }} {{ this.backend.currencyPrefix() }}</b><br>
|
||||||
{{ this.backend.calculateCryptoPrice(indexOfelement).toFixed(8) }} {{ this.backend.invoice.paymentMethod }}</span>
|
{{ this.backend.calculateCryptoPrice(indexOfelement).toFixed(8) }} {{ this.backend.invoice.paymentMethod }}</span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
<div class="qrWrapper" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">
|
<div class="qrWrapper" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">
|
||||||
<div class="qr">
|
<div class="qr">
|
||||||
<img src="assets/Bitcoin.svg">
|
<img [src]="this.backend.getIcon()">
|
||||||
<qrcode
|
<qrcode
|
||||||
[qrdata]="'bitcoin:' + this.backend.invoice!!.receiveAddress + '?amount=' + this.backend.getAmount()"
|
[qrdata]="this.backend.findCryptoBySymbol()!.toLowerCase() + ':' + this.backend.invoice.receiveAddress + '?amount=' + this.backend.getAmount()"
|
||||||
[width]="256"
|
[width]="256"
|
||||||
[errorCorrectionLevel]="'M'"
|
[errorCorrectionLevel]="'M'"
|
||||||
[elementType]="'svg'"
|
[elementType]="'svg'"
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<h3>{{ this.backend.invoice?.receiveAddress }}</h3>
|
<h3>{{ this.backend.invoice?.receiveAddress }}</h3>
|
||||||
</span>
|
</span>
|
||||||
<span id="amount" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">Amount
|
<span id="amount" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">Amount
|
||||||
<h3>{{ this.backend.getAmount() }} BTC <span class="price"> | {{ this.backend.invoice.totalPrice!.toFixed(2) }} {{ this.backend.currencyPrefix() }}</span></h3>
|
<h3>{{ this.backend.getAmount() }} {{ this.backend.invoice.paymentMethod }} <span class="price"> | {{ this.backend.invoice.totalPrice!.toFixed(2) }} {{ this.backend.currencyPrefix() }}</span></h3>
|
||||||
</span>
|
</span>
|
||||||
<span id="status" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">Status
|
<span id="status" [ngClass]="{'xyz-in': !this.state.showCart.value, 'xyz-out': this.state.showCart.value}">Status
|
||||||
<h3>
|
<h3>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { PushNotificationsService } from 'ng-push-ivy';
|
||||||
|
|
||||||
import { BackendService, CryptoUnits } from '../backend.service';
|
import { BackendService, CryptoUnits, PaymentStatus } from '../backend.service';
|
||||||
import { StateService } from '../state.service';
|
import { StateService } from '../state.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -15,6 +16,7 @@ export class PaymentComponent implements OnInit {
|
|||||||
confirmations = 0;
|
confirmations = 0;
|
||||||
status: string;
|
status: string;
|
||||||
ready = false;
|
ready = false;
|
||||||
|
emittedNotification = false;
|
||||||
|
|
||||||
// XYZ class (will be xyz-out if cart is shown for example)
|
// XYZ class (will be xyz-out if cart is shown for example)
|
||||||
xyzClass: string;
|
xyzClass: string;
|
||||||
@@ -23,7 +25,8 @@ export class PaymentComponent implements OnInit {
|
|||||||
constructor(
|
constructor(
|
||||||
public backend: BackendService,
|
public backend: BackendService,
|
||||||
public state: StateService,
|
public state: StateService,
|
||||||
private route: ActivatedRoute
|
private route: ActivatedRoute,
|
||||||
|
private push: PushNotificationsService
|
||||||
) {
|
) {
|
||||||
this.status = this.backend.getStatus();
|
this.status = this.backend.getStatus();
|
||||||
this.hideMain = false;
|
this.hideMain = false;
|
||||||
@@ -49,9 +52,31 @@ export class PaymentComponent implements OnInit {
|
|||||||
this.xyzClass = 'xyz-in';
|
this.xyzClass = 'xyz-in';
|
||||||
}, 600);
|
}, 600);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
this.backend.invoiceUpdate.subscribe(newInvoice => {
|
this.backend.invoiceUpdate.subscribe(newInvoice => {
|
||||||
|
if (newInvoice?.status === PaymentStatus.UNCONFIRMED) {
|
||||||
|
this.push.requestPermission();
|
||||||
|
}
|
||||||
|
if (newInvoice?.status === PaymentStatus.DONE) {
|
||||||
|
if (this.emittedNotification) { return; }
|
||||||
|
this.push.create('Transaction confirmed!', {
|
||||||
|
body: 'Your transaction just got confirmed.',
|
||||||
|
lang: 'en',
|
||||||
|
icon: this.backend.getIcon(),
|
||||||
|
sticky: true,
|
||||||
|
vibrate: [250, 400, 250],
|
||||||
|
sound: 'assets/pay_success.mp3'
|
||||||
|
}).subscribe(
|
||||||
|
(res: any) => {
|
||||||
|
console.log('Success');
|
||||||
|
},
|
||||||
|
(err: any) => {
|
||||||
|
console.error('Error:', err);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.emittedNotification = true;
|
||||||
|
}
|
||||||
this.status = this.backend.getStatus();
|
this.status = this.backend.getStatus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -61,7 +86,8 @@ export class PaymentComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async get(): Promise<void> {
|
async get(): Promise<void> {
|
||||||
await this.backend.setInvoice(this.paymentSelector);
|
const res = await this.backend.setInvoice(this.paymentSelector);
|
||||||
|
this.status = this.backend.getStatus();
|
||||||
this.backend.getConfirmation().catch();
|
this.backend.getConfirmation().catch();
|
||||||
this.ready = true;
|
this.ready = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,13 @@ export class StateService {
|
|||||||
|
|
||||||
constructor(private backend: BackendService) {
|
constructor(private backend: BackendService) {
|
||||||
this.showCart = new BehaviorSubject<boolean>(false);
|
this.showCart = new BehaviorSubject<boolean>(false);
|
||||||
|
|
||||||
this.backend.invoiceUpdate.subscribe(invoice => {
|
this.backend.invoiceUpdate.subscribe(invoice => {
|
||||||
this.showCart.next(false); // Hide cart if status changes
|
this.showCart.next(false); // Hide cart if status changes
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCart() {
|
toggleCart(): void {
|
||||||
this.showCart.next(!this.showCart.value);
|
this.showCart.next(!this.showCart.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
src/assets/pay_success.mp3
Normal file
BIN
src/assets/pay_success.mp3
Normal file
Binary file not shown.
Reference in New Issue
Block a user