diff --git a/common/actions/swap/actionCreators.ts b/common/actions/swap/actionCreators.ts index e638044f..2958a84f 100644 --- a/common/actions/swap/actionCreators.ts +++ b/common/actions/swap/actionCreators.ts @@ -29,7 +29,7 @@ export function loadBityRatesSucceededSwap( export type TLoadShapeshiftRatesSucceededSwap = typeof loadShapeshiftRatesSucceededSwap; export function loadShapeshiftRatesSucceededSwap( - payload + payload: interfaces.LoadShapeshiftRatesSucceededSwapAction['payload'] ): interfaces.LoadShapeshiftRatesSucceededSwapAction { return { type: TypeKeys.SWAP_LOAD_SHAPESHIFT_RATES_SUCCEEDED, diff --git a/common/api/bity.ts b/common/api/bity.ts index 8ba10d8d..8f74f70d 100644 --- a/common/api/bity.ts +++ b/common/api/bity.ts @@ -29,10 +29,14 @@ const repOptions = { name: 'Augur' }; +export interface MappedRates { + [key: string]: any; +} + export function getAllRates() { - const mappedRates = {}; + const mappedRates: MappedRates = {}; return _getAllRates().then(bityRates => { - bityRates.objects.forEach(each => { + bityRates.objects.forEach((each: any) => { const pairName = each.pair; const from = { id: pairName.substring(0, 3) }; const to = { id: pairName.substring(3, 6) }; diff --git a/common/api/gas.ts b/common/api/gas.ts index 1f0400c1..3bdd3b70 100644 --- a/common/api/gas.ts +++ b/common/api/gas.ts @@ -1,4 +1,5 @@ import { checkHttpStatus, parseJSON } from './utils'; +import { Omit } from 'react-redux'; const MAX_GAS_FAST = 250; @@ -21,15 +22,29 @@ export interface GasEstimates { isDefault: boolean; } +interface GasExpressResponse { + block_time: number; + blockNum: number; + fast: number; + fastest: number; + safeLow: number; + standard: number; +} + export function fetchGasEstimates(): Promise { return fetch('https://dev.blockscale.net/api/gasexpress.json', { mode: 'cors' }) .then(checkHttpStatus) .then(parseJSON) - .then((res: object) => { + .then((res: GasExpressResponse) => { // Make sure it looks like a raw gas estimate, and it has valid values - const keys = ['safeLow', 'standard', 'fast', 'fastest']; + const keys: (keyof Omit)[] = [ + 'safeLow', + 'standard', + 'fast', + 'fastest' + ]; keys.forEach(key => { if (typeof res[key] !== 'number') { throw new Error( diff --git a/common/api/shapeshift.ts b/common/api/shapeshift.ts index 01100505..84244709 100644 --- a/common/api/shapeshift.ts +++ b/common/api/shapeshift.ts @@ -28,6 +28,44 @@ export const SHAPESHIFT_TOKEN_WHITELIST = [ ]; export const SHAPESHIFT_WHITELIST = [...SHAPESHIFT_TOKEN_WHITELIST, 'ETH', 'ETC', 'BTC']; +interface IPairData { + limit: number; + maxLimit: number; + min: number; + minerFee: number; + pair: string; + rate: string; +} + +interface IExtraPairData { + status: string; + image: string; + name: string; +} + +interface IAvailablePairData { + [pairName: string]: IExtraPairData; +} + +interface ShapeshiftMarketInfo { + rate: string; + limit: number; + pair: string; + maxLimit: number; + min: number; + minerFee: number; +} + +interface TokenMap { + [pairName: string]: { + id: string; + rate: string; + limit: number; + min: number; + options: (IExtraPairData & { id: string })[]; + }; +} + class ShapeshiftService { public whitelist = SHAPESHIFT_WHITELIST; private url = SHAPESHIFT_BASE_URL; @@ -36,13 +74,18 @@ class ShapeshiftService { 'Content-Type': 'application/json' }; - public checkStatus(address) { + public checkStatus(address: string) { return fetch(`${this.url}/txStat/${address}`) .then(checkHttpStatus) .then(parseJSON); } - public sendAmount(withdrawal, originKind, destinationKind, destinationAmount) { + public sendAmount( + withdrawal: string, + originKind: string, + destinationKind: string, + destinationAmount: number + ) { const pair = `${originKind.toLowerCase()}_${destinationKind.toLowerCase()}`; return fetch(`${this.url}/sendamount`, { @@ -81,7 +124,7 @@ class ShapeshiftService { return mappedRates; }; - private getPairRates(marketInfo) { + private getPairRates(marketInfo: ShapeshiftMarketInfo[]) { const filteredMarketInfo = marketInfo.filter(obj => { const { pair } = obj; const pairArr = pair.split('_'); @@ -97,7 +140,7 @@ class ShapeshiftService { return pairRates; } - private async checkAvl(pairRates) { + private async checkAvl(pairRates: IPairData[]) { const avlCoins = await this.getAvlCoins(); const mapAvl = pairRates.map(p => { const { pair } = p; @@ -121,7 +164,8 @@ class ShapeshiftService { }; } }); - return mapAvl; + const filered = mapAvl.filter(v => v); + return filered as (IPairData & IAvailablePairData)[]; } private getAvlCoins() { @@ -130,7 +174,7 @@ class ShapeshiftService { .then(parseJSON); } - private getSinglePairRate(pair) { + private getSinglePairRate(pair: string) { return fetch(`${this.url}/rate/${pair}`) .then(checkHttpStatus) .then(parseJSON); @@ -142,12 +186,12 @@ class ShapeshiftService { .then(parseJSON); } - private isWhitelisted(coin) { + private isWhitelisted(coin: string) { return this.whitelist.includes(coin); } - private mapMarketInfo(marketInfo) { - const tokenMap = {}; + private mapMarketInfo(marketInfo: (IPairData & IAvailablePairData)[]) { + const tokenMap: TokenMap = {}; marketInfo.forEach(m => { const originKind = m.pair.substring(0, 3); const destinationKind = m.pair.substring(4, 7); diff --git a/common/assets/images/favicon.png b/common/assets/images/favicon.png index efe18386..a92338ad 100644 Binary files a/common/assets/images/favicon.png and b/common/assets/images/favicon.png differ diff --git a/common/assets/images/logo-shapeshift-no-text.svg b/common/assets/images/logo-shapeshift-no-text.svg new file mode 100644 index 00000000..b6ec8506 --- /dev/null +++ b/common/assets/images/logo-shapeshift-no-text.svg @@ -0,0 +1,97 @@ + + + + Artboard + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/assets/styles/bootstrap/mixins.less b/common/assets/styles/bootstrap/mixins.less index 0cdf66e0..eeee4418 100755 --- a/common/assets/styles/bootstrap/mixins.less +++ b/common/assets/styles/bootstrap/mixins.less @@ -1,36 +1,35 @@ // Mixins // -------------------------------------------------- // Utilities -@import "mixins/hide-text.less"; -@import "mixins/opacity.less"; -@import "mixins/image.less"; -@import "mixins/labels.less"; -@import "mixins/reset-filter.less"; -@import "mixins/resize.less"; -@import "mixins/responsive-visibility.less"; -@import "mixins/size.less"; -@import "mixins/tab-focus.less"; -@import "mixins/reset-text.less"; -@import "mixins/text-emphasis.less"; -@import "mixins/text-overflow.less"; -@import "mixins/vendor-prefixes.less"; +@import 'mixins/hide-text.less'; +@import 'mixins/opacity.less'; +@import 'mixins/image.less'; +@import 'mixins/labels.less'; +@import 'mixins/reset-filter.less'; +@import 'mixins/resize.less'; +@import 'mixins/responsive-visibility.less'; +@import 'mixins/size.less'; +@import 'mixins/reset-text.less'; +@import 'mixins/text-emphasis.less'; +@import 'mixins/text-overflow.less'; +@import 'mixins/vendor-prefixes.less'; // Components -@import "mixins/alerts.less"; -@import "mixins/buttons.less"; -@import "mixins/panels.less"; -@import "mixins/pagination.less"; -@import "mixins/list-group.less"; -@import "mixins/nav-divider.less"; -@import "mixins/forms.less"; -@import "mixins/progress-bar.less"; -@import "mixins/table-row.less"; +@import 'mixins/alerts.less'; +@import 'mixins/buttons.less'; +@import 'mixins/panels.less'; +@import 'mixins/pagination.less'; +@import 'mixins/list-group.less'; +@import 'mixins/nav-divider.less'; +@import 'mixins/forms.less'; +@import 'mixins/progress-bar.less'; +@import 'mixins/table-row.less'; // Skins -@import "mixins/background-variant.less"; -@import "mixins/border-radius.less"; -@import "mixins/gradients.less"; +@import 'mixins/background-variant.less'; +@import 'mixins/border-radius.less'; +@import 'mixins/gradients.less'; // Layout -@import "mixins/clearfix.less"; -@import "mixins/center-block.less"; -@import "mixins/nav-vertical-align.less"; -@import "mixins/grid-framework.less"; -@import "mixins/grid.less"; +@import 'mixins/clearfix.less'; +@import 'mixins/center-block.less'; +@import 'mixins/nav-vertical-align.less'; +@import 'mixins/grid-framework.less'; +@import 'mixins/grid.less'; diff --git a/common/assets/styles/bootstrap/mixins/forms.less b/common/assets/styles/bootstrap/mixins/forms.less index c874b077..cde07d25 100755 --- a/common/assets/styles/bootstrap/mixins/forms.less +++ b/common/assets/styles/bootstrap/mixins/forms.less @@ -20,10 +20,10 @@ // Set the border and box shadow on specific inputs to match .form-control { border-color: @border-color; - .box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075)); // Redeclare so transitions work + .box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.075)); // Redeclare so transitions work &:focus { border-color: darken(@border-color, 10%); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 3px rgba(@brand-primary, .5); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 3px rgba(@brand-primary, 0.5); } } // Set validation states also for addons @@ -51,11 +51,10 @@ // Example usage: change the default blue border and shadow to white for better // contrast against a dark gray background. .form-control-focus(@color: @input-border-focus) { - @color-rgba: rgba(red(@color), green(@color), blue(@color), .6); + @color-rgba: rgba(red(@color), green(@color), blue(@color), 0.6); &:focus { border-color: @color; - outline: 0; - .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}"); + .box-shadow(~'inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}'); } } diff --git a/common/assets/styles/bootstrap/mixins/tab-focus.less b/common/assets/styles/bootstrap/mixins/tab-focus.less deleted file mode 100755 index d12b9b9f..00000000 --- a/common/assets/styles/bootstrap/mixins/tab-focus.less +++ /dev/null @@ -1,6 +0,0 @@ -// WebKit-style focus - -.tab-focus() { - outline: thin dotted; - outline-offset: 3px; -} diff --git a/common/assets/styles/etherwallet-ext-custom.less b/common/assets/styles/etherwallet-ext-custom.less index bf904a45..c4e5552d 100755 --- a/common/assets/styles/etherwallet-ext-custom.less +++ b/common/assets/styles/etherwallet-ext-custom.less @@ -7,10 +7,6 @@ position: absolute; left: 5px; top: 5px; - &:hover, - &:active { - outline: 0; - } } } diff --git a/common/components/AmountField.tsx b/common/components/AmountField.tsx index 9e71fb67..69cee947 100644 --- a/common/components/AmountField.tsx +++ b/common/components/AmountField.tsx @@ -18,7 +18,7 @@ export const AmountField: React.SFC = ({ (
-
- + Shapeshift Logo
diff --git a/common/components/BalanceSidebar/Promos.scss b/common/components/BalanceSidebar/Promos.scss index 505dda6c..8dab7958 100644 --- a/common/components/BalanceSidebar/Promos.scss +++ b/common/components/BalanceSidebar/Promos.scss @@ -85,7 +85,6 @@ height: 12px; border: 3px solid $gray-lightest; border-radius: 100%; - outline: none; opacity: 0.6; &.is-active { @@ -96,7 +95,7 @@ // Per-promo customizations &--shapeshift { - background-color: #263A52; + background-color: #263a52; .Promos-promo-images { max-width: 130px; diff --git a/common/components/BalanceSidebar/Promos.tsx b/common/components/BalanceSidebar/Promos.tsx index 32f1fbfe..2c59325a 100644 --- a/common/components/BalanceSidebar/Promos.tsx +++ b/common/components/BalanceSidebar/Promos.tsx @@ -2,21 +2,29 @@ import React from 'react'; import { TransitionGroup, CSSTransition } from 'react-transition-group'; import { HardwareWallets, Coinbase, Shapeshift } from './PromoComponents'; import './Promos.scss'; +import { connect } from 'react-redux'; +import { AppState } from '../../reducers'; -const promos = [HardwareWallets, Coinbase, Shapeshift]; - -const CarouselAnimation = ({ children, ...props }) => ( +const CarouselAnimation = ({ children, ...props }: any) => ( {children} ); +// Don't change Coinbase index +const promos = [HardwareWallets, Coinbase, Shapeshift]; + interface State { activePromo: number; } -export default class Promos extends React.PureComponent<{}, State> { +interface StateProps { + wallet: AppState['wallet']['inst']; +} + +class PromosClass extends React.PureComponent { public timer: any = null; + public promos = [HardwareWallets, Coinbase, Shapeshift]; public state = { activePromo: parseInt(String(Math.random() * promos.length), 10) @@ -30,13 +38,27 @@ export default class Promos extends React.PureComponent<{}, State> { clearInterval(this.timer); } + public getPromo() { + const { activePromo } = this.state; + const { wallet } = this.props; + if (activePromo === 1) { + if (wallet) { + return ; + } else { + return ; + } + } else { + return promos[activePromo]; + } + } + public render() { const { activePromo } = this.state; return (
- {promos[activePromo]} + {this.getPromo()}
{promos.map((_, index) => { @@ -64,3 +86,11 @@ export default class Promos extends React.PureComponent<{}, State> { this.setState({ activePromo }); }; } + +function mapStateToProps(state: AppState): StateProps { + return { + wallet: state.wallet.inst + }; +} + +export default connect(mapStateToProps, {})(PromosClass); diff --git a/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx b/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx index 29c4d742..294f12ea 100644 --- a/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx +++ b/common/components/BalanceSidebar/TokenBalances/AddCustomTokenForm.tsx @@ -66,11 +66,11 @@ export default class AddCustomTokenForm extends React.PureComponent { return (