...
 
Commits (6)
......@@ -24,6 +24,7 @@ import { OffchainPaymentService } from './offchain-payment.service';
import { Client } from '../../services/api/client';
import { MarketingModule } from '../marketing/marketing.module';
import { BlockchainMarketingModule } from './marketing/marketing.module';
import { GetMetamaskComponent } from './metamask/getmetamask.component';
const cryptoRoutes: Routes = [
{
......@@ -52,6 +53,7 @@ const cryptoRoutes: Routes = [
BlockchainWalletAddressNoticeComponent,
TransactionOverlayComponent,
BlockchainTdeBuyComponent,
GetMetamaskComponent,
],
providers: [
TransactionOverlayService,
......@@ -100,7 +102,8 @@ const cryptoRoutes: Routes = [
BlockchainWalletSelector,
BlockchainWalletAddressNoticeComponent,
TransactionOverlayComponent,
BlockchainTdeBuyComponent
BlockchainTdeBuyComponent,
GetMetamaskComponent,
],
entryComponents: [
BlockchainTdeBuyComponent,
......
......@@ -39,6 +39,10 @@ export class LocalWalletService {
return cb(null, accounts);
}
async setupMetamask() {
return await this.transactionOverlay.waitForSetupMetaMask();
}
async unlock() {
if (this.account && this.privateKey) {
return Promise.resolve(true);
......@@ -46,7 +50,7 @@ export class LocalWalletService {
try {
const { privateKey, secureMode } = await this.transactionOverlay.waitForAccountUnlock(),
account = ethAccount.privateToAccount(privateKey).address;
account = ethAccount.privateToAccount(privateKey).address;
this.privateKey = privateKey;
this.account = account;
......
<m-modal
open="true"
(closed)="close.next(true)"
>
<div class="m-blockchain--marketing--modal">
<h2>Buy ETH</h2>
<div class="m-layout__row m-blockchainEthModal__form">
<div class="m-layout__cell">
<input type="number"
class="m-input"
[(ngModel)]="usd"
max="50"
/>
<label>USD</label>
</div>
<div class="m-layout__cell m-blockchainEthModal__eq">
<span>=</span>
</div>
<div class="m-layout__cell">
<input type="number"
class="m-input"
[ngModel]="eth"
(ngModelChange)="setEth($event)"
/>
<label>ETH</label>
</div>
</div>
<p>ETH is estimated at 1 ETH = <b>$</b>{{ ethRate }}. The checkout process will produce a more accurate ETH value.</p>
<p class="m-error mdl-color-text--red" *ngIf="usd > 50">
You can not purchase more than $50 worth of ETH
</p>
<button class="m-btn m-btn--slim m-btn--action" (click)="buy()" *ngIf="hasMetamask">
Buy with SendWyre
</button>
<a href="https://metamask.io/" target="_blank" *ngIf="!hasMetamask">
<button class="m-btn m-btn--slim m-btn--action">Download Metamask</button>
</a>
</div>
</m-modal>
.m-blockchainEthModal__form {
margin: 36px 0;
.m-input {
padding: 16px;
font-size: 24px;
border-radius: 24px;
letter-spacing: 2px;
text-align: center;
}
.m-layout__cell {
display: inline-flex;
flex-direction: row;
align-items: center;
}
label {
padding: 16px;
font-weight: 600;
}
.m-blockchainEthModal__eq {
flex: 0;
padding: 0 16px;
}
}
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef, Input,
OnDestroy,
OnInit,
ViewChild,
Output,
EventEmitter,
} from '@angular/core';
import { Client } from '../../../services/api/client';
import { MindsTitle } from '../../../services/ux/title';
import { WireCreatorComponent } from '../../wire/creator/creator.component';
import { OverlayModalService } from '../../../services/ux/overlay-modal';
import { BlockchainTdeBuyComponent } from '../tde-buy/tde-buy.component';
import { Session } from '../../../services/session';
import { Web3WalletService } from '../web3-wallet.service';
import { TokenDistributionEventService } from '../contracts/token-distribution-event.service';
import * as BN from 'bn.js';
@Component({
selector: 'm-blockchain__eth-modal',
templateUrl: 'eth-modal.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BlockchainEthModalComponent implements OnInit {
@Input() rate = 1;
@Output() close: EventEmitter<boolean> = new EventEmitter(true);
error: string = '';
usd: number = 40;
hasMetamask: boolean = true; // True by default
constructor(
private web3Wallet: Web3WalletService,
private cd: ChangeDetectorRef,
) { }
ngOnInit() {
// grab latest ETH rate
this.detectMetamask();
}
async detectMetamask() {
this.hasMetamask = !await this.web3Wallet.isLocal();
this.detectChanges();
}
get ethRate(): number {
const tokenUsdRate = 0.15;
const tokenUsd = 1 / tokenUsdRate;
const usd = this.rate / tokenUsd;
return usd;
}
get eth(): number {
return this.usd / this.ethRate;
}
setEth(eth) {
this.usd = eth * this.ethRate;
}
async buy() {
this.error = '';
this.detectChanges();
if (!this.hasMetamask) {
this.error = 'You need to install metamask';
this.detectChanges();
return;
}
if (this.usd > 40) {
this.usd = 40;
this.error = 'You can not purchase more than $40';
this.detectChanges();
return;
}
let win = window.open('/checkout?usd=' + this.usd);
this.close.next(true);
}
detectChanges() {
this.cd.markForCheck();
this.cd.detectChanges();
}
}
......@@ -67,6 +67,7 @@ m-blockchain--marketing {
border: 0;
margin: 16px 0;
margin-left: 12px;
white-space: nowrap;
@include m-theme(){
color: themed($m-white) !important;
background-color: rgba(themed($m-white), 0.3) !important;
......
......@@ -14,6 +14,7 @@ import { BlockchainPurchaseComponent } from './purchase.component';
import { BlockchainMarketingOnboardComponent } from './onboard.component';
import { BlockchainMarketingBlogsComponent } from './blogs.component';
import { BlockchainMarketingCountdownComponent } from './countdown.component';
import { BlockchainEthModalComponent } from './eth-modal.component';
import { ModalsModule } from '../../modals/modals.module';
const routes: Routes = [
......@@ -46,6 +47,7 @@ const routes: Routes = [
BlockchainPurchaseComponent,
BlockchainMarketingBlogsComponent,
BlockchainMarketingCountdownComponent,
BlockchainEthModalComponent,
],
exports: [
BlockchainMarketingComponent,
......
......@@ -48,7 +48,7 @@
<tr style="height:16px;">
<td class="m-blockchain--marketing--pledges--amounts--label">
</td>
<td class="m-blockchain--marketing--pledge--input">
<td class="m-blockchain--marketing--pledge--input" style="text-align: left">
<m-tooltip anchor="left" i18n="@@MINDS__TOPBAR__TOP__OPEN_TRENDING_ITEM_TOOLTIP">
<span m-tooltip--anchor class="m-blockchain--marketing--pledges--eth-subtext">1 Token = $0.15 USD = {{ 1 / rate}} ETH</span>
......@@ -56,6 +56,12 @@
ETH amount is based on the current USD/ETH rate and is updated in regular intervals
</ng-container>
</m-tooltip>
<span class="m-layout__spacer"></span>
<button
(click)="purchaseEth()"
class="m-btn m-btn--action">
Buy ETH
</button>
</td>
</tr>
</table>
......@@ -160,3 +166,10 @@
</div>
</m-modal>
<m-blockchain__eth-modal
*ngIf="showEthModal"
[rate]="rate"
(close)="closePurchaseEth()"
>
</m-blockchain__eth-modal>
......@@ -7,6 +7,9 @@ import {
OnInit,
ViewChild,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
import { Client } from '../../../services/api/client';
import { MindsTitle } from '../../../services/ux/title';
import { WireCreatorComponent } from '../../wire/creator/creator.component';
......@@ -44,6 +47,7 @@ export class BlockchainPurchaseComponent implements OnInit {
minds = window.Minds;
showPledgeModal: boolean = false;
showLoginModal: boolean = false;
showEthModal: boolean = false;
confirming: boolean = false;
confirmed: boolean = false;
error: string;
......@@ -52,6 +56,8 @@ export class BlockchainPurchaseComponent implements OnInit {
inProgress: boolean = false;
rate: number = 0;
paramsSubscription: Subscription;
constructor(
protected client: Client,
protected changeDetectorRef: ChangeDetectorRef,
......@@ -59,7 +65,8 @@ export class BlockchainPurchaseComponent implements OnInit {
protected overlayModal: OverlayModalService,
protected web3Wallet: Web3WalletService,
protected tde: TokenDistributionEventService,
public session: Session
public session: Session,
private route: ActivatedRoute,
) { }
ngOnInit() {
......@@ -67,6 +74,17 @@ export class BlockchainPurchaseComponent implements OnInit {
this.load().then(() => {
this.amount = 0.25;
});
this.paramsSubscription = this.route.params.subscribe(params => {
if (params.purchaseEth) {
this.purchaseEth();
}
});
}
ngOnDestroy() {
if (this.paramsSubscription) {
this.paramsSubscription.unsubscribe();
}
}
get amount() {
......@@ -153,6 +171,20 @@ export class BlockchainPurchaseComponent implements OnInit {
}, 2000);*/
}
purchaseEth() {
this.showEthModal = true;
this.detectChanges();
//let win = window.open('/checkout');
//win.onload = function() {
// alert('opened');
//}
}
closePurchaseEth() {
this.showEthModal = false;
this.detectChanges();
}
closeLoginModal() {
this.showPledgeModal = true;
this.showLoginModal = false;
......
<h2 class="m-get-metamask--title">
Setup Your OnChain Address to buy, send and receive crypto
</h2>
<div class="mdl-grid mdl-grid--no-spacing">
<div class="mdl-cell mdl-cell--4-col m-get-metamask--logo-container">
<img class="m-get-metamask--logo" [src]="minds.cdn_assets_url + 'assets/ext/metamask.png'" />
</div>
<div class="mdl-cell mdl-cell--4-col">
<h4>MetaMask</h4>
<p>Minds recommends using MetaMask for the most seamless user experience.</p>
<p class="m-get-metamask--button">
<a href="https://metamask.io/" target="_blank">
<button class="m-btn m-btn--slim m-btn--action">Download Metamask</button>
</a>
</p>
<p>
Be sure to refresh your browser after installing the plugin.
You can also
<a class="m-get-metamask--create-link" (click)="create()">create</a> or
<a class="m-get-metamask--provide-link" (click)="unlock()">provide</a> your own existing address.
</p>
<p class="m-get-metamask--button">
<button class="m-get-metamask--cancel-btn m-btn" (click)="cancel()">Cancel</button>
</p>
</div>
</div>
m-get-metamask {
padding: 16px;
max-width: 100%;
box-sizing: border-box;
display: block;
.m-get-metamask--title {
font-size: 42px;
margin: 0;
margin-bottom: 16px;
font-weight: bold;
line-height: 42px;
@include m-theme(){
color: themed($m-grey-800);
}
@media screen and (max-width: $max-mobile) {
font-size: 24px;
line-height: 1.2;
}
}
h4 {
font-weight: 600;
}
.m-get-metamask--logo-container {
display: flex;
justify-content: center;
align-content: center;
flex-direction: row;
.m-get-metamask--logo {
width: 227px;
height: 227px;
@media screen and (max-width: $max-mobile) {
width: 100px;
height: 100px;
}
}
}
.m-get-metamask--button {
text-align: left;
}
}
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { GetMetamaskComponent } from './getmetamask.component';
describe('GetMetamaskComponent', () => {
let comp: GetMetamaskComponent;
let fixture: ComponentFixture<GetMetamaskComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [GetMetamaskComponent]
}).compileComponents();
}));
beforeEach((done) => {
jasmine.MAX_PRETTY_PRINT_DEPTH = 2;
fixture = TestBed.createComponent(GetMetamaskComponent);
comp = fixture.componentInstance;
fixture.detectChanges();
if (fixture.isStable()) {
done();
} else {
fixture.whenStable()
.then(() => {
fixture.detectChanges();
done();
});
}
});
it('should have a title', () => {
const title = fixture.debugElement.query(By.css('.m-get-metamask--title'));
expect(title).not.toBeNull();
});
it('should raise a create address event', () => {
spyOn(comp.actioned, 'emit').and.callThrough();
const link = fixture.debugElement.query(By.css('.m-get-metamask--create-link'));
link.nativeElement.click();
expect(comp.actioned.emit).toHaveBeenCalledWith(GetMetamaskComponent.ACTION_CREATE);
});
it('should raise an provide address event', () => {
spyOn(comp.actioned, 'emit').and.callThrough();
const link = fixture.debugElement.query(By.css('.m-get-metamask--provide-link'));
link.nativeElement.click();
expect(comp.actioned.emit).toHaveBeenCalledWith(GetMetamaskComponent.ACTION_UNLOCK);
});
it('should raise a cancel', () => {
spyOn(comp.actioned, 'emit').and.callThrough();
const link = fixture.debugElement.query(By.css('.m-get-metamask--cancel-btn'));
link.nativeElement.click();
expect(comp.actioned.emit).toHaveBeenCalledWith(GetMetamaskComponent.ACTION_CANCEL);
});
});
import { Component, EventEmitter, Output } from '@angular/core';
@Component({
selector: 'm-get-metamask',
templateUrl: 'getmetamask.component.html'
})
export class GetMetamaskComponent {
public static ACTION_CREATE = 'create';
public static ACTION_UNLOCK = 'unlock';
public static ACTION_CANCEL = 'cancel';
@Output() actioned: EventEmitter<String> = new EventEmitter();
minds: Minds = window.Minds;
create() {
this.actioned.emit(GetMetamaskComponent.ACTION_CREATE);
}
unlock() {
this.actioned.emit(GetMetamaskComponent.ACTION_UNLOCK);
}
cancel() {
this.actioned.emit(GetMetamaskComponent.ACTION_CANCEL);
}
}
<div class="m--blockchain--transaction-overlay">
<div class="m--blockchain--transaction-overlay--content">
<img [src]="minds.cdn_assets_url + 'assets/logos/logo.svg'" class="m--blockchain--transaction-overlay--logo" />
<h2 class="m--blockchain--transaction-overlay--title" i18n="@@M__BLOCKCHAIN__TX_OVERLAY__COMPLETE_YOUR_TX">
Complete your OnChain transaction
</h2>
<ng-container *ngIf="comp !== COMP_SETUP_METAMASK">
<img [src]="minds.cdn_assets_url + 'assets/logos/logo.svg'" class="m--blockchain--transaction-overlay--logo" />
<h2 class="m--blockchain--transaction-overlay--title" i18n="@@M__BLOCKCHAIN__TX_OVERLAY__COMPLETE_YOUR_TX" *ngIf="comp !== COMP_SETUP_METAMASK">
Complete your OnChain transaction
</h2>
<p class="m--blockchain--transaction-overlay--note" *ngIf="comp === COMP_METAMASK" i18n="@@M__BLOCKCHAIN__TX_OVERLAY__COMPLETE_USING_METAMASK">
Please open your Metamask client to complete the transaction.
</p>
<p class="m--blockchain--transaction-overlay--note" *ngIf="comp === COMP_METAMASK" i18n="@@M__BLOCKCHAIN__TX_OVERLAY__COMPLETE_USING_METAMASK">
Please open your Metamask client to complete the transaction.
</p>
<p class="m--blockchain--transaction-overlay--note" *ngIf="comp !== COMP_UNLOCK && !!message" i18n="@@M__BLOCKCHAIN__TX_OVERLAY__YOURE_APPROVING_MESSAGE">
You are approving a {{ message }}
</p>
</ng-container>
<p class="m--blockchain--transaction-overlay--note" *ngIf="comp !== COMP_UNLOCK && !!message" i18n="@@M__BLOCKCHAIN__TX_OVERLAY__YOURE_APPROVING_MESSAGE">
You are approving a {{ message }}
</p>
<ng-container *ngIf="comp === COMP_SETUP_METAMASK">
<m-get-metamask (actioned)="handleMetamaskAction($event)"></m-get-metamask>
</ng-container>
<ng-container *ngIf="comp === COMP_UNLOCK">
<p class="m--blockchain--transaction-overlay--note" i18n="@@M__BLOCKCHAIN__TX_OVERLAY__ENTER_PKEY_OR_DROP_CSV">
......
......@@ -10,6 +10,7 @@ import { FormsModule } from '@angular/forms';
import { MaterialSwitchMock } from '../../../../tests/material-switch-mock.spec';
import { web3WalletServiceMock } from '../../../../tests/web3-wallet-service-mock.spec';
import { Web3WalletService } from '../web3-wallet.service';
import { GetMetamaskComponent } from '../metamask/getmetamask.component';
describe('TransactionOverlayComponent', () => {
......@@ -19,8 +20,8 @@ describe('TransactionOverlayComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MaterialSwitchMock, TransactionOverlayComponent],
imports: [RouterTestingModule, FormsModule],
declarations: [MaterialSwitchMock, TransactionOverlayComponent, GetMetamaskComponent],
imports: [RouterTestingModule, FormsModule ],
providers: [
{ provide: TransactionOverlayService, useValue: transactionOverlayService },
{ provide: TokenContractService, useValue: TokenContractService },
......@@ -58,4 +59,12 @@ describe('TransactionOverlayComponent', () => {
expect(havingIssues.nativeElement.href).toMatch(/https?:\/\/.*\/token/);
});
it('should display get metamask component', () => {
comp.comp = comp.COMP_SETUP_METAMASK;
comp.detectChanges();
fixture.detectChanges();
const note: DebugElement = fixture.debugElement.query(By.css('.m-get-metamask--title'));
expect(note.nativeElement.textContent.trim()).toContain('Setup Your OnChain Address to buy, send and receive crypto');
});
});
......@@ -15,6 +15,7 @@ import { TransactionOverlayService } from './transaction-overlay.service';
import { TokenContractService } from '../contracts/token-contract.service';
import { Router } from '@angular/router';
import { Web3WalletService } from '../web3-wallet.service';
import { GetMetamaskComponent } from '../metamask/getmetamask.component';
@Component({
moduleId: module.id,
......@@ -49,6 +50,7 @@ export class TransactionOverlayComponent implements OnInit {
readonly COMP_METAMASK = 1;
readonly COMP_LOCAL = 2;
readonly COMP_UNLOCK = 3;
readonly COMP_SETUP_METAMASK = 4;
constructor(
protected service: TransactionOverlayService,
......@@ -335,4 +337,13 @@ export class TransactionOverlayComponent implements OnInit {
this.cd.markForCheck();
this.cd.detectChanges();
}
handleMetamaskAction($event) {
let next = $event;
if ($event === GetMetamaskComponent.ACTION_CANCEL) {
next = false;
}
this.eventEmitter.next($event);
this.hide();
}
}
......@@ -10,6 +10,21 @@ export class TransactionOverlayService {
this.comp = comp;
}
waitForSetupMetaMask(): Promise<string> {
const compEventEmitter = this.comp.show(this.comp.COMP_SETUP_METAMASK);
return new Promise((resolve, reject) => {
const subscription: Subscription = compEventEmitter.subscribe(data => {
subscription.unsubscribe();
if (data && !(data instanceof Error)) {
resolve(data);
} else {
reject((data && data.message) || 'User cancelled');
}
});
});
}
waitForAccountUnlock(): Promise<{ privateKey, account, secureMode }> {
let compEventEmitter = this.comp.show(this.comp.COMP_UNLOCK);
......
......@@ -21,7 +21,7 @@ export class Web3WalletService {
constructor(
protected localWallet: LocalWalletService,
protected transactionOverlay: TransactionOverlayService
protected transactionOverlay: TransactionOverlayService,
) { }
// Wallet
......@@ -56,7 +56,9 @@ export class Web3WalletService {
async getBalance(address): Promise<string | false> {
return new Promise<string | false>((resolve, reject) => {
this.eth.getBalance(address, (error, result) => {
if (!window.web3 && !window.web3.eth)
return reject(false);
window.web3.eth.getBalance(address, (error, result) => {
if (error) {
console.log(error);
return reject(false);
......@@ -75,6 +77,12 @@ export class Web3WalletService {
return this.local;
}
async setupMetamask() {
if (await this.isLocal()) {
return await this.localWallet.setupMetamask();
}
}
async unlock() {
if ((await this.isLocal()) && (await this.isLocked())) {
await this.localWallet.unlock();
......
......@@ -28,6 +28,7 @@ import { TransactionOverlayService } from '../../blockchain/transaction-overlay/
import { localWalletServiceMock } from '../../../../tests/local-wallet-service-mock.spec';
import { sessionMock } from '../../../../tests/session-mock.spec';
import { Session } from '../../../services/session';
import { RouterTestingModule } from '@angular/router/testing';
/* tslint:disable */
@Component({
......@@ -111,6 +112,7 @@ let web3WalletServiceMock = new function () {
this.onChainInterfaceLabel = 'Metamask';
this.unavailable = false;
this.locked = false;
this.localWallet = false;
this.isUnavailable = jasmine.createSpy('isUnavailable').and.callFake(() => {
return this.unavailable;
......@@ -134,6 +136,11 @@ let web3WalletServiceMock = new function () {
return this.balance;
});
this.isLocal = jasmine.createSpy('isLocal').and.callFake(async () => {
return this.isLocalWallet;
});
this.getOnChainInterfaceLabel = jasmine.createSpy('getOnChainInterfaceLabel').and.callFake(() => {
return this.onChainInterfaceLabel ? this.onChainInterfaceLabel: 'Metamask';
});
......@@ -368,7 +375,7 @@ describe('BoostCreatorComponent', () => {
BoostP2PSearchMock,
BoostCheckoutMock,
],
imports: [FormsModule],
imports: [FormsModule, RouterTestingModule],
providers: [
{ provide: Session, useValue: sessionMock },
{ provide: Client, useValue: clientMock },
......
......@@ -9,7 +9,8 @@ import { TokenContractService } from '../../blockchain/contracts/token-contract.
import { BoostContractService } from '../../blockchain/contracts/boost-contract.service';
import { Web3WalletService } from '../../blockchain/web3-wallet.service';
import { OffchainPaymentService } from '../../blockchain/offchain-payment.service';
import { GetMetamaskComponent } from '../../blockchain/metamask/getmetamask.component';
import { Router } from '@angular/router';
type CurrencyType = 'offchain' | 'usd' | 'onchain' | 'creditcard';
export type BoostType = 'p2p' | 'newsfeed' | 'content';
......@@ -112,7 +113,8 @@ export class BoostCreatorComponent implements AfterViewInit {
private tokensContract: TokenContractService,
private boostContract: BoostContractService,
private web3Wallet: Web3WalletService,
private offchainPayment: OffchainPaymentService
private offchainPayment: OffchainPaymentService,
protected router: Router,
) { }
ngOnInit() {
......@@ -554,7 +556,22 @@ export class BoostCreatorComponent implements AfterViewInit {
if (this.web3Wallet.isUnavailable()) {
throw new Error('No Ethereum wallets available on your browser.');
} else if (!(await this.web3Wallet.unlock())) {
}
if (await this.web3Wallet.isLocal()) {
const action = await this.web3Wallet.setupMetamask();
switch (action) {
case GetMetamaskComponent.ACTION_CREATE:
this.router.navigate(['/wallet']);
this.inProgress = false;
this.overlayModal.dismiss();
break;
case GetMetamaskComponent.ACTION_CANCEL:
return;
}
}
if (!(await this.web3Wallet.unlock())) {
throw new Error('Your Ethereum wallet is locked or connected to another network.');
}
......
......@@ -126,25 +126,35 @@ m-wallet--balance-money, m-wallet--balance-reward, m-wallet--balance-tokens {
}
}
.m-wallet--balance--metamask {
margin-top: 16px;
.m-wallet--balance--metamask {
margin-top: 16px;
.m-wallet--balance--metamask-logo {
width: 50px;
}
.m-wallet--balance--metamask-logo {
width: 50px;
}
.m-btn--slim {
height: 30px;
margin: 0 0 0 28px;
color: #888;
}
.m-btn--slim {
height: 30px;
margin: 0 0 0 28px;
color: #888;
}
p {
text-align: left;
margin: 0;
padding: 0 8px;
color: #666;
}
p {
text-align: left;
margin: 0;
padding: 0 8px;
color: #666;
}
}
}
\ No newline at end of file
.m-walletBalance__purchaseBtns {
margin: 16px;
box-sizing: border-box;
.m-btn {
line-height: 32px;
text-decoration: none;
margin: 8px;
}
}
......@@ -70,7 +70,41 @@
</div>
<div class="m-wallet--balance--addresses-address">
<div class="m-wallet--balance--addresses-address-col">
<label class="m-wallet--balance--addresses-address-label">
<span i18n="@@WALLET__BALANCES__TOKENS__ADDRESS_LABEL">ETH Balance</span>
<m-tooltip icon="help">
ETH is required to purchase Minds Token and to fund OnChain transactions
</m-tooltip>
</label>
</div>
<div class="m-wallet--balance--addresses-address-col">
<span class="m-wallet--balance--addresses-address-balance">
{{ ethBalance | token: 18 | number }}
</span>
</div>
</div>
</div>
<div class="m-walletBalance__purchaseBtns" *ngIf="!isLocal">
<a routerLink="/tokens"
class="m-btn m-btn--slim m-btn--action"
>
Buy Tokens
</a>
<a [routerLink]="['/token', { 'purchaseEth': 'open' }]"
class="m-btn m-btn--slim m-btn--action"
>
Buy ETH
</a>
</div>
<div class="m-wallet--balance--metamask" *ngIf="isLocal">
<p>Setup Your OnChain Address to buy send and receive crypto</p>
<img class="m-wallet--balance--metamask-logo" [src]="minds.cdn_assets_url + 'assets/ext/metamask.png'" />
......
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import {RouterTestingModule} from '@angular/router/testing';
import { WalletBalanceTokensComponent } from './balance.component';
import { TokenPipe } from '../../../../common/pipes/token.pipe';
......@@ -44,6 +45,9 @@ describe('WalletBalanceTokensComponent', () => {
this.isLocal = jasmine.createSpy('getCurrentWallet').and.callFake(async () => {
return false;
});
this.getBalance = jasmine.createSpy('getBalance').and.callFake(async() => {
return 0;
});
}
const Web3WalletLocalServiceMock = new function () {
......@@ -64,6 +68,7 @@ describe('WalletBalanceTokensComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ RouterTestingModule ],
declarations: [
TokenPipe,
TooltipComponentMock,
......
......@@ -18,6 +18,7 @@ export class WalletBalanceTokensComponent implements OnInit {
inProgress: boolean = false;
balance: number = 0;
testnetBalance: number = 0;
ethBalance: string = '0';
addresses: Array<any> = [];
minds = window.Minds;
isLocal:boolean = false;
......@@ -38,6 +39,7 @@ export class WalletBalanceTokensComponent implements OnInit {
async load() {
await this.loadRemote();
await this.loadLocal();
await this.loadEth();
this.isLocal = await this.web3Wallet.isLocal();
this.detectChanges();
}
......@@ -65,12 +67,22 @@ export class WalletBalanceTokensComponent implements OnInit {
'address': address,
'balance': balance[0].toString(),
});
this.detectChanges();
} catch (e) {
console.log(e);
}
}
async loadEth() {
const address = await this.web3Wallet.getCurrentWallet();
if (!address)
return;
const ethBalance = await this.web3Wallet.getBalance(address);
this.ethBalance = ethBalance ? ethBalance : '0';
this.detectChanges();
}
async loadRemote() {
this.inProgress = true;
this.detectChanges();
......
......@@ -6,6 +6,7 @@ import { Client } from '../../../services/api';
import { Session } from '../../../services/session';
import { WireService } from '../wire.service';
import { Web3WalletService } from '../../blockchain/web3-wallet.service';
import { GetMetamaskComponent } from '../../blockchain/metamask/getmetamask.component';
import { TokenContractService } from '../../blockchain/contracts/token-contract.service';
import { MindsUser } from '../../../interfaces/entities';
import { Router } from '@angular/router';
......@@ -481,6 +482,19 @@ export class WireCreatorComponent {
this.submitted = true;
this.error = '';
if (await this.web3Wallet.isLocal()) {
const action = await this.web3Wallet.setupMetamask();
switch (action) {
case GetMetamaskComponent.ACTION_CREATE:
this.router.navigate(['/wallet']);
this.inProgress = false;
this.overlayModal.dismiss();
break;
case GetMetamaskComponent.ACTION_CANCEL:
return;
}
}
let { done } = await this.wireService.submitWire(this.wire);
if (done) {
......
......@@ -7,6 +7,7 @@ import { Session } from '../../../services/session';
import { WireService } from '../wire.service';
import { Web3WalletService } from '../../blockchain/web3-wallet.service';
import { TokenContractService } from '../../blockchain/contracts/token-contract.service';
import { GetMetamaskComponent } from '../../blockchain/metamask/getmetamask.component';
import { MindsUser } from '../../../interfaces/entities';
import { Router } from '@angular/router';
......@@ -371,6 +372,19 @@ export class WirePaymentsCreatorComponent {
this.submitted = true;
this.error = '';
if (await this.web3Wallet.isLocal()) {
const action = await this.web3Wallet.setupMetamask();
switch (action) {
case GetMetamaskComponent.ACTION_CREATE:
this.router.navigate(['/wallet']);
this.inProgress = false;
this.overlayModal.dismiss();
break;
case GetMetamaskComponent.ACTION_CANCEL:
return;
}
}
let { done } = await this.wireService.submitWire(this.wire);
if (done) {
......
import { and } from "@angular/router/src/utils/collection";
// TODO actually implement these mocks when necessary for testing
export let web3WalletServiceMock = new function () {
......@@ -6,6 +8,7 @@ export let web3WalletServiceMock = new function () {
this.onChainInterfaceLabel = 'Metamask';
this.unavailable = false;
this.locked = false;
this.isLocalWallet = false;
this.isUnavailable = jasmine.createSpy('isUnavailable').and.callFake(() => {
return this.unavailable;
......@@ -29,6 +32,10 @@ export let web3WalletServiceMock = new function () {
return this.balance;
});
this.isLocal = jasmine.createSpy('isLocal').and.callFake(async () => {
return this.isLocalWallet;
});
this.getOnChainInterfaceLabel = jasmine.createSpy('getOnChainInterfaceLabel').and.callFake(() => {
return this.onChainInterfaceLabel ? this.onChainInterfaceLabel: 'Metamask';
});
......