diff --git a/CHANGELOG.md b/CHANGELOG.md
index 34eaab466..ef888e0e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
# Changelog
+## **0.3.0** Boggart đź‘»
+
+### Feature:
+- USDC engine integration
+- New withdraw component
+
+### Enhancement:
+- Implements RCN API v6
+- Improves dialog approve UI
+- Estimate Fee amount
+
+### Fix:
+- Fix "go back" button behavior
+- Fix withdraw icon in loan history
+
## **0.2.7** Boggart đź‘»
### Feature:
diff --git a/package-lock.json b/package-lock.json
index 1c8b04bf5..81e2e6c77 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "rcn-angular-dapp",
- "version": "0.2.7",
+ "version": "0.3.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2750,9 +2750,9 @@
}
},
"abab": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz",
- "integrity": "sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg==",
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
"dev": true
},
"abbrev": {
@@ -4027,9 +4027,9 @@
}
},
"browser-process-hrtime": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
- "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==",
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
"dev": true
},
"browser-resolve": {
@@ -7346,12 +7346,12 @@
}
},
"fb-watchman": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz",
- "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
+ "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==",
"dev": true,
"requires": {
- "bser": "^2.0.0"
+ "bser": "2.1.1"
}
},
"fd-slicer": {
@@ -11793,9 +11793,9 @@
},
"dependencies": {
"acorn": {
- "version": "5.7.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
- "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+ "version": "5.7.4",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
+ "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
"dev": true
},
"parse5": {
@@ -13555,9 +13555,9 @@
}
},
"nwsapi": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz",
- "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==",
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+ "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
"dev": true
},
"oargv": {
@@ -15262,21 +15262,21 @@
}
},
"request-promise-core": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
- "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+ "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
"dev": true,
"requires": {
- "lodash": "^4.17.11"
+ "lodash": "^4.17.19"
}
},
"request-promise-native": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz",
- "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==",
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+ "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
"dev": true,
"requires": {
- "request-promise-core": "1.1.2",
+ "request-promise-core": "1.1.4",
"stealthy-require": "^1.1.1",
"tough-cookie": "^2.3.3"
}
@@ -18276,12 +18276,12 @@
"dev": true
},
"w3c-hr-time": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
- "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+ "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
"dev": true,
"requires": {
- "browser-process-hrtime": "^0.1.2"
+ "browser-process-hrtime": "^1.0.0"
}
},
"walker": {
diff --git a/package.json b/package.json
index c980ef8e9..e8d0ef6f9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "rcn-angular-dapp",
- "version": "0.2.7",
+ "version": "0.3.0",
"version_name": "Boggart",
"license": "MIT",
"scripts": {
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index f2bcc35e0..7eef0dcc3 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -3,7 +3,7 @@ import { Router, NavigationEnd } from '@angular/router';
import { MatDialog } from '@angular/material';
import { environment } from '../environments/environment';
// App services
-import { ApiService } from './services/api.service';
+import { ProxyApiService } from './services/proxy-api.service';
import { EventsService } from './services/events.service';
import { WalletConnectService } from './services/wallet-connect.service';
// App component
@@ -20,7 +20,7 @@ export class AppComponent implements OnInit {
constructor(
private router: Router,
private dialog: MatDialog,
- private apiService: ApiService,
+ private proxyApiService: ProxyApiService,
private eventsService: EventsService,
private walletConnectService: WalletConnectService
) {}
@@ -66,8 +66,12 @@ export class AppComponent implements OnInit {
}
private async checkApiHealth() {
- const synchronized: boolean = await this.apiService.isSynchronized();
- if (!synchronized) {
+ const { last_block: lastBlock, current_block: currentBlock } =
+ await this.proxyApiService.getApiStatus();
+ const ALLOWABLE_BLOCK_DIFFERENCE = 6;
+ const blockDiff = currentBlock - lastBlock;
+ const isSynchronized: boolean = blockDiff <= ALLOWABLE_BLOCK_DIFFERENCE;
+ if (!isSynchronized) {
this.dialog.open(DialogApiSyncComponent);
}
}
diff --git a/src/app/core/balance/balance.component.html b/src/app/core/balance/balance.component.html
index 0a0793a58..2253e6faa 100644
--- a/src/app/core/balance/balance.component.html
+++ b/src/app/core/balance/balance.component.html
@@ -1,10 +1,11 @@
+
@@ -16,10 +17,10 @@
- {{ displayAvailable || 0 }}
+ {{ usdcDisplayAvailable || 0 }}
- RCN
+ USDC
diff --git a/src/app/core/balance/balance.component.spec.ts b/src/app/core/balance/balance.component.spec.ts
index 07301037d..6e157d084 100644
--- a/src/app/core/balance/balance.component.spec.ts
+++ b/src/app/core/balance/balance.component.spec.ts
@@ -31,16 +31,16 @@ describe('BalanceComponent', () => {
it('should display balance', () => {
// set balance amount
- component.diasporeLoansWithBalance = [3000];
- component.rcnAvailable = 3000;
+ component.usdcLoansWithBalance = [3000];
+ component.usdcAvailable = 3000;
// update template
component.updateDisplay();
fixture.detectChanges();
// logic expect
- expect(component.displayAvailable).toBe('3,000.00');
- expect(component.canWithdraw).toBeTruthy();
+ expect(component.usdcDisplayAvailable).toBe('3,000.00');
+ expect(component.usdcCanWithdraw).toBeTruthy();
// ui expect
const withdrawPayments = readComponent(fixture, '.balance-withdraw__amount');
diff --git a/src/app/core/balance/balance.component.ts b/src/app/core/balance/balance.component.ts
index 23520bed7..c14f3190c 100644
--- a/src/app/core/balance/balance.component.ts
+++ b/src/app/core/balance/balance.component.ts
@@ -1,8 +1,8 @@
import { Component, OnInit, OnChanges, OnDestroy, Input } from '@angular/core';
import {Â Subscription } from 'rxjs';
import { environment } from '../../../environments/environment';
+import { Engine } from '../../models/loan.model';
import { Utils } from '../../utils/utils';
-// App Services
import { Web3Service } from '../../services/web3.service';
import { EventsService } from '../../services/events.service';
import { ContractsService } from '../../services/contracts.service';
@@ -16,13 +16,12 @@ import { Tx, Type, TxService } from '../../services/tx.service';
export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
@Input() account: string;
- rcnAvailable: number;
- diasporeLoansWithBalance: number[] = [];
- ongoingDiasporeWithdraw: Tx;
-
- canWithdraw = false;
- displayAvailable = '';
- txSubscription: boolean;
+ usdcAvailable: number;
+ usdcLoansWithBalance: number[] = [];
+ usdcOngoingWithdraw: Tx;
+ usdcCanWithdraw = false;
+ usdcDisplayAvailable = '';
+ usdcTxSubscription: boolean;
// subscriptions
subscriptionBalance: Subscription;
@@ -53,7 +52,7 @@ export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
if (this.subscriptionBalance) {
this.subscriptionBalance.unsubscribe();
}
- if (this.txSubscription) {
+ if (this.usdcTxSubscription) {
this.txService.unsubscribeConfirmedTx(async (tx: Tx) => this.trackWithdrawTx(tx));
}
}
@@ -71,16 +70,16 @@ export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
* Update balance and withdraw amount
*/
updateDisplay() {
- if (this.rcnAvailable) {
- this.displayAvailable = Utils.formatAmount(this.rcnAvailable);
+ if (this.usdcAvailable) {
+ this.usdcDisplayAvailable = Utils.formatAmount(this.usdcAvailable);
} else {
- this.displayAvailable = '0';
+ this.usdcDisplayAvailable = '0';
}
- this.canWithdraw =
- this.diasporeLoansWithBalance !== undefined &&
- this.diasporeLoansWithBalance.length > 0 &&
- this.ongoingDiasporeWithdraw === undefined;
+ this.usdcCanWithdraw =
+ this.usdcLoansWithBalance !== undefined &&
+ this.usdcLoansWithBalance.length > 0 &&
+ this.usdcOngoingWithdraw === undefined;
}
/**
@@ -88,9 +87,9 @@ export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
* total available
*/
async loadWithdrawBalance() {
- const pendingWithdraws = await this.contractService.getPendingWithdraws();
- this.rcnAvailable = pendingWithdraws[2] / 10 ** 18;
- this.diasporeLoansWithBalance = pendingWithdraws[3];
+ const pendingWithdraws = await this.contractService.getPendingWithdraws(Engine.UsdcEngine);
+ this.usdcAvailable = pendingWithdraws[2] / 10 ** 6;
+ this.usdcLoansWithBalance = pendingWithdraws[3];
this.loadOngoingWithdraw();
this.updateDisplay();
}
@@ -99,18 +98,18 @@ export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
* Load the pending withdraw
*/
loadOngoingWithdraw() {
- this.ongoingDiasporeWithdraw = this.txService.getLastWithdraw(
- environment.contracts.diaspore.debtEngine,
- this.diasporeLoansWithBalance
+ this.usdcOngoingWithdraw = this.txService.getLastWithdraw(
+ environment.contracts[Engine.UsdcEngine].diaspore.debtEngine,
+ this.usdcLoansWithBalance
);
}
/**
- * Handle click on withdraw
+ * Handle click on withdraw USDC
*/
- async clickWithdraw() {
+ async clickWithdrawUsdc() {
try {
- await this.withdraw();
+ await this.withdrawUsdc();
} catch (err) {
if (err.stack.indexOf('User denied transaction signature') < 0) {
this.eventsService.trackError(err);
@@ -121,11 +120,11 @@ export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
/**
* Withdraw diaspore funds
*/
- async withdraw() {
- if (this.canWithdraw) {
- if (this.diasporeLoansWithBalance.length > 0) {
- const tx = await this.contractService.withdrawFundsDiaspore(this.diasporeLoansWithBalance);
- this.txService.registerWithdrawTx(tx, environment.contracts.diaspore.debtEngine, this.diasporeLoansWithBalance);
+ private async withdrawUsdc() {
+ if (this.usdcCanWithdraw) {
+ if (this.usdcLoansWithBalance.length > 0) {
+ const tx = await this.contractService.withdrawFundsDiaspore(Engine.UsdcEngine, this.usdcLoansWithBalance);
+ this.txService.registerWithdrawTx(tx, environment.contracts[Engine.UsdcEngine].diaspore.debtEngine, this.usdcLoansWithBalance);
}
this.loadWithdrawBalance();
this.retrievePendingTx();
@@ -135,9 +134,9 @@ export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
/**
* Retrieve pending Tx
*/
- retrievePendingTx() {
- if (!this.txSubscription) {
- this.txSubscription = true;
+ private retrievePendingTx() {
+ if (!this.usdcTxSubscription) {
+ this.usdcTxSubscription = true;
this.txService.subscribeConfirmedTx(async (tx: Tx) => this.trackWithdrawTx(tx));
}
}
@@ -145,7 +144,7 @@ export class BalanceComponent implements OnInit, OnChanges, OnDestroy {
/**
* Track tx
*/
- trackWithdrawTx(tx: Tx) {
+ private trackWithdrawTx(tx: Tx) {
if (tx.type === Type.withdraw) {
this.web3Service.updateBalanceEvent.emit();
}
diff --git a/src/app/core/content-wrapper/content-wrapper.component.ts b/src/app/core/content-wrapper/content-wrapper.component.ts
index dba5ddc5a..3de66e68e 100644
--- a/src/app/core/content-wrapper/content-wrapper.component.ts
+++ b/src/app/core/content-wrapper/content-wrapper.component.ts
@@ -2,13 +2,16 @@ import { Component, OnInit, HostListener } from '@angular/core';
import { MatDialog } from '@angular/material';
import * as BN from 'bn.js';
import { Utils } from '../../utils/utils';
-import { Loan } from './../../models/loan.model';
+import { Loan, Engine } from './../../models/loan.model';
import { Status } from './../../models/collateral.model';
+import { LoanContentApi } from './../../interfaces/loan-api-diaspore';
+import { LoanUtils } from './../../utils/loan-utils';
// App Components
import { DialogWrongCountryComponent } from '../../dialogs/dialog-wrong-country/dialog-wrong-country.component';
import { DialogNeedWithdrawComponent } from '../../dialogs/dialog-need-withdraw/dialog-need-withdraw.component';
// App Service
import { environment } from '../../../environments/environment';
+import { ProxyApiService } from '../../services/proxy-api.service';
import { SidebarService } from '../../services/sidebar.service';
import { ApplicationAdsService } from '../../services/application-ads.service';
import { Web3Service } from '../../services/web3.service';
@@ -40,7 +43,7 @@ export class ContentWrapperComponent implements OnInit {
get withdrawEnabled(): boolean {
return this.diasporeLoansWithBalance !== undefined &&
this.diasporeLoansWithBalance.length > 0 &&
- this.pendingDiasporeWithdraw === undefined;
+ this.pendingRcnWithdraw === undefined;
}
winHeight: number = window.innerHeight;
account: string;
@@ -52,7 +55,7 @@ export class ContentWrapperComponent implements OnInit {
rcnAvailable: BN;
loansWithBalance: number[];
diasporeLoansWithBalance: number[];
- pendingDiasporeWithdraw: Tx;
+ pendingRcnWithdraw: Tx;
navToggle: boolean; // Navbar toggled
navmobileToggled = false; // Nav Mobile toggled
@@ -62,6 +65,7 @@ export class ContentWrapperComponent implements OnInit {
needWithdraw: boolean;
constructor(
+ private proxyApiService: ProxyApiService,
private sidebarService: SidebarService, // Navbar Service
private applicationAdsService: ApplicationAdsService,
private web3Service: Web3Service,
@@ -141,8 +145,8 @@ export class ContentWrapperComponent implements OnInit {
* Load all pending withdraw
*/
private loadPendingWithdraw() {
- this.pendingDiasporeWithdraw = this.txService.getLastWithdraw(
- environment.contracts.diaspore.debtEngine,
+ this.pendingRcnWithdraw = this.txService.getLastWithdraw(
+ environment.contracts[Engine.RcnEngine].diaspore.debtEngine,
this.diasporeLoansWithBalance
);
}
@@ -159,7 +163,7 @@ export class ContentWrapperComponent implements OnInit {
* Load withdraw balance adding diaspore amount
*/
private async loadWithdrawBalance() {
- const pendingWithdraws = await this.contractService.getPendingWithdraws();
+ const pendingWithdraws = await this.contractService.getPendingWithdraws(Engine.RcnEngine);
this.rcnAvailable = Utils.bn(pendingWithdraws[2] / 10 ** 18);
this.diasporeLoansWithBalance = pendingWithdraws[3];
this.loadPendingWithdraw();
@@ -177,9 +181,10 @@ export class ContentWrapperComponent implements OnInit {
return;
}
+ const { content } = await this.proxyApiService.getLent(account);
+ const loans: Loan[] = content.map((loanData: LoanContentApi) =>Â LoanUtils.buildLoan(loanData));
const loansToWithdraw: Loan[] =
- (await this.contractService.getLoansOfBorrower(account))
- .filter(({ collateral }) => collateral && collateral.status === Status.ToWithdraw);
+ loans.filter(({ collateral }) => collateral && collateral.status === Status.ToWithdraw);
if (loansToWithdraw.length) {
this.needWithdraw = true;
diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts
index 0d73f407e..dc460e2e2 100644
--- a/src/app/core/core.module.ts
+++ b/src/app/core/core.module.ts
@@ -12,6 +12,7 @@ import { NotificationsComponent } from './header/notifications/notifications.com
import { NotificationItemComponent } from './header/notifications/notification-item/notification-item.component';
import { WalletBalancesComponent } from './header/wallet-balances/wallet-balances.component';
import { WalletAvatarComponent } from './header/wallet-avatar/wallet-avatar.component';
+import { WalletWithdrawComponent } from './header/wallet-withdraw/wallet-withdraw.component';
import { SocialContainerComponent } from './social-container/social-container.component';
// App Directives
import { ClickOutsideDirective } from '../directives/click-outside.directive';
@@ -34,6 +35,7 @@ import { OpenEtherscanDirective } from '../directives/open-etherscan.directive';
NotificationItemComponent,
WalletBalancesComponent,
WalletAvatarComponent,
+ WalletWithdrawComponent,
SocialContainerComponent
],
exports: [
diff --git a/src/app/core/header/header.component.html b/src/app/core/header/header.component.html
index cb8f5b14d..9522a0a40 100644
--- a/src/app/core/header/header.component.html
+++ b/src/app/core/header/header.component.html
@@ -62,9 +62,7 @@ RCN / Ripio credit network
class="align-items-center px-0"
[ngClass]="hasAccount ? 'd-flex' : 'd-none'"
>
-
-
-
+
diff --git a/src/app/core/header/icon-group-header/icon-group-header.component.html b/src/app/core/header/icon-group-header/icon-group-header.component.html
index 2c1cab5ee..c6ec8295f 100644
--- a/src/app/core/header/icon-group-header/icon-group-header.component.html
+++ b/src/app/core/header/icon-group-header/icon-group-header.component.html
@@ -1,5 +1,5 @@
@@ -23,7 +23,17 @@
>
+
+
+
+
+
diff --git a/src/app/core/header/icon-group-header/icon-group-header.component.scss b/src/app/core/header/icon-group-header/icon-group-header.component.scss
index 0da949939..5f53d8ede 100644
--- a/src/app/core/header/icon-group-header/icon-group-header.component.scss
+++ b/src/app/core/header/icon-group-header/icon-group-header.component.scss
@@ -18,7 +18,7 @@
align-items: center;
justify-content: center;
position: absolute;
- right: 5.5em;
+ right: 10.65em;
top: -5px;
padding: 0 4px;
font-size: 11px;
diff --git a/src/app/core/header/icon-group-header/icon-group-header.component.ts b/src/app/core/header/icon-group-header/icon-group-header.component.ts
index 98c808252..11999cdc9 100644
--- a/src/app/core/header/icon-group-header/icon-group-header.component.ts
+++ b/src/app/core/header/icon-group-header/icon-group-header.component.ts
@@ -1,51 +1,84 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, OnDestroy, Input } from '@angular/core';
+import { Subscription } from 'rxjs';
import { HeaderPopoverService } from '../../../services/header-popover.service';
+enum ViewType {
+ Notifications = 'notifications',
+ WalletBalance = 'balance',
+ WalletWithdraw = 'withdraw'
+}
+
@Component({
selector: 'app-icon-group-header',
templateUrl: './icon-group-header.component.html',
styleUrls: ['./icon-group-header.component.scss']
})
-export class IconGroupHeaderComponent implements OnInit {
+export class IconGroupHeaderComponent implements OnInit, OnDestroy {
+ @Input() account: string;
viewDetail: string;
selection: string;
previousSelection: string;
-
notificationsCounter: number;
+ // subscriptions
+ subscriptionHeader: Subscription;
constructor(
public headerPopoverService: HeaderPopoverService
- ) {}
+ ) { }
+
+ ngOnInit() {
+ this.subscriptionHeader =
+ this.headerPopoverService.currentDetail.subscribe(detail => this.viewDetail = detail);
+ }
+
+ ngOnDestroy() {
+ if (this.subscriptionHeader) {
+ this.subscriptionHeader.unsubscribe();
+ }
+ }
- isDetail(view: string): Boolean { // Check viewDetail state to open/close notifications Component
+ /**
+ * Check viewDetail state to open/close notifications Component
+ * @param view ViewType
+ */
+ isDetail(view: ViewType |Â string): Boolean {
return view === this.viewDetail;
}
- openDetail(selection: 'clickOutside' | 'notifications' | 'balance') { // Change viewDetail state to open/close notifications Component
+
+ /**
+ * Change viewDetail state to open/close notifications Component
+ * @param selection ViewType (icon clicked)
+ */
+ openDetail(selection: ViewType |Â string) {
this.previousSelection = this.selection;
this.selection = selection;
- switch (selection) {
- case 'notifications':
- case 'balance':
- if (selection !== this.previousSelection || this.viewDetail === undefined) {
- this.headerPopoverService.changeDetail(selection); // Change value of viewDetail from Notifications Service
- } else {
- this.headerPopoverService.changeDetail(undefined); // Force to close notifications Component by ClickOutside Directive event
- }
- break;
- default:
- this.headerPopoverService.changeDetail(undefined); // Force to close notifications Component by ClickOutside Directive event
- break;
+
+ const { previousSelection, viewDetail } = this;
+ const isClickOutside = !Object.values(ViewType).includes(selection);
+
+ // case clickOutside
+ if (isClickOutside) {
+ const isPreviousClickOutside = previousSelection === 'clickOutside';
+ if (previousSelection && !isPreviousClickOutside) {
+ this.headerPopoverService.changeDetail(undefined);
+ }
+ return;
+ }
+
+ // case ViewType
+ if (selection !== previousSelection || viewDetail === undefined) {
+ this.headerPopoverService.changeDetail(selection);
+ } else {
+ this.headerPopoverService.changeDetail(undefined);
}
}
+ /**
+ * Updates the notification counter when the child component emits an event
+ * @param counter New notifications counter number
+ */
updateCounter(counter: number) {
this.notificationsCounter = counter;
}
-
- ngOnInit() {
- // Subscribe to detail from Notifications Service
- this.headerPopoverService.currentDetail.subscribe(detail => this.viewDetail = detail);
- }
-
}
diff --git a/src/app/core/header/icon-group-header/icon-group.component.spec.ts b/src/app/core/header/icon-group-header/icon-group.component.spec.ts
index 131539b46..298ce7e63 100644
--- a/src/app/core/header/icon-group-header/icon-group.component.spec.ts
+++ b/src/app/core/header/icon-group-header/icon-group.component.spec.ts
@@ -39,7 +39,7 @@ describe('IconGroupHeaderComponent', () => {
});
it('should return true', () => {
- const view = 'notifications';
+ const view = 'notifications' as any;
component.viewDetail = view;
expect(component.isDetail(view)).toBeTruthy();
});
diff --git a/src/app/core/header/notifications/notifications.component.ts b/src/app/core/header/notifications/notifications.component.ts
index fd9dea760..00ee97ce0 100644
--- a/src/app/core/header/notifications/notifications.component.ts
+++ b/src/app/core/header/notifications/notifications.component.ts
@@ -7,6 +7,7 @@ import {
transition
} from '@angular/animations';
import { environment } from '../../../../environments/environment';
+import { Engine } from '../../../models/loan.model';
import { Notification, TxObject } from '../../../models/notification.model';
import { HeaderPopoverService } from '../../../services/header-popover.service';
import { TxService, Tx } from '../../../services/tx.service';
@@ -62,16 +63,20 @@ export class NotificationsComponent implements OnInit {
*/
getContractName(contract: string) {
switch (contract) {
- case environment.contracts.diaspore.loanManager:
+ case environment.contracts[Engine.RcnEngine].diaspore.loanManager:
+ case environment.contracts[Engine.UsdcEngine].diaspore.loanManager:
return 'Loan Manager Contract';
- case environment.contracts.diaspore.debtEngine:
+ case environment.contracts[Engine.RcnEngine].diaspore.debtEngine:
+ case environment.contracts[Engine.UsdcEngine].diaspore.debtEngine:
return 'Debt Engine Contract';
- case environment.contracts.converter.converterRamp:
+ case environment.contracts[Engine.RcnEngine].converter.converterRamp:
+ case environment.contracts[Engine.UsdcEngine].converter.converterRamp:
return 'Converter Ramp Contract';
- case environment.contracts.collateral.collateral:
+ case environment.contracts[Engine.RcnEngine].collateral.collateral:
+ case environment.contracts[Engine.UsdcEngine].collateral.collateral:
return 'Collateral Contract';
default:
diff --git a/src/app/core/header/wallet-balances/wallet-balances.component.ts b/src/app/core/header/wallet-balances/wallet-balances.component.ts
index 9966a31cc..d923f7f02 100644
--- a/src/app/core/header/wallet-balances/wallet-balances.component.ts
+++ b/src/app/core/header/wallet-balances/wallet-balances.component.ts
@@ -1,4 +1,4 @@
-import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
+import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core';
import {
trigger,
state,
@@ -6,6 +6,7 @@ import {
animate,
transition
} from '@angular/animations';
+import { Subscription } from 'rxjs';
import * as BN from 'bn.js';
import { Currency } from './../../../utils/currencies';
import { HeaderPopoverService } from './../../../services/header-popover.service';
@@ -41,11 +42,13 @@ interface Balance {
])
]
})
-export class WalletBalancesComponent implements OnInit {
+export class WalletBalancesComponent implements OnInit, OnDestroy {
viewDetail: string;
- rcnBalance: string;
balances: Balance[];
+ // subscriptions
+ subscriptionPopover: Subscription;
+
constructor(
private cdRef: ChangeDetectorRef,
public headerPopoverService: HeaderPopoverService,
@@ -54,15 +57,22 @@ export class WalletBalancesComponent implements OnInit {
) { }
ngOnInit() {
- this.headerPopoverService.currentDetail.subscribe(async detail => {
- this.viewDetail = detail;
- this.cdRef.detectChanges();
- await this.loadBalances();
- });
+ this.subscriptionPopover =
+ this.headerPopoverService.currentDetail.subscribe(async detail => {
+ this.viewDetail = detail;
+ this.cdRef.detectChanges();
+ await this.loadBalances();
+ });
this.loadBalances();
}
+ ngOnDestroy() {
+ if (this.subscriptionPopover) {
+ this.subscriptionPopover.unsubscribe();
+ }
+ }
+
/**
* Show the user balance in different tokens
*/
diff --git a/src/app/core/header/wallet-withdraw/wallet-withdraw.component.html b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.html
new file mode 100644
index 000000000..a87af6fc7
--- /dev/null
+++ b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.html
@@ -0,0 +1,58 @@
+
+
+ - Withdrawable Balance
+ -
+
+
+
+
+ {{ rcnDisplayAvailable || 0 }}
+
+
+ RCN
+
+
+
+
+
+
+
+ -
+
+
+
+
+ {{ usdcDisplayAvailable || 0 }}
+
+
+ USDC
+
+
+
+
+
+
+
+
+
diff --git a/src/app/core/header/wallet-withdraw/wallet-withdraw.component.scss b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.scss
new file mode 100644
index 000000000..5d0f58c2c
--- /dev/null
+++ b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.scss
@@ -0,0 +1,53 @@
+@import './../../../../scss/variables';
+@import './../../../../scss/fonts';
+
+.notifications-container {
+ width: 212px;
+ max-height: 670px;
+ overflow: hidden;
+ position: absolute;
+ right: 14px;
+ @extend %roboto-regular;
+ @include transition();
+
+ ul {
+ li {
+ &:nth-child(1){
+ display: flex;
+ align-items: center;
+ height: 30px;
+ padding: 10px;
+ background-color: var(--app-color-gray-500);
+ border-bottom: 1px solid var(--app-color-gray-600);
+ }
+ }
+ .item {
+ padding: 11px 9px 11px 7px;
+ border-bottom: 1px solid var(--app-color-gray-600);
+ background-color: var(--app-color-gray-400);
+ color: white;
+ &__image {
+ width: 30px;
+ margin-right: 6px;
+ }
+ &__text {
+ &-amount {
+ @include typography-caption(1);
+ }
+ &-currency {
+ @include styled-font('Roboto', 700, 11px);
+ color: var(--app-color-gray-50);
+ }
+ }
+ &__action {
+ .button {
+ @include styled-font('Roboto', 400, 10px);
+ min-width: inherit;
+ padding: 6px 8px;
+ line-height: 1em;
+ margin: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/src/app/core/header/wallet-withdraw/wallet-withdraw.component.spec.ts b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.spec.ts
new file mode 100644
index 000000000..f61d0dd7f
--- /dev/null
+++ b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.spec.ts
@@ -0,0 +1,32 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { HttpClientModule } from '@angular/common/http';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { SharedModule } from './../../../shared/shared.module';
+import { WalletWithdrawComponent } from './wallet-withdraw.component';
+
+describe('WalletWithdrawComponent', () => {
+ let component: WalletWithdrawComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ BrowserAnimationsModule,
+ HttpClientModule,
+ SharedModule
+ ],
+ declarations: [ WalletWithdrawComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(WalletWithdrawComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/core/header/wallet-withdraw/wallet-withdraw.component.ts b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.ts
new file mode 100644
index 000000000..c4bef3e25
--- /dev/null
+++ b/src/app/core/header/wallet-withdraw/wallet-withdraw.component.ts
@@ -0,0 +1,258 @@
+import { Component, OnInit, OnChanges, OnDestroy, Input, ChangeDetectorRef } from '@angular/core';
+import {
+ trigger,
+ state,
+ style,
+ animate,
+ transition
+} from '@angular/animations';
+import { Subscription, timer } from 'rxjs';
+import { environment } from '../../../../environments/environment';
+import { Engine } from './../../../models/loan.model';
+import { HeaderPopoverService } from './../../../services/header-popover.service';
+import { ContractsService } from './../../../services/contracts.service';
+import { Web3Service } from './../../../services/web3.service';
+import { EventsService } from './../../../services/events.service';
+import { Tx, Type, TxService } from './../../../services/tx.service';
+import { Utils } from './../../../utils/utils';
+
+@Component({
+ selector: 'app-wallet-withdraw',
+ templateUrl: './wallet-withdraw.component.html',
+ styleUrls: ['./wallet-withdraw.component.scss'],
+ animations: [
+ trigger('anmNotifications', [
+ state('open', style({
+ opacity: 1,
+ display: 'block',
+ top: '43px'
+ })),
+ state('closed', style({
+ opacity: 0,
+ display: 'none',
+ top: '48px'
+ })),
+ transition('open => closed', [
+ animate('.3s')
+ ]),
+ transition('closed => open', [
+ animate('.3s')
+ ])
+ ])
+ ]
+})
+export class WalletWithdrawComponent implements OnInit, OnDestroy, OnChanges {
+ @Input() account: string;
+ viewDetail: string;
+
+ rcnAvailable: number;
+ rcnLoansWithBalance: number[] = [];
+ rcnOngoingWithdraw: Tx;
+ rcnCanWithdraw = false;
+ rcnTxSubscription: boolean;
+ rcnDisplayAvailable = '';
+
+ usdcAvailable: number;
+ usdcLoansWithBalance: number[] = [];
+ usdcOngoingWithdraw: Tx;
+ usdcCanWithdraw = false;
+ usdcTxSubscription: boolean;
+ usdcDisplayAvailable = '';
+
+ // subscriptions
+ subscriptionPopover: Subscription;
+ subscriptionBalance: Subscription;
+
+ constructor(
+ private cdRef: ChangeDetectorRef,
+ private headerPopoverService: HeaderPopoverService,
+ private txService: TxService,
+ private web3Service: Web3Service,
+ private eventsService: EventsService,
+ private contractsService: ContractsService
+ ) { }
+
+ ngOnInit() {
+ this.subscriptionPopover =
+ this.headerPopoverService.currentDetail.subscribe(detail => {
+ this.viewDetail = detail;
+ this.cdRef.detectChanges();
+ this.loadWithdrawBalance();
+ });
+
+ this.retrievePendingTx();
+ this.handleBalanceEvents();
+ }
+
+ ngOnChanges(changes) {
+ const web3: any = this.web3Service.web3;
+ const { account } = changes;
+
+ if (account.currentValue) {
+ this.account = web3.utils.toChecksumAddress(account.currentValue);
+ this.loadWithdrawBalance();
+ }
+ }
+
+ ngOnDestroy() {
+ if (this.subscriptionPopover) {
+ this.subscriptionPopover.unsubscribe();
+ }
+ if (this.subscriptionBalance) {
+ this.subscriptionBalance.unsubscribe();
+ }
+ if (this.rcnTxSubscription) {
+ this.txService.unsubscribeConfirmedTx(async (tx: Tx) => this.trackWithdrawTx(tx));
+ }
+ if (this.usdcTxSubscription) {
+ this.txService.unsubscribeConfirmedTx(async (tx: Tx) => this.trackWithdrawTx(tx));
+ }
+ }
+
+ /**
+ * Listen and handle balance events for update amounts
+ */
+ handleBalanceEvents() {
+ this.subscriptionBalance = this.web3Service.updateBalanceEvent.subscribe(() => {
+ this.loadWithdrawBalance();
+ });
+ }
+
+ /**
+ * Update balance and withdraw amount
+ */
+ updateDisplay() {
+ // rcn engine
+ if (this.rcnAvailable) {
+ this.rcnDisplayAvailable = Utils.formatAmount(this.rcnAvailable);
+ } else {
+ this.rcnDisplayAvailable = '0';
+ }
+ this.rcnCanWithdraw =
+ this.rcnLoansWithBalance !== undefined &&
+ this.rcnLoansWithBalance.length > 0 &&
+ this.rcnOngoingWithdraw === undefined;
+
+ // usdc engine
+ if (this.usdcAvailable) {
+ this.usdcDisplayAvailable = Utils.formatAmount(this.usdcAvailable);
+ } else {
+ this.usdcDisplayAvailable = '0';
+ }
+ this.usdcCanWithdraw =
+ this.usdcLoansWithBalance !== undefined &&
+ this.usdcLoansWithBalance.length > 0 &&
+ this.usdcOngoingWithdraw === undefined;
+ }
+
+ /**
+ * Load balance to withdraw amounts. Then, add all the values ​​and show the
+ * total available
+ */
+ async loadWithdrawBalance() {
+ // rcn engine
+ const rcnPendingWithdraws = await this.contractsService.getPendingWithdraws(Engine.RcnEngine);
+ this.rcnAvailable = rcnPendingWithdraws[2] / 10 ** 18;
+ this.rcnLoansWithBalance = rcnPendingWithdraws[3];
+
+ // usdc engine
+ const usdcPendingWithdraws = await this.contractsService.getPendingWithdraws(Engine.UsdcEngine);
+ this.usdcAvailable = usdcPendingWithdraws[2] / 10 ** 6;
+ this.usdcLoansWithBalance = usdcPendingWithdraws[3];
+
+ this.loadOngoingWithdraw();
+ this.updateDisplay();
+ }
+
+ /**
+ * Load the pending withdraw
+ */
+ loadOngoingWithdraw() {
+ this.rcnOngoingWithdraw = this.txService.getLastWithdraw(
+ environment.contracts[Engine.RcnEngine].diaspore.debtEngine,
+ this.rcnLoansWithBalance
+ );
+ this.usdcOngoingWithdraw = this.txService.getLastWithdraw(
+ environment.contracts[Engine.UsdcEngine].diaspore.debtEngine,
+ this.usdcLoansWithBalance
+ );
+ }
+
+ /**
+ * Handle click on withdraw RCN
+ */
+ async clickWithdrawRcn() {
+ try {
+ await this.withdrawRcn();
+ } catch (err) {
+ if (err.stack.indexOf('User denied transaction signature') < 0) {
+ this.eventsService.trackError(err);
+ }
+ }
+ }
+
+ /**
+ * Handle click on withdraw USDC
+ */
+ async clickWithdrawUsdc() {
+ try {
+ await this.withdrawUsdc();
+ } catch (err) {
+ if (err.stack.indexOf('User denied transaction signature') < 0) {
+ this.eventsService.trackError(err);
+ }
+ }
+ }
+
+ /**
+ * Withdraw diaspore funds
+ */
+ private async withdrawRcn() {
+ if (this.rcnCanWithdraw) {
+ if (this.rcnLoansWithBalance.length > 0) {
+ const tx = await this.contractsService.withdrawFundsDiaspore(Engine.RcnEngine, this.rcnLoansWithBalance);
+ this.txService.registerWithdrawTx(tx, environment.contracts[Engine.RcnEngine].diaspore.debtEngine, this.rcnLoansWithBalance);
+ }
+ this.loadWithdrawBalance();
+ this.retrievePendingTx();
+ }
+ }
+
+ /**
+ * Withdraw diaspore funds
+ */
+ private async withdrawUsdc() {
+ if (this.usdcCanWithdraw) {
+ if (this.usdcLoansWithBalance.length > 0) {
+ const tx = await this.contractsService.withdrawFundsDiaspore(Engine.UsdcEngine, this.usdcLoansWithBalance);
+ this.txService.registerWithdrawTx(tx, environment.contracts[Engine.UsdcEngine].diaspore.debtEngine, this.usdcLoansWithBalance);
+ }
+ this.loadWithdrawBalance();
+ this.retrievePendingTx();
+ }
+ }
+
+ /**
+ * Retrieve pending Tx
+ */
+ private retrievePendingTx() {
+ if (!this.rcnTxSubscription) {
+ this.rcnTxSubscription = true;
+ this.txService.subscribeConfirmedTx(async (tx: Tx) => this.trackWithdrawTx(tx));
+ }
+ if (!this.usdcTxSubscription) {
+ this.usdcTxSubscription = true;
+ this.txService.subscribeConfirmedTx(async (tx: Tx) => this.trackWithdrawTx(tx));
+ }
+ }
+
+ /**
+ * Track tx
+ */
+ private async trackWithdrawTx(tx: Tx) {
+ if (tx.type === Type.withdraw) {
+ await timer(12000).toPromise();
+ this.loadWithdrawBalance();
+ }
+ }
+}
diff --git a/src/app/core/social-container/social-container.component.ts b/src/app/core/social-container/social-container.component.ts
index a72b8b854..31a90a5a9 100644
--- a/src/app/core/social-container/social-container.component.ts
+++ b/src/app/core/social-container/social-container.component.ts
@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
+import { Engine } from '../../models/loan.model';
import { environment } from '../../../environments/environment.prod';
@Component({
@@ -12,6 +13,10 @@ export class SocialContainerComponent implements OnInit {
constructor() { }
ngOnInit() {
- this.linkContract = environment.network.explorer.address.replace('${address}', environment.contracts.diaspore.loanManager);
+ this.linkContract = environment
+ .network
+ .explorer
+ .address
+ .replace('${address}', environment.contracts[Engine.RcnEngine].diaspore.loanManager);
}
}
diff --git a/src/app/dialogs/dialog-approve-contract/dialog-approve-contract.component.html b/src/app/dialogs/dialog-approve-contract/dialog-approve-contract.component.html
index b4c4e034d..8d01e08f5 100644
--- a/src/app/dialogs/dialog-approve-contract/dialog-approve-contract.component.html
+++ b/src/app/dialogs/dialog-approve-contract/dialog-approve-contract.component.html
@@ -19,15 +19,43 @@