Compare commits
69 Commits
Author | SHA1 | Date |
---|---|---|
James Prado | 816ce3180f | |
William O'Beirne | 7521337bda | |
greenkeeper[bot] | f42b81ac8a | |
greenkeeper[bot] | aa08c1902d | |
Michael - Blurpesec | 8eeefe7e25 | |
William O'Beirne | 5d41a7f774 | |
William O'Beirne | 0b44919912 | |
Danny Skubak | 937cc3fde5 | |
Danny Skubak | 9be46bf8aa | |
Daniel Ternyak | f5716514b1 | |
Daniel Ternyak | 8c0dd3f455 | |
Daniel Ternyak | 825e467db7 | |
greenkeeper[bot] | 9e723e609c | |
greenkeeper[bot] | be9729c302 | |
greenkeeper[bot] | 9a5e2a8c2a | |
greenkeeper[bot] | 0976552a0b | |
greenkeeper[bot] | bd4a63e591 | |
greenkeeper[bot] | aeb1b74d8d | |
greenkeeper[bot] | 433a3276bb | |
greenkeeper[bot] | 4b6cba3211 | |
William O'Beirne | db6b737cad | |
greenkeeper[bot] | 740b191542 | |
greenkeeper[bot] | 0bc24f5825 | |
greenkeeper[bot] | 92e757ceb2 | |
William O'Beirne | 4b8adc81ce | |
anticlimactic | 16469e1a62 | |
Daniel Ternyak | 3d8f678887 | |
Daniel Ternyak | 8649655019 | |
greenkeeper[bot] | 9284fb56eb | |
greenkeeper[bot] | 7905b0cf67 | |
greenkeeper[bot] | 3f6681711f | |
greenkeeper[bot] | 7b7728cf9d | |
greenkeeper[bot] | 033407460b | |
James Prado | 4788381641 | |
William O'Beirne | 56943232a0 | |
greenkeeper[bot] | aa287f478a | |
James Prado | 9cac0298a2 | |
greenkeeper[bot] | 6e8d807b22 | |
greenkeeper[bot] | 80383a831e | |
greenkeeper[bot] | 5e97a9fe66 | |
greenkeeper[bot] | 3af6607901 | |
aitrean | cf9887f21f | |
HenryNguyen5 | 94b3f3403b | |
HenryNguyen5 | 16e6677c0f | |
William O'Beirne | 3bce82ba86 | |
HenryNguyen5 | c340246ca0 | |
Daniel Ternyak | 0e26f7af4c | |
Daniel Ternyak | 3278318fa6 | |
greenkeeper[bot] | b901c3e125 | |
Daniel Ternyak | 8acef8e54f | |
HenryNguyen5 | 2d057eb0b2 | |
William O'Beirne | a5be81e96d | |
Daniel Ternyak | 0d4bc5bbb1 | |
greenkeeper[bot] | 446d7043c3 | |
greenkeeper[bot] | 3bdf41cf59 | |
Taylor Monahan | 7941403782 | |
greenkeeper[bot] | 533255094b | |
William O'Beirne | d0379b19a0 | |
Daniel Ternyak | e45324e7f1 | |
William O'Beirne | 3a36818412 | |
William O'Beirne | e3194a649e | |
greenkeeper[bot] | b016d35819 | |
Daniel Ternyak | dd83071ec7 | |
greenkeeper[bot] | 383df967a7 | |
greenkeeper[bot] | 611e511b33 | |
Luit Hollander | a9aeacb775 | |
William O'Beirne | 6927aa0b55 | |
HenryNguyen5 | 4dd0fc9785 | |
greenkeeper[bot] | 86992e8198 |
|
@ -0,0 +1 @@
|
|||
common/translations
|
|
@ -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,
|
||||
|
|
|
@ -18,3 +18,18 @@ export function setTransactionData(
|
|||
payload
|
||||
};
|
||||
}
|
||||
|
||||
export type TResetTransactionData = typeof resetTransactionData;
|
||||
export function resetTransactionData(): interfaces.ResetTransactionDataAction {
|
||||
return { type: TypeKeys.TRANSACTIONS_RESET_TRANSACTION_DATA };
|
||||
}
|
||||
|
||||
export type TAddRecentTransaction = typeof addRecentTransaction;
|
||||
export function addRecentTransaction(
|
||||
payload: interfaces.AddRecentTransactionAction['payload']
|
||||
): interfaces.AddRecentTransactionAction {
|
||||
return {
|
||||
type: TypeKeys.TRANSACTIONS_ADD_RECENT_TRANSACTION,
|
||||
payload
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { TypeKeys } from './constants';
|
||||
import { TransactionData, TransactionReceipt } from 'libs/nodes';
|
||||
import { SavedTransaction, TransactionData, TransactionReceipt } from 'types/transactions';
|
||||
|
||||
export interface FetchTransactionDataAction {
|
||||
type: TypeKeys.TRANSACTIONS_FETCH_TRANSACTION_DATA;
|
||||
|
@ -16,5 +16,18 @@ export interface SetTransactionDataAction {
|
|||
};
|
||||
}
|
||||
|
||||
export interface ResetTransactionDataAction {
|
||||
type: TypeKeys.TRANSACTIONS_RESET_TRANSACTION_DATA;
|
||||
}
|
||||
|
||||
export interface AddRecentTransactionAction {
|
||||
type: TypeKeys.TRANSACTIONS_ADD_RECENT_TRANSACTION;
|
||||
payload: SavedTransaction;
|
||||
}
|
||||
|
||||
/*** Union Type ***/
|
||||
export type TransactionsAction = FetchTransactionDataAction | SetTransactionDataAction;
|
||||
export type TransactionsAction =
|
||||
| FetchTransactionDataAction
|
||||
| SetTransactionDataAction
|
||||
| ResetTransactionDataAction
|
||||
| AddRecentTransactionAction;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
export enum TypeKeys {
|
||||
TRANSACTIONS_FETCH_TRANSACTION_DATA = 'TRANSACTIONS_FETCH_TRANSACTION_DATA',
|
||||
TRANSACTIONS_SET_TRANSACTION_DATA = 'TRANSACTIONS_SET_TRANSACTION_DATA',
|
||||
TRANSACTIONS_SET_TRANSACTION_ERROR = 'TRANSACTIONS_SET_TRANSACTION_ERROR'
|
||||
TRANSACTIONS_SET_TRANSACTION_ERROR = 'TRANSACTIONS_SET_TRANSACTION_ERROR',
|
||||
TRANSACTIONS_RESET_TRANSACTION_DATA = 'TRANSACTIONS_RESET_TRANSACTION_DATA',
|
||||
TRANSACTIONS_ADD_RECENT_TRANSACTION = 'TRANSACTIONS_ADD_RECENT_TRANSACTION'
|
||||
}
|
||||
|
|
|
@ -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) };
|
||||
|
|
|
@ -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<GasEstimates> {
|
||||
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<GasExpressResponse, 'block_time' | 'blockNum'>)[] = [
|
||||
'safeLow',
|
||||
'standard',
|
||||
'fast',
|
||||
'fastest'
|
||||
];
|
||||
keys.forEach(key => {
|
||||
if (typeof res[key] !== 'number') {
|
||||
throw new Error(
|
||||
|
|
|
@ -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);
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 38 KiB |
|
@ -0,0 +1,97 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="81px" height="120px" viewBox="0 0 81 120" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 48.2 (47327) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Artboard</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="100.099854%" y1="-16.122731%" x2="44.1579795%" y2="77.6511455%" id="linearGradient-1">
|
||||
<stop stop-color="#2B415B" offset="13.45%"></stop>
|
||||
<stop stop-color="#3B5676" offset="37.62%"></stop>
|
||||
<stop stop-color="#54769E" offset="69.23%"></stop>
|
||||
<stop stop-color="#52749B" offset="79.01%"></stop>
|
||||
<stop stop-color="#4D6C92" offset="86.14%"></stop>
|
||||
<stop stop-color="#436082" offset="92.44%"></stop>
|
||||
<stop stop-color="#364F6C" offset="98.22%"></stop>
|
||||
<stop stop-color="#314863" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="99.7887646%" y1="61.4941149%" x2="-4.60729499%" y2="13.3460499%" id="linearGradient-2">
|
||||
<stop stop-color="#54769E" offset="0%"></stop>
|
||||
<stop stop-color="#53749C" offset="48.02%"></stop>
|
||||
<stop stop-color="#4F6F95" offset="68.78%"></stop>
|
||||
<stop stop-color="#486588" offset="84.23%"></stop>
|
||||
<stop stop-color="#435F80" offset="90.95%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="725.856983%" y1="50.0229457%" x2="-347.124022%" y2="50.0229457%" id="linearGradient-3">
|
||||
<stop stop-color="#20344C" offset="25.39%"></stop>
|
||||
<stop stop-color="#273D57" offset="40.72%"></stop>
|
||||
<stop stop-color="#395373" offset="67.33%"></stop>
|
||||
<stop stop-color="#54769E" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-653.788268%" y1="50.0214562%" x2="455.494413%" y2="50.0214562%" id="linearGradient-4">
|
||||
<stop stop-color="#54769E" offset="25.39%"></stop>
|
||||
<stop stop-color="#4D6E93" offset="41.33%"></stop>
|
||||
<stop stop-color="#3C5777" offset="68.97%"></stop>
|
||||
<stop stop-color="#233850" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50.0370086%" y1="-209.335792%" x2="49.9575834%" y2="235.965027%" id="linearGradient-5">
|
||||
<stop stop-color="#54769E" offset="0.6545247%"></stop>
|
||||
<stop stop-color="#507198" offset="19.93%"></stop>
|
||||
<stop stop-color="#466488" offset="45.02%"></stop>
|
||||
<stop stop-color="#354F6D" offset="73.18%"></stop>
|
||||
<stop stop-color="#21354D" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="163.859644%" y1="-95.5767241%" x2="-53.1396713%" y2="173.049483%" id="linearGradient-6">
|
||||
<stop stop-color="#54769E" offset="25.39%"></stop>
|
||||
<stop stop-color="#4D6E93" offset="41.02%"></stop>
|
||||
<stop stop-color="#3C5777" offset="68.13%"></stop>
|
||||
<stop stop-color="#22364E" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-44.1116757%" y1="-60.1463542%" x2="137.687762%" y2="146.957292%" id="linearGradient-7">
|
||||
<stop stop-color="#54769E" offset="25.39%"></stop>
|
||||
<stop stop-color="#4D6E93" offset="41.02%"></stop>
|
||||
<stop stop-color="#3C5777" offset="68.13%"></stop>
|
||||
<stop stop-color="#22364E" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-1.43584837%" y1="31.6007047%" x2="127.701745%" y2="103.798097%" id="linearGradient-8">
|
||||
<stop stop-color="#54769E" offset="26.64%"></stop>
|
||||
<stop stop-color="#425E7F" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="-71.276129%" y1="-65.3137535%" x2="149.174581%" y2="96.3311507%" id="linearGradient-9">
|
||||
<stop stop-color="#54769E" stop-opacity="0" offset="46.09%"></stop>
|
||||
<stop stop-color="#52739A" stop-opacity="0.2156" offset="56.99%"></stop>
|
||||
<stop stop-color="#4A698E" stop-opacity="0.4266" offset="67.64%"></stop>
|
||||
<stop stop-color="#3D597B" stop-opacity="0.6356" offset="78.2%"></stop>
|
||||
<stop stop-color="#2C435F" stop-opacity="0.8422" offset="88.63%"></stop>
|
||||
<stop stop-color="#1B2E45" offset="96.61%"></stop>
|
||||
</linearGradient>
|
||||
<linearGradient x1="50.309375%" y1="295.997443%" x2="50.309375%" y2="-124.649242%" id="linearGradient-10">
|
||||
<stop stop-color="#54769E" offset="25.39%"></stop>
|
||||
<stop stop-color="#4D6E93" offset="41.02%"></stop>
|
||||
<stop stop-color="#3C5777" offset="68.13%"></stop>
|
||||
<stop stop-color="#22364E" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Artboard" fill-rule="nonzero">
|
||||
<g id="logo-shapeshift">
|
||||
<polygon id="Shape" fill="#273C51" points="75.9045553 29.0113736 81 0.0349956255 59.7045553 8.88888889 36.3006508 8.88888889 15.0052061 0 20.1357918 29.0113736 15.3214751 45.1793526 19.8546638 48.0139983 0 65.3718285 0 81.67979 22.7010846 112.825897 44.1019523 119.965004 44.1370933 120 60.8642082 111.461067 60.8993492 111.426072 60.8993492 98.7926509 51.4112798 93.6132983 51.4112798 93.6132983 51.4112798 93.6132983 66.3110629 54.1032371 66.2056399 54.1032371 80.683731 45.1793526"></polygon>
|
||||
<g id="Group">
|
||||
<polygon id="Shape" fill="url(#linearGradient-1)" points="34.2584416 35.3372093 0 65.2325581 47.7233766 92.5465116 47.8987013 36.244186"></polygon>
|
||||
<polygon id="Shape" fill="#466284" points="29.3844156 53.8255814 47.7584416 102.697674 47.8987013 56.5465116"></polygon>
|
||||
<polygon id="Shape" fill="#354D6A" points="66.1675325 54 47.7584416 102.697674 47.8987013 56.5465116"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-2)" points="80.8246753 0.104651163 62.5558442 9.6627907 47.9337662 12.8372093 33.1714286 9.6627907 14.9727273 0.0697674419 21.2493506 28.9186047 15.2883117 45.1046512 36.187013 58.0116279 47.7935065 70.5697674 47.7935065 70.6744186 47.8636364 70.6046512 47.9337662 70.6744186 47.9337662 70.5697674 59.5402597 58.0116279 80.5090909 45.1046512 74.5480519 28.9534884"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-3)" points="80.8246753 0.104651163 75.7402597 28.9883721 80.5090909 45.1046512 74.5480519 28.9534884"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-4)" points="14.9727273 0.0697674419 20.0922078 28.9883721 15.2883117 45.1046512 21.2493506 28.9186047"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-5)" points="14.9727273 0.0697674419 36.2220779 8.93023256 59.5753247 8.93023256 80.8246753 0.104651163 62.5558442 9.6627907 47.9337662 12.8372093 33.1714286 9.6627907"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-6)" points="21.2493506 28.9186047 20.1974026 31.8139535 42.2532468 11.5813953"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-7)" points="74.5480519 28.9534884 53.6493506 11.5813953 75.5649351 31.6744186"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-8)" points="0 65.2325581 51.3 93.3837209 57.787013 109.534884 44.0415584 114.732558 22.6519481 112.534884 0 81.4883721"></polygon>
|
||||
<polygon id="Shape" fill="#FFFFFF" points="42.4636364 88.5348837 22.7220779 112.465116 22.6519481 112.534884 44.0064935 119.651163 44.0415584 119.686047 60.7324675 111.174419 60.7675325 111.139535 60.7675325 98.5465116"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-9)" points="74.5480519 28.9534884 80.8246753 0.104651163 62.5558442 9.6627907 53.6493506 11.5813953"></polygon>
|
||||
<polygon id="Shape" fill="#FFFFFF" points="47.8987013 70.6744186 42.112987 64.3604651 47.8987013 58.0116279 53.7194805 64.3604651"></polygon>
|
||||
<polygon id="Shape" fill="url(#linearGradient-10)" points="47.3376623 12.6976744 47.9337662 12.8372093 48.4597403 12.7325581 47.8987013 49.5348837"></polygon>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 8.1 KiB |
|
@ -1,36 +0,0 @@
|
|||
// 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";
|
||||
// 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";
|
||||
// Skins
|
||||
@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";
|
|
@ -1,14 +0,0 @@
|
|||
// Alerts
|
||||
|
||||
.alert-variant(@background; @border; @text-color) {
|
||||
background-color: @background;
|
||||
border-color: @border;
|
||||
color: @text-color;
|
||||
|
||||
hr {
|
||||
border-top-color: darken(@border, 5%);
|
||||
}
|
||||
.alert-link {
|
||||
color: darken(@text-color, 10%);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// Contextual backgrounds
|
||||
|
||||
.bg-variant(@color) {
|
||||
background-color: @color;
|
||||
a&:hover,
|
||||
a&:focus {
|
||||
background-color: darken(@color, 10%);
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// Single side border-radius
|
||||
|
||||
.border-top-radius(@radius) {
|
||||
border-top-right-radius: @radius;
|
||||
border-top-left-radius: @radius;
|
||||
}
|
||||
|
||||
.border-right-radius(@radius) {
|
||||
border-bottom-right-radius: @radius;
|
||||
border-top-right-radius: @radius;
|
||||
}
|
||||
|
||||
.border-bottom-radius(@radius) {
|
||||
border-bottom-right-radius: @radius;
|
||||
border-bottom-left-radius: @radius;
|
||||
}
|
||||
|
||||
.border-left-radius(@radius) {
|
||||
border-bottom-left-radius: @radius;
|
||||
border-top-left-radius: @radius;
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
// Button variants
|
||||
//
|
||||
// Easily pump out default styles, as well as :hover, :focus, :active,
|
||||
// and disabled options for all buttons
|
||||
|
||||
.button-variant(@color; @background; @border) {
|
||||
color: @color;
|
||||
background-color: @background;
|
||||
border-color: @border;
|
||||
transition: all ease 250ms;
|
||||
|
||||
&:focus,
|
||||
&.focus {
|
||||
color: @color;
|
||||
background-color: darken(@background, 5%);
|
||||
border-color: darken(@border, 5%);
|
||||
transition: all ease 250ms;
|
||||
}
|
||||
&:hover {
|
||||
color: @color;
|
||||
background-color: darken(@background, 5%);
|
||||
border-color: darken(@border, 5%);
|
||||
transition: all ease 250ms;
|
||||
}
|
||||
&:active,
|
||||
&.active,
|
||||
.open > .dropdown-toggle& {
|
||||
color: @color;
|
||||
background-color: darken(@background, 5%);
|
||||
border-color: darken(@border, 5%);
|
||||
transition: all ease 250ms;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.focus {
|
||||
color: @color;
|
||||
background-color: darken(@background, 15%);
|
||||
border-color: darken(@border, 15%);
|
||||
transition: all ease 250ms;
|
||||
}
|
||||
}
|
||||
&:active,
|
||||
&.active,
|
||||
.open > .dropdown-toggle& {
|
||||
background-image: none;
|
||||
}
|
||||
&.disabled,
|
||||
&[disabled],
|
||||
fieldset[disabled] & {
|
||||
&,
|
||||
&:hover,
|
||||
&:focus,
|
||||
&.focus,
|
||||
&:active,
|
||||
&.active {
|
||||
background-color: @background;
|
||||
border-color: @border;
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
color: @background;
|
||||
background-color: @color;
|
||||
}
|
||||
}
|
||||
|
||||
// Button sizes
|
||||
.button-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
|
||||
padding: @padding-vertical @padding-horizontal;
|
||||
font-size: @font-size;
|
||||
line-height: @line-height;
|
||||
border-radius: @border-radius;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
// Center-align a block level element
|
||||
|
||||
.center-block() {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
// Clearfix
|
||||
//
|
||||
// For modern browsers
|
||||
// 1. The space content is one way to avoid an Opera bug when the
|
||||
// contenteditable attribute is included anywhere else in the document.
|
||||
// Otherwise it causes space to appear at the top and bottom of elements
|
||||
// that are clearfixed.
|
||||
// 2. The use of `table` rather than `block` is only necessary if using
|
||||
// `:before` to contain the top-margins of child elements.
|
||||
//
|
||||
// Source: http://nicolasgallagher.com/micro-clearfix-hack/
|
||||
|
||||
.clearfix() {
|
||||
&:before,
|
||||
&:after {
|
||||
content: " "; // 1
|
||||
display: table; // 2
|
||||
}
|
||||
&:after {
|
||||
clear: both;
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
// Form validation states
|
||||
//
|
||||
// Used in forms.less to generate the form validation CSS for warnings, errors,
|
||||
// and successes.
|
||||
|
||||
.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
|
||||
// Color the label and help text
|
||||
.help-block,
|
||||
.control-label,
|
||||
.radio,
|
||||
.checkbox,
|
||||
.radio-inline,
|
||||
.checkbox-inline,
|
||||
&.radio label,
|
||||
&.checkbox label,
|
||||
&.radio-inline label,
|
||||
&.checkbox-inline label {
|
||||
color: @text-color;
|
||||
}
|
||||
// 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
|
||||
&: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);
|
||||
}
|
||||
}
|
||||
// Set validation states also for addons
|
||||
.input-group-addon {
|
||||
color: @text-color;
|
||||
border-color: @border-color;
|
||||
background-color: @background-color;
|
||||
}
|
||||
// Optional feedback icon
|
||||
.form-control-feedback {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
// Form control focus state
|
||||
//
|
||||
// Generate a customized focus state and for any input with the specified color,
|
||||
// which defaults to the `@input-border-focus` variable.
|
||||
//
|
||||
// We highly encourage you to not customize the default value, but instead use
|
||||
// this to tweak colors on an as-needed basis. This aesthetic change is based on
|
||||
// WebKit's default styles, but applicable to a wider range of browsers. Its
|
||||
// usability and accessibility should be taken into account with any change.
|
||||
//
|
||||
// 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);
|
||||
&:focus {
|
||||
border-color: @color;
|
||||
outline: 0;
|
||||
.box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}");
|
||||
}
|
||||
}
|
||||
|
||||
// Form control sizing
|
||||
//
|
||||
// Relative text size, padding, and border-radii changes for form controls. For
|
||||
// horizontal sizing, wrap controls in the predefined grid classes. `<select>`
|
||||
// element gets special love because it's special, and that's a fact!
|
||||
.input-size(@input-height; @padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
|
||||
height: @input-height;
|
||||
padding: @padding-vertical @padding-horizontal;
|
||||
font-size: @font-size;
|
||||
line-height: @line-height;
|
||||
border-radius: @border-radius;
|
||||
|
||||
select& {
|
||||
height: @input-height;
|
||||
line-height: @input-height;
|
||||
}
|
||||
|
||||
textarea&,
|
||||
select[multiple] & {
|
||||
height: auto;
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
// Gradients
|
||||
|
||||
#gradient {
|
||||
|
||||
// Horizontal gradient, from left to right
|
||||
//
|
||||
// Creates two color stops, start and end, by specifying a color and position for each color stop.
|
||||
// Color stops are not available in IE9 and below.
|
||||
.horizontal(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {
|
||||
background-image: -webkit-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+
|
||||
background-image: -o-linear-gradient(left, @start-color @start-percent, @end-color @end-percent); // Opera 12
|
||||
background-image: linear-gradient(to right, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
|
||||
background-repeat: repeat-x;
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)", argb(@start-color), argb(@end-color))); // IE9 and down
|
||||
}
|
||||
|
||||
// Vertical gradient, from top to bottom
|
||||
//
|
||||
// Creates two color stops, start and end, by specifying a color and position for each color stop.
|
||||
// Color stops are not available in IE9 and below.
|
||||
.vertical(@start-color: #555; @end-color: #333; @start-percent: 0%; @end-percent: 100%) {
|
||||
background-image: -webkit-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Safari 5.1-6, Chrome 10+
|
||||
background-image: -o-linear-gradient(top, @start-color @start-percent, @end-color @end-percent); // Opera 12
|
||||
background-image: linear-gradient(to bottom, @start-color @start-percent, @end-color @end-percent); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
|
||||
background-repeat: repeat-x;
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)", argb(@start-color), argb(@end-color))); // IE9 and down
|
||||
}
|
||||
|
||||
.directional(@start-color: #555; @end-color: #333; @deg: 45deg) {
|
||||
background-repeat: repeat-x;
|
||||
background-image: -webkit-linear-gradient(@deg, @start-color, @end-color); // Safari 5.1-6, Chrome 10+
|
||||
background-image: -o-linear-gradient(@deg, @start-color, @end-color); // Opera 12
|
||||
background-image: linear-gradient(@deg, @start-color, @end-color); // Standard, IE10, Firefox 16+, Opera 12.10+, Safari 7+, Chrome 26+
|
||||
}
|
||||
.horizontal-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {
|
||||
background-image: -webkit-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);
|
||||
background-image: -o-linear-gradient(left, @start-color, @mid-color @color-stop, @end-color);
|
||||
background-image: linear-gradient(to right, @start-color, @mid-color @color-stop, @end-color);
|
||||
background-repeat: no-repeat;
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=1)", argb(@start-color), argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback
|
||||
}
|
||||
.vertical-three-colors(@start-color: #00b3ee; @mid-color: #7a43b6; @color-stop: 50%; @end-color: #c3325f) {
|
||||
background-image: -webkit-linear-gradient(@start-color, @mid-color @color-stop, @end-color);
|
||||
background-image: -o-linear-gradient(@start-color, @mid-color @color-stop, @end-color);
|
||||
background-image: linear-gradient(@start-color, @mid-color @color-stop, @end-color);
|
||||
background-repeat: no-repeat;
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.gradient(startColorstr='%d', endColorstr='%d', GradientType=0)", argb(@start-color), argb(@end-color))); // IE9 and down, gets no color-stop at all for proper fallback
|
||||
}
|
||||
.radial(@inner-color: #555; @outer-color: #333) {
|
||||
background-image: -webkit-radial-gradient(circle, @inner-color, @outer-color);
|
||||
background-image: radial-gradient(circle, @inner-color, @outer-color);
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.striped(@color: rgba(255,255,255,.15); @angle: 45deg) {
|
||||
background-image: -webkit-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
|
||||
background-image: -o-linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
|
||||
background-image: linear-gradient(@angle, @color 25%, transparent 25%, transparent 50%, @color 50%, @color 75%, transparent 75%, transparent);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
// Framework grid generation
|
||||
//
|
||||
// Used only by Bootstrap to generate the correct number of grid classes given
|
||||
// any value of `@grid-columns`.
|
||||
|
||||
.make-grid-columns() {
|
||||
// Common styles for all sizes of grid columns, widths 1-12
|
||||
.col(@index) { // initial
|
||||
@item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
|
||||
.col((@index + 1), @item);
|
||||
}
|
||||
.col(@index, @list) when (@index =< @grid-columns) { // general; "=<" isn't a typo
|
||||
@item: ~".col-xs-@{index}, .col-sm-@{index}, .col-md-@{index}, .col-lg-@{index}";
|
||||
.col((@index + 1), ~"@{list}, @{item}");
|
||||
}
|
||||
.col(@index, @list) when (@index > @grid-columns) { // terminal
|
||||
@{list} {
|
||||
position: relative;
|
||||
// Prevent columns from collapsing when empty
|
||||
min-height: 1px;
|
||||
// Inner gutter via padding
|
||||
padding-left: (@grid-gutter-width / 2);
|
||||
padding-right: (@grid-gutter-width / 2);
|
||||
}
|
||||
}
|
||||
.col(1); // kickstart it
|
||||
}
|
||||
|
||||
.float-grid-columns(@class) {
|
||||
.col(@index) { // initial
|
||||
@item: ~".col-@{class}-@{index}";
|
||||
.col((@index + 1), @item);
|
||||
}
|
||||
.col(@index, @list) when (@index =< @grid-columns) { // general
|
||||
@item: ~".col-@{class}-@{index}";
|
||||
.col((@index + 1), ~"@{list}, @{item}");
|
||||
}
|
||||
.col(@index, @list) when (@index > @grid-columns) { // terminal
|
||||
@{list} {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
.col(1); // kickstart it
|
||||
}
|
||||
|
||||
.calc-grid-column(@index, @class, @type) when (@type = width) and (@index > 0) {
|
||||
.col-@{class}-@{index} {
|
||||
width: percentage((@index / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.calc-grid-column(@index, @class, @type) when (@type = push) and (@index > 0) {
|
||||
.col-@{class}-push-@{index} {
|
||||
left: percentage((@index / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.calc-grid-column(@index, @class, @type) when (@type = push) and (@index = 0) {
|
||||
.col-@{class}-push-0 {
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index > 0) {
|
||||
.col-@{class}-pull-@{index} {
|
||||
right: percentage((@index / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.calc-grid-column(@index, @class, @type) when (@type = pull) and (@index = 0) {
|
||||
.col-@{class}-pull-0 {
|
||||
right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.calc-grid-column(@index, @class, @type) when (@type = offset) {
|
||||
.col-@{class}-offset-@{index} {
|
||||
margin-left: percentage((@index / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
// Basic looping in LESS
|
||||
.loop-grid-columns(@index, @class, @type) when (@index >= 0) {
|
||||
.calc-grid-column(@index, @class, @type);
|
||||
// next iteration
|
||||
.loop-grid-columns((@index - 1), @class, @type);
|
||||
}
|
||||
|
||||
// Create grid for specific class
|
||||
.make-grid(@class) {
|
||||
.float-grid-columns(@class);
|
||||
.loop-grid-columns(@grid-columns, @class, width);
|
||||
.loop-grid-columns(@grid-columns, @class, pull);
|
||||
.loop-grid-columns(@grid-columns, @class, push);
|
||||
.loop-grid-columns(@grid-columns, @class, offset);
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
// Grid system
|
||||
//
|
||||
// Generate semantic grid columns with these mixins.
|
||||
|
||||
// Centered container element
|
||||
.container-fixed(@gutter: @grid-gutter-width) {
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
padding-left: (@gutter / 2);
|
||||
padding-right: (@gutter / 2);
|
||||
&:extend(.clearfix all);
|
||||
}
|
||||
|
||||
// Creates a wrapper for a series of columns
|
||||
.make-row(@gutter: @grid-gutter-width) {
|
||||
margin-left: (@gutter / -2);
|
||||
margin-right: (@gutter / -2);
|
||||
&:extend(.clearfix all);
|
||||
}
|
||||
|
||||
// Generate the extra small columns
|
||||
.make-xs-column(@columns; @gutter: @grid-gutter-width) {
|
||||
position: relative;
|
||||
float: left;
|
||||
width: percentage((@columns / @grid-columns));
|
||||
min-height: 1px;
|
||||
padding-left: (@gutter / 2);
|
||||
padding-right: (@gutter / 2);
|
||||
}
|
||||
|
||||
.make-xs-column-offset(@columns) {
|
||||
margin-left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
|
||||
.make-xs-column-push(@columns) {
|
||||
left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
|
||||
.make-xs-column-pull(@columns) {
|
||||
right: percentage((@columns / @grid-columns));
|
||||
}
|
||||
|
||||
// Generate the small columns
|
||||
.make-sm-column(@columns; @gutter: @grid-gutter-width) {
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-left: (@gutter / 2);
|
||||
padding-right: (@gutter / 2);
|
||||
|
||||
@media (min-width: @screen-sm-min) {
|
||||
float: left;
|
||||
width: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-sm-column-offset(@columns) {
|
||||
@media (min-width: @screen-sm-min) {
|
||||
margin-left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-sm-column-push(@columns) {
|
||||
@media (min-width: @screen-sm-min) {
|
||||
left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-sm-column-pull(@columns) {
|
||||
@media (min-width: @screen-sm-min) {
|
||||
right: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the medium columns
|
||||
.make-md-column(@columns; @gutter: @grid-gutter-width) {
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-left: (@gutter / 2);
|
||||
padding-right: (@gutter / 2);
|
||||
|
||||
@media (min-width: @screen-md-min) {
|
||||
float: left;
|
||||
width: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-md-column-offset(@columns) {
|
||||
@media (min-width: @screen-md-min) {
|
||||
margin-left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-md-column-push(@columns) {
|
||||
@media (min-width: @screen-md-min) {
|
||||
left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-md-column-pull(@columns) {
|
||||
@media (min-width: @screen-md-min) {
|
||||
right: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the large columns
|
||||
.make-lg-column(@columns; @gutter: @grid-gutter-width) {
|
||||
position: relative;
|
||||
min-height: 1px;
|
||||
padding-left: (@gutter / 2);
|
||||
padding-right: (@gutter / 2);
|
||||
|
||||
@media (min-width: @screen-lg-min) {
|
||||
float: left;
|
||||
width: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-lg-column-offset(@columns) {
|
||||
@media (min-width: @screen-lg-min) {
|
||||
margin-left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-lg-column-push(@columns) {
|
||||
@media (min-width: @screen-lg-min) {
|
||||
left: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
||||
|
||||
.make-lg-column-pull(@columns) {
|
||||
@media (min-width: @screen-lg-min) {
|
||||
right: percentage((@columns / @grid-columns));
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// CSS image replacement
|
||||
//
|
||||
// Heads up! v3 launched with only `.hide-text()`, but per our pattern for
|
||||
// mixins being reused as classes with the same name, this doesn't hold up. As
|
||||
// of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`.
|
||||
//
|
||||
// Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757
|
||||
|
||||
// Deprecated as of v3.0.1 (will be removed in v4)
|
||||
.hide-text() {
|
||||
font: ~"0/0" a;
|
||||
color: transparent;
|
||||
text-shadow: none;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// New mixin to use as of v3.0.1
|
||||
.text-hide() {
|
||||
.hide-text();
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
// Image Mixins
|
||||
// - Responsive image
|
||||
// - Retina image
|
||||
|
||||
// Responsive image
|
||||
//
|
||||
// Keep images from scaling beyond the width of their parents.
|
||||
.img-responsive(@display: block) {
|
||||
display: @display;
|
||||
max-width: 100%; // Part 1: Set a maximum relative to the parent
|
||||
height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching
|
||||
}
|
||||
|
||||
// Retina image
|
||||
//
|
||||
// Short retina mixin for setting background-image and -size. Note that the
|
||||
// spelling of `min--moz-device-pixel-ratio` is intentional.
|
||||
.img-retina(@file-1x; @file-2x; @width-1x; @height-1x) {
|
||||
background-image: url("@{file-1x}");
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and ( min--moz-device-pixel-ratio: 2), only screen and ( -o-min-device-pixel-ratio: 2/1), only screen and ( min-device-pixel-ratio: 2), only screen and ( min-resolution: 192dpi), only screen and ( min-resolution: 2dppx) {
|
||||
background-image: url("@{file-2x}");
|
||||
background-size: @width-1x @height-1x;
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
// Labels
|
||||
|
||||
.label-variant(@color) {
|
||||
background-color: @color;
|
||||
|
||||
&[href] {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: darken(@color, 10%);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
// List Groups
|
||||
|
||||
.list-group-item-variant(@state; @background; @color) {
|
||||
.list-group-item-@{state} {
|
||||
color: @color;
|
||||
background-color: @background;
|
||||
|
||||
a&,
|
||||
button& {
|
||||
color: @color;
|
||||
|
||||
.list-group-item-heading {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: @color;
|
||||
background-color: darken(@background, 5%);
|
||||
}
|
||||
&.active,
|
||||
&.active:hover,
|
||||
&.active:focus {
|
||||
color: #fff;
|
||||
background-color: @color;
|
||||
border-color: @color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Horizontal dividers
|
||||
//
|
||||
// Dividers (basically an hr) within dropdowns and nav lists
|
||||
|
||||
.nav-divider(@color: #e5e5e5) {
|
||||
height: 1px;
|
||||
margin: 2px 0;
|
||||
overflow: hidden;
|
||||
background-color: @color;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// Navbar vertical align
|
||||
//
|
||||
// Vertically center elements in the navbar.
|
||||
// Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin.
|
||||
|
||||
.navbar-vertical-align(@element-height) {
|
||||
margin-top: ((@navbar-height - @element-height) / 2);
|
||||
margin-bottom: ((@navbar-height - @element-height) / 2);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
// Opacity
|
||||
|
||||
.opacity(@opacity) {
|
||||
opacity: @opacity;
|
||||
// IE8 filter
|
||||
@opacity-ie: (@opacity * 100);
|
||||
filter: ~"alpha(opacity=@{opacity-ie})";
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Pagination
|
||||
|
||||
.pagination-size(@padding-vertical; @padding-horizontal; @font-size; @line-height; @border-radius) {
|
||||
> li {
|
||||
> a,
|
||||
> span {
|
||||
padding: @padding-vertical @padding-horizontal;
|
||||
font-size: @font-size;
|
||||
line-height: @line-height;
|
||||
}
|
||||
&:first-child {
|
||||
> a,
|
||||
> span {
|
||||
.border-left-radius(@border-radius);
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
> a,
|
||||
> span {
|
||||
.border-right-radius(@border-radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
// Panels
|
||||
|
||||
.panel-variant(@border; @heading-text-color; @heading-bg-color; @heading-border) {
|
||||
border-color: @border;
|
||||
|
||||
& > .panel-heading {
|
||||
color: @heading-text-color;
|
||||
background-color: @heading-bg-color;
|
||||
border-color: @heading-border;
|
||||
|
||||
+ .panel-collapse > .panel-body {
|
||||
border-top-color: @border;
|
||||
}
|
||||
.badge {
|
||||
color: @heading-bg-color;
|
||||
background-color: @heading-text-color;
|
||||
}
|
||||
}
|
||||
& > .panel-footer {
|
||||
+ .panel-collapse > .panel-body {
|
||||
border-bottom-color: @border;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Progress bars
|
||||
|
||||
.progress-bar-variant(@color) {
|
||||
background-color: @color;
|
||||
|
||||
// Deprecated parent class requirement as of v3.2.0
|
||||
.progress-striped & {
|
||||
#gradient > .striped();
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
// Reset filters for IE
|
||||
//
|
||||
// When you need to remove a gradient background, do not forget to use this to reset
|
||||
// the IE filter for IE9 and below.
|
||||
|
||||
.reset-filter() {
|
||||
filter: e(%("progid:DXImageTransform.Microsoft.gradient(enabled = false)"));
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
.reset-text() {
|
||||
font-family: @font-family-base;
|
||||
// We deliberately do NOT reset font-size.
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
letter-spacing: normal;
|
||||
line-break: auto;
|
||||
line-height: @line-height-base;
|
||||
text-align: left; // Fallback for where `start` is not supported
|
||||
text-align: start;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
text-transform: none;
|
||||
white-space: normal;
|
||||
word-break: normal;
|
||||
word-spacing: normal;
|
||||
word-wrap: normal;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// Resize anything
|
||||
|
||||
.resizable(@direction) {
|
||||
resize: @direction; // Options: horizontal, vertical, both
|
||||
overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible`
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
// Responsive utilities
|
||||
|
||||
//
|
||||
// More easily include all the states for responsive-utilities.less.
|
||||
.responsive-visibility() {
|
||||
display: block !important;
|
||||
table& {
|
||||
display: table !important;
|
||||
}
|
||||
tr& {
|
||||
display: table-row !important;
|
||||
}
|
||||
th&,
|
||||
td& {
|
||||
display: table-cell !important;
|
||||
}
|
||||
}
|
||||
|
||||
.responsive-invisibility() {
|
||||
display: none !important;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Sizing shortcuts
|
||||
|
||||
.size(@width; @height) {
|
||||
width: @width;
|
||||
height: @height;
|
||||
}
|
||||
|
||||
.square(@size) {
|
||||
.size(@size; @size);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
// WebKit-style focus
|
||||
|
||||
.tab-focus() {
|
||||
outline: thin dotted;
|
||||
outline-offset: 3px;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Tables
|
||||
|
||||
.table-row-variant(@state; @background) {
|
||||
// Exact selectors below required to override `.table-striped` and prevent
|
||||
// inheritance to nested tables.
|
||||
.table > thead > tr,
|
||||
.table > tbody > tr,
|
||||
.table > tfoot > tr {
|
||||
> td.@{state},
|
||||
> th.@{state},
|
||||
&.@{state} > td,
|
||||
&.@{state} > th {
|
||||
background-color: @background;
|
||||
}
|
||||
}
|
||||
|
||||
// Hover states for `.table-hover`
|
||||
// Note: this is not available for cells or rows within `thead` or `tfoot`.
|
||||
.table-hover > tbody > tr {
|
||||
> td.@{state}:hover,
|
||||
> th.@{state}:hover,
|
||||
&.@{state}:hover > td,
|
||||
&:hover > .@{state},
|
||||
&.@{state}:hover > th {
|
||||
background-color: darken(@background, 5%);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
// Typography
|
||||
|
||||
.text-emphasis-variant(@color) {
|
||||
color: @color;
|
||||
a&:hover,
|
||||
a&:focus {
|
||||
color: darken(@color, 5%);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
// Text overflow
|
||||
// Requires inline-block or block for proper styling
|
||||
|
||||
.text-overflow() {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
|
@ -1,254 +0,0 @@
|
|||
// Vendor Prefixes
|
||||
//
|
||||
// All vendor mixins are deprecated as of v3.2.0 due to the introduction of
|
||||
// Autoprefixer in our Gruntfile. They will be removed in v4.
|
||||
|
||||
// - Animations
|
||||
// - Backface visibility
|
||||
// - Box shadow
|
||||
// - Box sizing
|
||||
// - Content columns
|
||||
// - Hyphens
|
||||
// - Placeholder text
|
||||
// - Transformations
|
||||
// - Transitions
|
||||
// - User Select
|
||||
|
||||
// Animations
|
||||
.animation(@animation) {
|
||||
-webkit-animation: @animation;
|
||||
-o-animation: @animation;
|
||||
animation: @animation;
|
||||
}
|
||||
|
||||
.animation-name(@name) {
|
||||
-webkit-animation-name: @name;
|
||||
animation-name: @name;
|
||||
}
|
||||
|
||||
.animation-duration(@duration) {
|
||||
-webkit-animation-duration: @duration;
|
||||
animation-duration: @duration;
|
||||
}
|
||||
|
||||
.animation-timing-function(@timing-function) {
|
||||
-webkit-animation-timing-function: @timing-function;
|
||||
animation-timing-function: @timing-function;
|
||||
}
|
||||
|
||||
.animation-delay(@delay) {
|
||||
-webkit-animation-delay: @delay;
|
||||
animation-delay: @delay;
|
||||
}
|
||||
|
||||
.animation-iteration-count(@iteration-count) {
|
||||
-webkit-animation-iteration-count: @iteration-count;
|
||||
animation-iteration-count: @iteration-count;
|
||||
}
|
||||
|
||||
.animation-direction(@direction) {
|
||||
-webkit-animation-direction: @direction;
|
||||
animation-direction: @direction;
|
||||
}
|
||||
|
||||
.animation-fill-mode(@fill-mode) {
|
||||
-webkit-animation-fill-mode: @fill-mode;
|
||||
animation-fill-mode: @fill-mode;
|
||||
}
|
||||
|
||||
// Backface visibility
|
||||
// Prevent browsers from flickering when using CSS 3D transforms.
|
||||
// Default value is `visible`, but can be changed to `hidden`
|
||||
|
||||
.backface-visibility(@visibility) {
|
||||
-webkit-backface-visibility: @visibility;
|
||||
-moz-backface-visibility: @visibility;
|
||||
backface-visibility: @visibility;
|
||||
}
|
||||
|
||||
// Drop shadows
|
||||
//
|
||||
// Note: Deprecated `.box-shadow()` as of v3.1.0 since all of Bootstrap's
|
||||
// supported browsers that have box shadow capabilities now support it.
|
||||
|
||||
.box-shadow(@shadow) {
|
||||
-webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1
|
||||
box-shadow: @shadow;
|
||||
}
|
||||
|
||||
// Box sizing
|
||||
.box-sizing(@boxmodel) {
|
||||
-webkit-box-sizing: @boxmodel;
|
||||
-moz-box-sizing: @boxmodel;
|
||||
box-sizing: @boxmodel;
|
||||
}
|
||||
|
||||
// CSS3 Content Columns
|
||||
.content-columns(@column-count; @column-gap: @grid-gutter-width) {
|
||||
-webkit-column-count: @column-count;
|
||||
-moz-column-count: @column-count;
|
||||
column-count: @column-count;
|
||||
-webkit-column-gap: @column-gap;
|
||||
-moz-column-gap: @column-gap;
|
||||
column-gap: @column-gap;
|
||||
}
|
||||
|
||||
// Optional hyphenation
|
||||
.hyphens(@mode: auto) {
|
||||
word-wrap: break-word;
|
||||
-webkit-hyphens: @mode;
|
||||
-moz-hyphens: @mode;
|
||||
-ms-hyphens: @mode; // IE10+
|
||||
-o-hyphens: @mode;
|
||||
hyphens: @mode;
|
||||
}
|
||||
|
||||
// Placeholder text
|
||||
.placeholder(@color: @input-color-placeholder) {
|
||||
// Firefox
|
||||
&::-moz-placeholder {
|
||||
color: @color;
|
||||
opacity: 1; // Override Firefox's unusual default opacity; see https://github.com/twbs/bootstrap/pull/11526
|
||||
}
|
||||
&:-ms-input-placeholder {
|
||||
color: @color;
|
||||
}
|
||||
// Internet Explorer 10+
|
||||
&::-webkit-input-placeholder {
|
||||
color: @color;
|
||||
}
|
||||
// Safari and Chrome
|
||||
}
|
||||
|
||||
// Transformations
|
||||
.scale(@ratio) {
|
||||
-webkit-transform: scale(@ratio);
|
||||
-ms-transform: scale(@ratio); // IE9 only
|
||||
-o-transform: scale(@ratio);
|
||||
transform: scale(@ratio);
|
||||
}
|
||||
|
||||
.scale(@ratioX; @ratioY) {
|
||||
-webkit-transform: scale(@ratioX, @ratioY);
|
||||
-ms-transform: scale(@ratioX, @ratioY); // IE9 only
|
||||
-o-transform: scale(@ratioX, @ratioY);
|
||||
transform: scale(@ratioX, @ratioY);
|
||||
}
|
||||
|
||||
.scaleX(@ratio) {
|
||||
-webkit-transform: scaleX(@ratio);
|
||||
-ms-transform: scaleX(@ratio); // IE9 only
|
||||
-o-transform: scaleX(@ratio);
|
||||
transform: scaleX(@ratio);
|
||||
}
|
||||
|
||||
.scaleY(@ratio) {
|
||||
-webkit-transform: scaleY(@ratio);
|
||||
-ms-transform: scaleY(@ratio); // IE9 only
|
||||
-o-transform: scaleY(@ratio);
|
||||
transform: scaleY(@ratio);
|
||||
}
|
||||
|
||||
.skew(@x; @y) {
|
||||
-webkit-transform: skewX(@x) skewY(@y);
|
||||
-ms-transform: skewX(@x) skewY(@y); // See https://github.com/twbs/bootstrap/issues/4885; IE9+
|
||||
-o-transform: skewX(@x) skewY(@y);
|
||||
transform: skewX(@x) skewY(@y);
|
||||
}
|
||||
|
||||
.translate(@x; @y) {
|
||||
-webkit-transform: translate(@x, @y);
|
||||
-ms-transform: translate(@x, @y); // IE9 only
|
||||
-o-transform: translate(@x, @y);
|
||||
transform: translate(@x, @y);
|
||||
}
|
||||
|
||||
.translate3d(@x; @y; @z) {
|
||||
-webkit-transform: translate3d(@x, @y, @z);
|
||||
transform: translate3d(@x, @y, @z);
|
||||
}
|
||||
|
||||
.rotate(@degrees) {
|
||||
-webkit-transform: rotate(@degrees);
|
||||
-ms-transform: rotate(@degrees); // IE9 only
|
||||
-o-transform: rotate(@degrees);
|
||||
transform: rotate(@degrees);
|
||||
}
|
||||
|
||||
.rotateX(@degrees) {
|
||||
-webkit-transform: rotateX(@degrees);
|
||||
-ms-transform: rotateX(@degrees); // IE9 only
|
||||
-o-transform: rotateX(@degrees);
|
||||
transform: rotateX(@degrees);
|
||||
}
|
||||
|
||||
.rotateY(@degrees) {
|
||||
-webkit-transform: rotateY(@degrees);
|
||||
-ms-transform: rotateY(@degrees); // IE9 only
|
||||
-o-transform: rotateY(@degrees);
|
||||
transform: rotateY(@degrees);
|
||||
}
|
||||
|
||||
.perspective(@perspective) {
|
||||
-webkit-perspective: @perspective;
|
||||
-moz-perspective: @perspective;
|
||||
perspective: @perspective;
|
||||
}
|
||||
|
||||
.perspective-origin(@perspective) {
|
||||
-webkit-perspective-origin: @perspective;
|
||||
-moz-perspective-origin: @perspective;
|
||||
perspective-origin: @perspective;
|
||||
}
|
||||
|
||||
.transform-origin(@origin) {
|
||||
-webkit-transform-origin: @origin;
|
||||
-moz-transform-origin: @origin;
|
||||
-ms-transform-origin: @origin; // IE9 only
|
||||
transform-origin: @origin;
|
||||
}
|
||||
|
||||
// Transitions
|
||||
|
||||
.transition(@transition) {
|
||||
-webkit-transition: @transition;
|
||||
-o-transition: @transition;
|
||||
transition: @transition;
|
||||
}
|
||||
|
||||
.transition-property(@transition-property) {
|
||||
-webkit-transition-property: @transition-property;
|
||||
transition-property: @transition-property;
|
||||
}
|
||||
|
||||
.transition-delay(@transition-delay) {
|
||||
-webkit-transition-delay: @transition-delay;
|
||||
transition-delay: @transition-delay;
|
||||
}
|
||||
|
||||
.transition-duration(@transition-duration) {
|
||||
-webkit-transition-duration: @transition-duration;
|
||||
transition-duration: @transition-duration;
|
||||
}
|
||||
|
||||
.transition-timing-function(@timing-function) {
|
||||
-webkit-transition-timing-function: @timing-function;
|
||||
transition-timing-function: @timing-function;
|
||||
}
|
||||
|
||||
.transition-transform(@transition) {
|
||||
-webkit-transition: -webkit-transform @transition;
|
||||
-moz-transition: -moz-transform @transition;
|
||||
-o-transition: -o-transform @transition;
|
||||
transition: transform @transition;
|
||||
}
|
||||
|
||||
// User select
|
||||
// For selecting text on the page
|
||||
|
||||
.user-select(@select) {
|
||||
-webkit-user-select: @select;
|
||||
-moz-user-select: @select;
|
||||
-ms-user-select: @select; // IE10+
|
||||
user-select: @select;
|
||||
}
|
|
@ -1,373 +0,0 @@
|
|||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
// anouncement bars
|
||||
.announcement {
|
||||
padding: 3px 10px;
|
||||
text-align: center;
|
||||
font-weight: 300;
|
||||
color: white;
|
||||
display: block;
|
||||
background-color: @brand-primary;
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: white;
|
||||
transition: 250ms all ease;
|
||||
}
|
||||
&:hover,
|
||||
&:focus {
|
||||
transition: 250ms all ease;
|
||||
color: darken(white, 5%);
|
||||
text-decoration: none;
|
||||
background-color: darken(@brand-primary, 5%);
|
||||
}
|
||||
strong {
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.annoucement-warning {
|
||||
background-color: @brand-danger;
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: darken(@brand-danger, 5%);
|
||||
a,
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.annoucement-danger {
|
||||
background-color: @brand-danger;
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: darken(white, 5%);
|
||||
a,
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// address icenticons
|
||||
.address-identicon-container {
|
||||
padding-left: 0;
|
||||
padding-top: @space-md;
|
||||
text-align: left;
|
||||
@media screen and (max-width: @grid-float-breakpoint) {
|
||||
padding-left: @space-lg;
|
||||
padding-right: @space-lg;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.address-identicon-container-right {
|
||||
padding-right: 0;
|
||||
padding-top: @space-md;
|
||||
text-align: right;
|
||||
.addressIdenticon {
|
||||
float: right;
|
||||
}
|
||||
@media screen and (max-width: @grid-float-breakpoint) {
|
||||
padding-left: @space-lg;
|
||||
padding-right: @space-lg;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.address-identicon-container-offline {
|
||||
padding: 0;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.addressIdenticon {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
border-radius: 50%;
|
||||
box-shadow: inset rgba(255, 255, 255, 0.5) 0 2px 2px,
|
||||
inset rgba(0, 0, 0, 0.6) 0 -1px 8px;
|
||||
}
|
||||
|
||||
.addressIdenticon.med {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
}
|
||||
|
||||
.addressIdenticon.small {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.addressIdenticon.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.addressIdenticon.float {
|
||||
float: left;
|
||||
margin-right: @space-sm;
|
||||
}
|
||||
|
||||
// helpers
|
||||
.wrap {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.bigger-on-mobile.form-control[readonly] {
|
||||
height: 60px;
|
||||
@media screen and (max-width: 767px) {
|
||||
height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
// help page
|
||||
#paneHelp {
|
||||
h3 {
|
||||
margin-top: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
// monospace things
|
||||
.mono,
|
||||
.form-control,
|
||||
#accountAddress,
|
||||
#accountBalance,
|
||||
#accountBalanceUsd,
|
||||
#accountBalanceEur,
|
||||
#accountBalanceBtc,
|
||||
#accountBalancePopMB-0,
|
||||
#accountBalancePopMB-2,
|
||||
#accountAddressMainTbl-1 {
|
||||
font-family: @font-family-monospace;
|
||||
font-weight: normal;
|
||||
letter-spacing: .02em;
|
||||
}
|
||||
|
||||
// QR Code on Offline Transactions Page
|
||||
.offline-qrcode {
|
||||
margin-top: -150px;
|
||||
max-width: 300px;
|
||||
@media screen and (max-width: 942px) {
|
||||
margin-top: -78px;
|
||||
}
|
||||
@media screen and (max-width: 769px) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
// collapsable containers
|
||||
.collapse-container {
|
||||
h2,
|
||||
h4 {
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapse-button {
|
||||
float: left;
|
||||
font-weight: 500;
|
||||
user-select: none;
|
||||
padding: 10px;
|
||||
margin: -10px -35px;
|
||||
font-size: 24px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
|
||||
// help collapsable containers
|
||||
.help .collapse-container {
|
||||
margin: 40px 0;
|
||||
.collapse-button {
|
||||
margin: -10px -30px;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// little x image next to custom tokens
|
||||
.token-remove {
|
||||
width: @font-size-small;
|
||||
height: @font-size-small;
|
||||
position: absolute;
|
||||
left: 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.node-remove {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 8px;
|
||||
}
|
||||
|
||||
.m-addresses td:first-child {
|
||||
max-width: 50px;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
.m-addresses td:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
h2 a.isActive {
|
||||
color: #333;
|
||||
cursor: default;
|
||||
&:hover,
|
||||
&:active {
|
||||
color: #333;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
margin: 6px 0;
|
||||
}
|
||||
|
||||
.output.well {
|
||||
padding: @space-sm @space;
|
||||
margin-top: -@space/2;
|
||||
p {
|
||||
margin: @space-sm 0;
|
||||
}
|
||||
}
|
||||
|
||||
label small {
|
||||
color: @gray-light;
|
||||
}
|
||||
|
||||
.write-address {
|
||||
.col-xs-1 {
|
||||
padding: 0;
|
||||
}
|
||||
.col-xs-11 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.write-boolean label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.decrypt-drtv {
|
||||
padding: @space 0;
|
||||
label.radio {
|
||||
padding: 0 @space-lg;
|
||||
}
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
max-width: 17rem;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.qr-pkey-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.qr-overlay {
|
||||
background-color: black;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.contracts {
|
||||
h4 + .dropdown,
|
||||
h4 + .dropdown .btn {
|
||||
margin-top: 0;
|
||||
}
|
||||
a.isActive {
|
||||
color: @text-color;
|
||||
}
|
||||
.dropdown-toggle {
|
||||
display: block;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
.caret {
|
||||
position: absolute;
|
||||
right: @space;
|
||||
top: 1.25rem;
|
||||
}
|
||||
}
|
||||
.dropdown-menu-left {
|
||||
font-size: @font-size-small;
|
||||
left: auto;
|
||||
right: 0;
|
||||
min-width: 37rem;
|
||||
small {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.view-wallet-content {
|
||||
@media screen and (min-width: @grid-float-breakpoint) {
|
||||
padding-left: @space*3;
|
||||
}
|
||||
h5 {
|
||||
margin-bottom: @space-xs;
|
||||
}
|
||||
a.isActive {
|
||||
color: @text-color;
|
||||
}
|
||||
}
|
||||
|
||||
#selectedTypeLedger ol {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.loading-wrap {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: fadeOut(black, 30%);
|
||||
z-index: 999;
|
||||
text-align: center;
|
||||
|
||||
.loading {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -60%);
|
||||
}
|
||||
img {
|
||||
border-radius: 50%;
|
||||
}
|
||||
h1 {
|
||||
animation: opacity 2000ms infinite ease-in-out;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@keyframes opacity {
|
||||
from,
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.embedded-logo {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
max-width: 290px;
|
||||
padding: .5rem 0;
|
||||
}
|
||||
|
||||
.ens-response {
|
||||
color: @gray;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#etherWalletPopUp {
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
min-width: 346px;
|
||||
|
||||
.back-icon {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 5px;
|
||||
&:hover,
|
||||
&:active {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.quicksend-address {
|
||||
font-family: @font-family-monospace;
|
||||
max-width: 170px;
|
||||
word-wrap: break-word;
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.chrome-tokens span {
|
||||
display: inline-block;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
@import 'etherwallet-variables.less';
|
||||
// Core variables and mixins
|
||||
@import 'bootstrap/mixins.less';
|
||||
// Utility classes
|
||||
@import 'etherwallet-custom.less';
|
||||
@import 'etherwallet-ext-custom.less';
|
||||
@import 'etherwallet-utilities.less';
|
|
@ -1,92 +0,0 @@
|
|||
/* Custom Utilities */
|
||||
|
||||
.text-navy {
|
||||
color: @ether-navy;
|
||||
}
|
||||
|
||||
.text-gray-lighter {
|
||||
color: @gray-lighter;
|
||||
}
|
||||
|
||||
.text-blue {
|
||||
color: @ether-blue;
|
||||
}
|
||||
|
||||
.text-white {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bg-navy {
|
||||
background: @ether-navy;
|
||||
}
|
||||
|
||||
.bg-gray-lighter {
|
||||
background-color: @gray-lighter;
|
||||
}
|
||||
|
||||
.bg-blue {
|
||||
background-color: @ether-blue;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.text-serif {
|
||||
font-family: @font-family-serif;
|
||||
}
|
||||
|
||||
.text-sans-serif {
|
||||
font-family: @font-family-sans-serif;
|
||||
}
|
||||
|
||||
.pad-v-sm {
|
||||
padding-top: .5em;
|
||||
padding-bottom: .5em;
|
||||
}
|
||||
|
||||
.pad-v-md {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.pad-v-lg {
|
||||
padding-top: 1.5em;
|
||||
padding-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.pad-v-xl {
|
||||
padding-top: 2em;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.marg-v-sm {
|
||||
margin-top: .5em;
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.marg-v-md {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.marg-v-lg {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.marg-v-xl {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.img-fill {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.no-scroll {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
|
@ -1,362 +0,0 @@
|
|||
// Colors
|
||||
@ether-navy: #163151;
|
||||
@ether-blue: #0e97c0;
|
||||
|
||||
@space-xs: 0.25rem;
|
||||
@space-sm: 0.5rem;
|
||||
@space-md: 0.75rem;
|
||||
@space: 1rem;
|
||||
@space-lg: 1.5rem;
|
||||
@space-xl: 2rem;
|
||||
|
||||
@gray-base: #000;
|
||||
@gray-darker: lighten(@gray-base, 13.5%);
|
||||
@gray-dark: lighten(@gray-base, 20%);
|
||||
@gray: #737373;
|
||||
@gray-light: #9a9a9a;
|
||||
@gray-lighter: #ececec;
|
||||
@gray-lightest: #fafafa;
|
||||
|
||||
@brand-primary: @ether-blue;
|
||||
@brand-success: #5dba5a;
|
||||
@brand-info: @ether-navy;
|
||||
@brand-warning: #ff9800;
|
||||
@brand-danger: #ea4b40;
|
||||
|
||||
@body-bg: #fff;
|
||||
@text-color: @gray-dark;
|
||||
|
||||
@link-color: @brand-primary;
|
||||
@link-hover-color: darken(@link-color, 5%);
|
||||
@link-hover-decoration: none;
|
||||
|
||||
@transition: 500ms all ease-in-out;
|
||||
|
||||
// Typography
|
||||
@font-family-sans-serif: 'Lato', sans-serif;
|
||||
@font-family-serif: Georgia, 'Times New Roman', Times, serif;
|
||||
@font-family-monospace: 'Roboto Mono', Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
@font-family-base: @font-family-sans-serif;
|
||||
|
||||
@base: 15;
|
||||
@font-size-pixels: @base+px;
|
||||
@font-size-pixels-xl: @base+1px; // for xl screens
|
||||
@font-size-pixels-sm: @base+px; // for small screens
|
||||
|
||||
@font-size-large-bump: 2.25rem; // 33.75
|
||||
@font-size-large: 1.9rem; // 28.5
|
||||
@font-size-medium-bump: 1.5rem; // 22.5
|
||||
@font-size-medium: 1.3rem; // 19.5
|
||||
@font-size-bump-more: 1.15rem; // 17.25
|
||||
@font-size-bump: 1.07rem; // 16.05
|
||||
@font-size-base: 1rem; // 15
|
||||
@font-size-small: 0.92rem; // 13.8
|
||||
@font-size-xs: 0.8rem; // 12
|
||||
|
||||
@font-size-h1: @font-size-large-bump;
|
||||
@font-size-h2: @font-size-large;
|
||||
@font-size-h3: @font-size-medium-bump;
|
||||
@font-size-h4: @font-size-medium;
|
||||
@font-size-h5: @font-size-bump-more;
|
||||
@font-size-h6: @font-size-bump;
|
||||
|
||||
@line-height-base: 1.4;
|
||||
@line-height-computed: 1.4;
|
||||
|
||||
@headings-font-family: inherit;
|
||||
@headings-font-weight: 700;
|
||||
@headings-line-height: 1.2;
|
||||
@headings-color: inherit;
|
||||
|
||||
// Spacing
|
||||
@padding-base-vertical: @space*.6;
|
||||
@padding-base-horizontal: @space;
|
||||
|
||||
@padding-large-vertical: @space-md;
|
||||
@padding-large-horizontal: @space-xl;
|
||||
|
||||
@padding-small-vertical: 0.1rem;
|
||||
@padding-small-horizontal: @space-sm;
|
||||
|
||||
@padding-xs-vertical: @space-xs;
|
||||
@padding-xs-horizontal: 0.2rem;
|
||||
|
||||
@line-height-large: 1.2;
|
||||
@line-height-small: 1.5;
|
||||
|
||||
@border-radius: 2px;
|
||||
|
||||
@component-active-color: #fff;
|
||||
@component-active-bg: @brand-primary;
|
||||
|
||||
@caret-width-base: @space-xs;
|
||||
@caret-width-large: @space-xs;
|
||||
|
||||
// Tables
|
||||
@table-cell-padding: @space-sm;
|
||||
@table-condensed-cell-padding: @space-xs;
|
||||
|
||||
@table-bg: transparent;
|
||||
@table-bg-accent: #f9f9f9;
|
||||
@table-bg-hover: @gray-lightest;
|
||||
@table-bg-active: @table-bg-hover;
|
||||
|
||||
@table-border-color: #ddd;
|
||||
|
||||
// Buttons
|
||||
@btn-font-weight: normal;
|
||||
|
||||
@btn-default-color: #333;
|
||||
@btn-default-bg: #ececec;
|
||||
@btn-default-border: @gray-lighter;
|
||||
|
||||
@btn-primary-color: #fff;
|
||||
@btn-primary-bg: @brand-primary;
|
||||
@btn-primary-border: darken(@btn-primary-bg, 5%);
|
||||
|
||||
@btn-success-color: #fff;
|
||||
@btn-success-bg: @brand-success;
|
||||
@btn-success-border: darken(@btn-success-bg, 5%);
|
||||
|
||||
@btn-info-color: #fff;
|
||||
@btn-info-bg: @brand-info;
|
||||
@btn-info-border: darken(@btn-info-bg, 5%);
|
||||
|
||||
@btn-warning-color: #fff;
|
||||
@btn-warning-bg: @brand-warning;
|
||||
@btn-warning-border: darken(@btn-warning-bg, 5%);
|
||||
|
||||
@btn-danger-color: #fff;
|
||||
@btn-danger-bg: @brand-danger;
|
||||
@btn-danger-border: darken(@btn-danger-bg, 5%);
|
||||
|
||||
@btn-link-disabled-color: @gray-light;
|
||||
|
||||
// Forms
|
||||
@input-bg: #fff;
|
||||
@input-bg-disabled: @gray-lightest;
|
||||
|
||||
@input-color: @gray;
|
||||
@input-border: @gray-lighter;
|
||||
|
||||
@input-border-focus: @brand-primary;
|
||||
|
||||
@input-color-placeholder: darken(@gray-lighter, 10%);
|
||||
|
||||
@input-height-base: 2.55rem;
|
||||
@input-height-large: 4rem;
|
||||
@input-height-small: 2rem;
|
||||
|
||||
@form-group-margin-bottom: @space-sm;
|
||||
|
||||
@legend-color: @gray-dark;
|
||||
@legend-border-color: #e5e5e5;
|
||||
|
||||
@input-group-addon-bg: @gray-lighter;
|
||||
@input-group-addon-border-color: @input-border;
|
||||
|
||||
@cursor-disabled: default;
|
||||
|
||||
@dropdown-bg: #fff;
|
||||
@dropdown-border: rgba(0, 0, 0, 0.15);
|
||||
@dropdown-fallback-border: @gray-lighter;
|
||||
@dropdown-divider-bg: #e5e5e5;
|
||||
|
||||
@dropdown-link-color: @ether-navy;
|
||||
@dropdown-link-hover-color: @ether-blue;
|
||||
@dropdown-link-hover-bg: @gray-lightest;
|
||||
|
||||
@dropdown-link-active-color: @component-active-color;
|
||||
@dropdown-link-active-bg: @component-active-bg;
|
||||
|
||||
@dropdown-link-disabled-color: @gray-light;
|
||||
|
||||
@dropdown-header-color: @gray-light;
|
||||
|
||||
@dropdown-caret-color: #000;
|
||||
|
||||
@zindex-navbar: 1000;
|
||||
@zindex-dropdown: 1000;
|
||||
@zindex-popover: 1060;
|
||||
@zindex-tooltip: 1070;
|
||||
@zindex-navbar-fixed: 1030;
|
||||
@zindex-modal-background: 1040;
|
||||
@zindex-modal: 1050;
|
||||
@zindex-alerts: 1060;
|
||||
|
||||
@screen-xs: 32rem;
|
||||
@screen-xs-min: @screen-xs;
|
||||
|
||||
@screen-sm: 51.2rem;
|
||||
@screen-sm-min: @screen-sm;
|
||||
|
||||
@screen-md: 66.133333333rem;
|
||||
@screen-md-min: @screen-md;
|
||||
|
||||
@screen-lg: 80rem;
|
||||
@screen-lg-min: @screen-lg;
|
||||
|
||||
@screen-xl: 94rem;
|
||||
@screen-xl-min: @screen-xl;
|
||||
|
||||
@screen-xs-max: (@screen-sm-min - 1);
|
||||
@screen-sm-max: (@screen-md-min - 1);
|
||||
@screen-md-max: (@screen-lg-min - 1);
|
||||
@screen-lg-max: (@screen-xl-min - 1);
|
||||
|
||||
@grid-columns: 12;
|
||||
@grid-gutter-width: 3rem;
|
||||
@grid-float-breakpoint: @screen-sm-min;
|
||||
@grid-float-breakpoint-max: (@grid-float-breakpoint - 1);
|
||||
|
||||
@cont-padding: 5%;
|
||||
@cont-padding-lg: 7.5%;
|
||||
|
||||
@container-tablet: (@screen-sm + @grid-gutter-width);
|
||||
@container-sm: @container-tablet;
|
||||
|
||||
@container-desktop: (@screen-md + @grid-gutter-width);
|
||||
@container-md: @container-desktop;
|
||||
|
||||
@container-large-desktop: (@screen-lg + @grid-gutter-width);
|
||||
@container-lg: @container-large-desktop;
|
||||
|
||||
@state-success-text: darken(@brand-success, 10%);
|
||||
@state-success-bg: #dff0d8;
|
||||
@state-success-border: darken(spin(@state-success-bg, -10), 5%);
|
||||
|
||||
@state-info-text: darken(@brand-info, 10%);
|
||||
@state-info-bg: #d9edf7;
|
||||
@state-info-border: darken(spin(@state-info-bg, -10), 7%);
|
||||
|
||||
@state-warning-text: darken(@brand-warning, 10%);
|
||||
@state-warning-bg: #fcf8e3;
|
||||
@state-warning-border: darken(spin(@state-warning-bg, -10), 5%);
|
||||
|
||||
@state-danger-text: darken(@brand-danger, 10%);
|
||||
@state-danger-bg: #f2dede;
|
||||
@state-danger-border: darken(spin(@state-danger-bg, -10), 5%);
|
||||
|
||||
@tooltip-max-width: 200px;
|
||||
@tooltip-color: #fff;
|
||||
@tooltip-bg: #000;
|
||||
@tooltip-opacity: 0.9;
|
||||
|
||||
@tooltip-arrow-width: @space-sm;
|
||||
@tooltip-arrow-color: @tooltip-bg;
|
||||
|
||||
@label-default-bg: @gray-light;
|
||||
@label-primary-bg: @brand-primary;
|
||||
@label-success-bg: @brand-success;
|
||||
@label-info-bg: @brand-info;
|
||||
@label-warning-bg: @brand-warning;
|
||||
@label-danger-bg: @brand-danger;
|
||||
|
||||
@label-color: #fff;
|
||||
@label-link-hover-color: #fff;
|
||||
|
||||
@modal-inner-padding: @space*1.5;
|
||||
|
||||
@modal-title-padding: @space;
|
||||
@modal-title-line-height: @line-height-base;
|
||||
|
||||
@modal-content-bg: #fff;
|
||||
@modal-content-border-color: rgba(0, 0, 0, 0.2);
|
||||
@modal-content-fallback-border-color: #999;
|
||||
|
||||
@modal-backdrop-bg: #000;
|
||||
@modal-backdrop-opacity: 0.5;
|
||||
@modal-header-border-color: #e5e5e5;
|
||||
@modal-footer-border-color: @modal-header-border-color;
|
||||
|
||||
@modal-lg: 70rem;
|
||||
@modal-md: 50rem;
|
||||
@modal-sm: 30rem;
|
||||
|
||||
@alert-border-radius: @border-radius;
|
||||
@alert-link-font-weight: bold;
|
||||
|
||||
@alert-success-bg: @brand-success;
|
||||
@alert-success-text: white;
|
||||
@alert-success-border: @alert-success-bg;
|
||||
|
||||
@alert-info-bg: @brand-primary;
|
||||
@alert-info-text: white;
|
||||
@alert-info-border: @alert-info-bg;
|
||||
|
||||
@alert-warning-bg: @brand-warning;
|
||||
@alert-warning-text: white;
|
||||
@alert-warning-border: @alert-warning-bg;
|
||||
|
||||
@alert-danger-bg: @brand-danger;
|
||||
@alert-danger-text: white;
|
||||
@alert-danger-border: @alert-danger-bg;
|
||||
|
||||
@progress-bg: @gray-lightest;
|
||||
@progress-bar-color: #fff;
|
||||
@progress-border-radius: @border-radius;
|
||||
|
||||
@progress-bar-bg: @brand-primary;
|
||||
@progress-bar-success-bg: @brand-success;
|
||||
@progress-bar-warning-bg: @brand-warning;
|
||||
@progress-bar-danger-bg: @brand-danger;
|
||||
@progress-bar-info-bg: @brand-info;
|
||||
|
||||
@list-group-bg: #fff;
|
||||
@list-group-border: #ddd;
|
||||
@list-group-border-radius: @border-radius;
|
||||
|
||||
@list-group-hover-bg: @gray-lightest;
|
||||
@list-group-active-color: @component-active-color;
|
||||
@list-group-active-bg: @component-active-bg;
|
||||
@list-group-active-border: @list-group-active-bg;
|
||||
@list-group-active-text-color: lighten(@list-group-active-bg, 40%);
|
||||
|
||||
@list-group-disabled-color: @gray-light;
|
||||
@list-group-disabled-bg: @gray-lighter;
|
||||
@list-group-disabled-text-color: @list-group-disabled-color;
|
||||
|
||||
@list-group-link-color: #555;
|
||||
@list-group-link-hover-color: @list-group-link-color;
|
||||
@list-group-link-heading-color: #333;
|
||||
|
||||
@thumbnail-padding: 4px;
|
||||
@thumbnail-bg: @body-bg;
|
||||
@thumbnail-border: #ddd;
|
||||
@thumbnail-border-radius: @border-radius;
|
||||
|
||||
@thumbnail-caption-color: @text-color;
|
||||
@thumbnail-caption-padding: 9px;
|
||||
|
||||
@well-bg: @gray-lightest;
|
||||
@well-border: darken(@well-bg, 7%);
|
||||
|
||||
@badge-font-weight: bold;
|
||||
@badge-line-height: 1;
|
||||
@badge-border-radius: 10px;
|
||||
|
||||
@close-font-weight: bold;
|
||||
@close-color: #000;
|
||||
@close-text-shadow: 0 1px 0 #fff;
|
||||
|
||||
@code-color: #c7254e;
|
||||
@code-bg: #f9f2f4;
|
||||
|
||||
@kbd-color: #fff;
|
||||
@kbd-bg: #333;
|
||||
|
||||
@pre-bg: @gray-lightest;
|
||||
@pre-color: @gray-dark;
|
||||
@pre-border-color: @gray-lighter;
|
||||
@pre-scrollable-max-height: 340px;
|
||||
|
||||
@component-offset-horizontal: 180px;
|
||||
@text-muted: @gray-light;
|
||||
@abbr-border-color: @gray-light;
|
||||
@headings-small-color: inherit;
|
||||
@blockquote-small-color: @gray-light;
|
||||
@blockquote-font-size: (@font-size-base * 1.25);
|
||||
@blockquote-border-color: @gray-lighter;
|
||||
@page-header-border-color: @gray-lighter;
|
||||
@dl-horizontal-offset: @component-offset-horizontal;
|
||||
@hr-border: @gray-lighter;
|
|
@ -13,7 +13,7 @@ export const AddressField: React.SFC<Props> = ({ isReadOnly }) => (
|
|||
withProps={({ currentTo, isValid, onChange, readOnly }) => (
|
||||
<div className="input-group-wrapper">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('SEND_addr')}</div>
|
||||
<div className="input-group-header">{translate('SEND_ADDR_SHORT')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${isValid ? '' : 'invalid'}`}
|
||||
type="text"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { AmountFieldFactory } from './AmountFieldFactory';
|
||||
import { UnitDropDown } from 'components';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import translate from 'translations';
|
||||
import { Input } from 'components/ui';
|
||||
|
||||
interface Props {
|
||||
|
@ -18,14 +18,14 @@ export const AmountField: React.SFC<Props> = ({
|
|||
<AmountFieldFactory
|
||||
withProps={({ currentValue: { raw }, isValid, onChange, readOnly }) => (
|
||||
<div className="input-group-wrapper">
|
||||
<label className="input-group input-group-inline-dropdown">
|
||||
<div className="input-group-header">{translate('SEND_amount')}</div>
|
||||
<label className="input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('SEND_AMOUNT_SHORT')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
isAmountValid(raw, customValidator, isValid) ? '' : 'invalid'
|
||||
}`}
|
||||
type="number"
|
||||
placeholder={translateRaw('SEND_amount_short')}
|
||||
placeholder={'1'}
|
||||
value={raw}
|
||||
readOnly={!!readOnly}
|
||||
onChange={onChange}
|
||||
|
@ -37,5 +37,8 @@ export const AmountField: React.SFC<Props> = ({
|
|||
/>
|
||||
);
|
||||
|
||||
const isAmountValid = (raw, customValidator, isValid) =>
|
||||
customValidator ? customValidator(raw) : isValid;
|
||||
const isAmountValid = (
|
||||
raw: string,
|
||||
customValidator: ((rawAmount: string) => boolean) | undefined,
|
||||
isValid: boolean
|
||||
) => (customValidator ? customValidator(raw) : isValid);
|
||||
|
|
|
@ -98,7 +98,7 @@ class AccountInfo extends React.Component<Props, State> {
|
|||
const wallet = this.props.wallet as LedgerWallet | TrezorWallet;
|
||||
return (
|
||||
<div className="AccountInfo">
|
||||
<h5 className="AccountInfo-section-header">{translate('sidebar_AccountAddr')}</h5>
|
||||
<h5 className="AccountInfo-section-header">{translate('SIDEBAR_ACCOUNTADDR')}</h5>
|
||||
<div className="AccountInfo-section AccountInfo-address-section">
|
||||
<div className="AccountInfo-address-icon">
|
||||
<Identicon address={address} size="100%" />
|
||||
|
@ -134,7 +134,9 @@ class AccountInfo extends React.Component<Props, State> {
|
|||
});
|
||||
}}
|
||||
>
|
||||
{confirmAddr ? null : 'Display address on ' + wallet.getWalletType()}
|
||||
{confirmAddr
|
||||
? null
|
||||
: translate('SIDEBAR_DISPLAY_ADDR', { $wallet: wallet.getWalletType() })}
|
||||
</a>
|
||||
{confirmAddr ? (
|
||||
<span className="AccountInfo-address-confirm">
|
||||
|
@ -145,7 +147,7 @@ class AccountInfo extends React.Component<Props, State> {
|
|||
)}
|
||||
|
||||
<div className="AccountInfo-section">
|
||||
<h5 className="AccountInfo-section-header">{translate('sidebar_AccountBal')}</h5>
|
||||
<h5 className="AccountInfo-section-header">{translate('SIDEBAR_ACCOUNTBAL')}</h5>
|
||||
<ul className="AccountInfo-list">
|
||||
<li className="AccountInfo-list-item AccountInfo-balance">
|
||||
<span
|
||||
|
@ -182,7 +184,7 @@ class AccountInfo extends React.Component<Props, State> {
|
|||
|
||||
{(!!blockExplorer || !!tokenExplorer) && (
|
||||
<div className="AccountInfo-section">
|
||||
<h5 className="AccountInfo-section-header">{translate('sidebar_TransHistory')}</h5>
|
||||
<h5 className="AccountInfo-section-header">{translate('SIDEBAR_TRANSHISTORY')}</h5>
|
||||
<ul className="AccountInfo-list">
|
||||
{!!blockExplorer && (
|
||||
<li className="AccountInfo-list-item">
|
||||
|
|
|
@ -8,7 +8,6 @@ import { chain, flatMap } from 'lodash';
|
|||
import { TokenBalance, getShownTokenBalances } from 'selectors/wallet';
|
||||
import { Balance } from 'libs/wallet';
|
||||
import './EquivalentValues.scss';
|
||||
import { Wei } from 'libs/units';
|
||||
import { AppState } from 'reducers';
|
||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -16,6 +15,7 @@ import btcIco from 'assets/images/bitcoin.png';
|
|||
import ethIco from 'assets/images/ether.png';
|
||||
import repIco from 'assets/images/augur.png';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import BN from 'bn.js';
|
||||
|
||||
interface AllValue {
|
||||
symbol: string;
|
||||
|
@ -51,6 +51,14 @@ interface DispatchProps {
|
|||
fetchCCRates: TFetchCCRatesRequested;
|
||||
}
|
||||
|
||||
interface FiatSymbols {
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
interface Rates {
|
||||
[rate: string]: number;
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps;
|
||||
|
||||
class EquivalentValues extends React.Component<Props, State> {
|
||||
|
@ -110,7 +118,7 @@ class EquivalentValues extends React.Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
public selectOption = equivalentValues => {
|
||||
public selectOption = (equivalentValues: Option) => {
|
||||
this.setState({ equivalentValues });
|
||||
};
|
||||
|
||||
|
@ -120,28 +128,38 @@ class EquivalentValues extends React.Component<Props, State> {
|
|||
const isFetching =
|
||||
!balance || balance.isPending || !tokenBalances || Object.keys(rates).length === 0;
|
||||
const pairRates = this.generateValues(equivalentValues.label, equivalentValues.value);
|
||||
const fiatSymbols = {
|
||||
const fiatSymbols: FiatSymbols = {
|
||||
USD: '$',
|
||||
EUR: '€',
|
||||
GBP: '£',
|
||||
CHF: ' '
|
||||
};
|
||||
const coinAndTokenSymbols = {
|
||||
const coinAndTokenSymbols: any = {
|
||||
BTC: btcIco,
|
||||
ETH: ethIco,
|
||||
REP: repIco
|
||||
};
|
||||
interface ValueProps {
|
||||
className: string;
|
||||
rate: string;
|
||||
value: BN | null;
|
||||
symbol?: string;
|
||||
icon?: string;
|
||||
key?: number | string;
|
||||
}
|
||||
|
||||
const Value = ({ className = '', rate, value, symbol = '', icon = '' }) => (
|
||||
<div className={`EquivalentValues-values-currency ${className}`}>
|
||||
<img src={icon} />
|
||||
{!!symbol && <span className="EquivalentValues-values-currency-fiat-symbol">{symbol}</span>}
|
||||
<span className="EquivalentValues-values-currency-label">{rate}</span>{' '}
|
||||
const Value = (props: ValueProps) => (
|
||||
<div className={`EquivalentValues-values-currency ${props.className}`}>
|
||||
<img src={props.icon} />
|
||||
{!!props.symbol && (
|
||||
<span className="EquivalentValues-values-currency-fiat-symbol">{props.symbol}</span>
|
||||
)}
|
||||
<span className="EquivalentValues-values-currency-label">{props.rate}</span>{' '}
|
||||
<span className="EquivalentValues-values-currency-value">
|
||||
<UnitDisplay
|
||||
unit={'ether'}
|
||||
value={value}
|
||||
displayShortBalance={rateSymbols.isFiat(rate) ? 2 : 3}
|
||||
value={props.value}
|
||||
displayShortBalance={rateSymbols.isFiat(props.rate) ? 2 : 3}
|
||||
checkOffline={true}
|
||||
/>
|
||||
</span>
|
||||
|
@ -151,13 +169,13 @@ class EquivalentValues extends React.Component<Props, State> {
|
|||
return (
|
||||
<div className="EquivalentValues">
|
||||
<div className="EquivalentValues-header">
|
||||
<h5 className="EquivalentValues-title">{translate('sidebar_Equiv')}</h5>
|
||||
<h5 className="EquivalentValues-title">{translate('SIDEBAR_EQUIV')}</h5>
|
||||
<Select
|
||||
name="equivalentValues"
|
||||
// TODO: Update type
|
||||
value={equivalentValues as any}
|
||||
options={options as any}
|
||||
onChange={this.selectOption}
|
||||
onChange={this.selectOption as any}
|
||||
clearable={false}
|
||||
searchable={false}
|
||||
/>
|
||||
|
@ -165,13 +183,11 @@ class EquivalentValues extends React.Component<Props, State> {
|
|||
|
||||
{isOffline ? (
|
||||
<div className="EquivalentValues-offline well well-sm">
|
||||
Equivalent values are unavailable offline
|
||||
{translate('EQUIV_VALS_OFFLINE')}
|
||||
</div>
|
||||
) : network.isTestnet ? (
|
||||
<div className="text-center">
|
||||
<h5 style={{ color: 'red' }}>
|
||||
On test network, equivalent values will not be displayed.
|
||||
</h5>
|
||||
<h5 style={{ color: 'red' }}>{translate('EQUIV_VALS_TESTNET')}</h5>
|
||||
</div>
|
||||
) : ratesError ? (
|
||||
<h5>{ratesError}</h5>
|
||||
|
@ -210,7 +226,7 @@ class EquivalentValues extends React.Component<Props, State> {
|
|||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<p>Sorry, equivalent values are not supported for this unit.</p>
|
||||
<p>{translate('EQUIV_VALS_UNSUPPORTED_UNIT')}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
@ -224,7 +240,7 @@ class EquivalentValues extends React.Component<Props, State> {
|
|||
const allRates = Object.values(balance).map(
|
||||
value => !!rates[value.symbol] && rates[value.symbol]
|
||||
);
|
||||
const allEquivalentValues = allRates.map((rateType, i) => {
|
||||
const allEquivalentValues = allRates.map((rateType: any, i) => {
|
||||
return {
|
||||
symbol: Object.keys(rates)[i],
|
||||
equivalentValues: [
|
||||
|
@ -260,9 +276,9 @@ class EquivalentValues extends React.Component<Props, State> {
|
|||
// return equivalent value (unit * rate * balance)
|
||||
private handleValues(unit: string, balance: Balance['wei']) {
|
||||
const { rates } = this.props;
|
||||
const ratesObj = { ...rates[unit] };
|
||||
const ratesObj: Rates = { ...rates[unit] };
|
||||
return Object.keys(ratesObj).map(key => {
|
||||
const value = (balance as Wei).muln(ratesObj[key]);
|
||||
const value = balance!.muln(ratesObj[key]);
|
||||
return { rate: key, value };
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,10 +2,14 @@ import React from 'react';
|
|||
import CoinbaseLogo from 'assets/images/logo-coinbase.svg';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
|
||||
export const Coinbase: React.SFC = () => (
|
||||
interface Props {
|
||||
address: string;
|
||||
}
|
||||
|
||||
export const Coinbase: React.SFC<Props> = ({ address }) => (
|
||||
<NewTabLink
|
||||
className="Promos-promo Promos--coinbase"
|
||||
href="https://buy.coinbase.com?code=60c05061-3a76-57be-b1cd-a7afa97bcb8c&address=0xA7DeFf12461661212734dB35AdE9aE7d987D648c&crypto_currency=ETH¤cy=USD"
|
||||
href={`https://buy.coinbase.com?code=60c05061-3a76-57be-b1cd-a7afa97bcb8c&address=${address}&crypto_currency=ETH¤cy=USD`}
|
||||
>
|
||||
<div className="Promos-promo-inner">
|
||||
<div className="Promos-promo-text">
|
||||
|
@ -13,7 +17,7 @@ export const Coinbase: React.SFC = () => (
|
|||
<h5 key="2">Buy ETH with USD</h5>
|
||||
</div>
|
||||
<div className="Promos-promo-images">
|
||||
<img src={CoinbaseLogo} />
|
||||
<img src={CoinbaseLogo} alt="Coinbase logo" />
|
||||
</div>
|
||||
</div>
|
||||
</NewTabLink>
|
||||
|
|
|
@ -11,8 +11,8 @@ export const HardwareWallets: React.SFC = () => (
|
|||
<h6>Learn more about protecting your funds.</h6>
|
||||
</div>
|
||||
<div className="Promos-promo-images">
|
||||
<img src={ledgerLogo} />
|
||||
<img src={trezorLogo} />
|
||||
<img src={ledgerLogo} alt="Ledger Logo" />
|
||||
<img src={trezorLogo} alt="Trezor Logo" />
|
||||
</div>
|
||||
</div>
|
||||
</HelpLink>
|
||||
|
|
|
@ -13,7 +13,7 @@ export const Shapeshift: React.SFC = () => (
|
|||
</h5>
|
||||
</div>
|
||||
<div className="Promos-promo-images">
|
||||
<img src={ShapeshiftLogo} />
|
||||
<img src={ShapeshiftLogo} alt="Shapeshift Logo" />
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) => (
|
||||
<CSSTransition {...props} timeout={300} classNames="carousel">
|
||||
{children}
|
||||
</CSSTransition>
|
||||
);
|
||||
|
||||
// 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<StateProps, State> {
|
||||
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 <Coinbase address={wallet.getAddressString()} />;
|
||||
} else {
|
||||
return <Shapeshift />;
|
||||
}
|
||||
} else {
|
||||
return promos[activePromo];
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { activePromo } = this.state;
|
||||
|
||||
return (
|
||||
<div className="Promos">
|
||||
<TransitionGroup className="Promos-promo-wrapper">
|
||||
<CarouselAnimation key={Math.random()}>{promos[activePromo]}</CarouselAnimation>
|
||||
<CarouselAnimation key={Math.random()}>{this.getPromo()}</CarouselAnimation>
|
||||
</TransitionGroup>
|
||||
<div className="Promos-nav">
|
||||
{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);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "common/sass/variables";
|
||||
@import 'common/sass/variables';
|
||||
|
||||
.AddCustom {
|
||||
&-field {
|
||||
|
@ -11,23 +11,20 @@
|
|||
|
||||
&-buttons {
|
||||
padding-top: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&-help {
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
&-btn {
|
||||
margin-right: 10px;
|
||||
|
||||
&.btn-primary {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
&.btn-default {
|
||||
width: 110px;
|
||||
}
|
||||
padding: 0.5rem 1.5rem;
|
||||
margin: 0.25rem 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { HELP_ARTICLE } from 'config';
|
||||
import { isPositiveIntegerOrZero, isValidETHAddress } from 'libs/validators';
|
||||
import translate from 'translations';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { HelpLink, Input } from 'components/ui';
|
||||
import './AddCustomTokenForm.scss';
|
||||
import { Token } from 'types/network';
|
||||
|
@ -47,17 +47,17 @@ export default class AddCustomTokenForm extends React.PureComponent<Props, State
|
|||
{
|
||||
name: 'symbol',
|
||||
value: symbol,
|
||||
label: translate('TOKEN_Symbol')
|
||||
label: translateRaw('TOKEN_SYMBOL')
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
value: address,
|
||||
label: translate('TOKEN_Addr')
|
||||
label: translateRaw('TOKEN_ADDR')
|
||||
},
|
||||
{
|
||||
name: 'decimal',
|
||||
value: decimal,
|
||||
label: translate('TOKEN_Dec')
|
||||
label: translateRaw('TOKEN_DEC')
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -66,11 +66,11 @@ export default class AddCustomTokenForm extends React.PureComponent<Props, State
|
|||
{fields.map(field => {
|
||||
return (
|
||||
<label className="AddCustom-field form-group" key={field.name}>
|
||||
<span className="AddCustom-field-label">{field.label}</span>
|
||||
<div className="input-group-header">{field.label}</div>
|
||||
<Input
|
||||
className={`${
|
||||
errors[field.name] ? 'invalid' : field.value ? 'valid' : ''
|
||||
} AddCustom-field-input input-sm`}
|
||||
} input-group-input-small`}
|
||||
type="text"
|
||||
name={field.name}
|
||||
value={field.value}
|
||||
|
@ -83,21 +83,21 @@ export default class AddCustomTokenForm extends React.PureComponent<Props, State
|
|||
);
|
||||
})}
|
||||
|
||||
<HelpLink article={HELP_ARTICLE.ADDING_NEW_TOKENS} className="AddCustom-buttons-help">
|
||||
{translate('ADD_CUSTOM_TKN_HELP')}
|
||||
</HelpLink>
|
||||
<div className="AddCustom-buttons">
|
||||
<HelpLink article={HELP_ARTICLE.ADDING_NEW_TOKENS} className="AddCustom-buttons-help">
|
||||
{translate('Need help? Learn how to add custom tokens.')}
|
||||
</HelpLink>
|
||||
<button
|
||||
className="AddCustom-buttons-btn btn btn-primary btn-sm"
|
||||
disabled={!this.isValid()}
|
||||
>
|
||||
{translate('x_Save')}
|
||||
</button>
|
||||
<button
|
||||
className="AddCustom-buttons-btn btn btn-sm btn-default"
|
||||
onClick={this.props.toggleForm}
|
||||
>
|
||||
{translate('x_Cancel')}
|
||||
{translate('ACTION_2')}
|
||||
</button>
|
||||
<button
|
||||
className="AddCustom-buttons-btn btn btn-primary btn-sm"
|
||||
disabled={!this.isValid()}
|
||||
>
|
||||
{translate('X_SAVE')}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -15,19 +15,23 @@ interface Props {
|
|||
onRemoveCustomToken(symbol: string): any;
|
||||
}
|
||||
|
||||
interface TrackedTokens {
|
||||
[symbol: string]: boolean;
|
||||
}
|
||||
|
||||
interface State {
|
||||
trackedTokens: { [symbol: string]: boolean };
|
||||
showCustomTokenForm: boolean;
|
||||
}
|
||||
export default class TokenBalances extends React.PureComponent<Props, State> {
|
||||
public state = {
|
||||
public state: State = {
|
||||
trackedTokens: {},
|
||||
showCustomTokenForm: false
|
||||
};
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
if (nextProps.tokenBalances !== this.props.tokenBalances) {
|
||||
const trackedTokens = nextProps.tokenBalances.reduce((prev, t) => {
|
||||
const trackedTokens = nextProps.tokenBalances.reduce<TrackedTokens>((prev, t) => {
|
||||
prev[t.symbol] = !t.balance.isZero();
|
||||
return prev;
|
||||
}, {});
|
||||
|
@ -46,11 +50,9 @@ export default class TokenBalances extends React.PureComponent<Props, State> {
|
|||
bottom = (
|
||||
<div className="TokenBalances-buttons">
|
||||
<button className="btn btn-primary btn-block" onClick={this.handleSetWalletTokens}>
|
||||
<span>{translate('x_Save')}</span>
|
||||
<span>{translate('X_SAVE')}</span>
|
||||
</button>
|
||||
<p className="TokenBalances-buttons-help">
|
||||
{translate('Missing tokens? You can add custom tokens next.')}
|
||||
</p>
|
||||
<p className="TokenBalances-buttons-help">{translate('PROMPT_ADD_CUSTOM_TKN')}</p>
|
||||
</div>
|
||||
);
|
||||
} else if (showCustomTokenForm) {
|
||||
|
@ -67,10 +69,10 @@ export default class TokenBalances extends React.PureComponent<Props, State> {
|
|||
bottom = (
|
||||
<div className="TokenBalances-buttons">
|
||||
<button className="btn btn-default btn-xs" onClick={this.toggleShowCustomTokenForm}>
|
||||
<span>{translate('SEND_custom')}</span>
|
||||
</button>{' '}
|
||||
<span>{translate('SEND_CUSTOM')}</span>
|
||||
</button>
|
||||
<button className="btn btn-default btn-xs" onClick={this.props.scanWalletForTokens}>
|
||||
<span>Scan for New Tokens</span>
|
||||
<span>{translate('SCAN_TOKENS')}</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
@ -101,7 +103,7 @@ export default class TokenBalances extends React.PureComponent<Props, State> {
|
|||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<div className="well well-sm text-center">No tokens found</div>
|
||||
<div className="well well-sm text-center">{translate('SCAN_TOKENS_FAIL_NO_TOKENS')}</div>
|
||||
)}
|
||||
{bottom}
|
||||
</div>
|
||||
|
|
|
@ -54,6 +54,7 @@ export default class TokenRow extends React.PureComponent<Props, State> {
|
|||
{!!custom && (
|
||||
<img
|
||||
src={removeIcon}
|
||||
alt="Remove"
|
||||
className="TokenRow-symbol-remove"
|
||||
title="Remove Token"
|
||||
onClick={this.onRemove}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "common/sass/variables";
|
||||
@import 'common/sass/variables';
|
||||
|
||||
.TokenBalances {
|
||||
&-title {
|
||||
|
@ -34,6 +34,12 @@
|
|||
}
|
||||
|
||||
&-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
& > &-btn {
|
||||
margin: 0.25rem 0.5rem;
|
||||
}
|
||||
&-help {
|
||||
padding-top: 10px;
|
||||
text-align: center;
|
||||
|
|
|
@ -74,7 +74,7 @@ class TokenBalances extends React.Component<Props> {
|
|||
className="TokenBalances-scan btn btn-primary btn-block"
|
||||
onClick={this.scanWalletForTokens}
|
||||
>
|
||||
{translate('Scan for my Tokens')}
|
||||
{translate('SCAN_TOKENS')}
|
||||
</button>
|
||||
);
|
||||
} else {
|
||||
|
@ -95,7 +95,7 @@ class TokenBalances extends React.Component<Props> {
|
|||
|
||||
return (
|
||||
<section className="TokenBalances">
|
||||
<h5 className="TokenBalances-title">{translate('sidebar_TokenBal')}</h5>
|
||||
<h5 className="TokenBalances-title">{translate('SIDEBAR_TOKENBAL')}</h5>
|
||||
{content}
|
||||
</section>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@import "common/sass/variables";
|
||||
@import "common/sass/mixins";
|
||||
@import 'common/sass/variables';
|
||||
@import 'common/sass/mixins';
|
||||
|
||||
.BetaAgreement {
|
||||
@include cover-message;
|
||||
|
@ -20,7 +20,6 @@
|
|||
margin: 0 auto;
|
||||
border: none;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
transition: $transition;
|
||||
|
||||
&.is-continue {
|
||||
|
|
|
@ -7,6 +7,7 @@ import { AppState } from 'reducers';
|
|||
import './Body.scss';
|
||||
import { getNetworkConfig } from 'selectors/config';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import translate from 'translations';
|
||||
|
||||
interface State {
|
||||
showDetails: boolean;
|
||||
|
@ -43,7 +44,7 @@ class BodyClass extends React.Component<StateProps, State> {
|
|||
}`}
|
||||
onClick={this.toggleDetails}
|
||||
>
|
||||
Details
|
||||
{translate('ACTION_8')}
|
||||
</button>
|
||||
{showDetails && <Details />}
|
||||
</div>
|
||||
|
|
|
@ -8,6 +8,8 @@ import { connect } from 'react-redux';
|
|||
import { SerializedTransaction } from 'components/renderCbs';
|
||||
import { AppState } from 'reducers';
|
||||
import { getFrom, getUnit, isEtherTransaction } from 'selectors/transaction';
|
||||
import { toChecksumAddress } from 'ethereumjs-util';
|
||||
import translate from 'translations';
|
||||
|
||||
interface StateProps {
|
||||
from: AppState['transaction']['meta']['from'];
|
||||
|
@ -24,7 +26,9 @@ class AddressesClass extends Component<StateProps> {
|
|||
return (
|
||||
<SerializedTransaction
|
||||
withSerializedTransaction={(_, { to, data }) => {
|
||||
const toFormatted = isToken ? ERC20.transfer.decodeInput(data)._to : to;
|
||||
const toFormatted = toChecksumAddress(
|
||||
isToken ? ERC20.transfer.decodeInput(data)._to : to
|
||||
);
|
||||
return (
|
||||
<div className="tx-modal-address">
|
||||
<div className="tx-modal-address-from">
|
||||
|
@ -32,7 +36,7 @@ class AddressesClass extends Component<StateProps> {
|
|||
<Identicon className="tx-modal-address-from-icon" size={size} address={from} />
|
||||
)}
|
||||
<div className="tx-modal-address-from-content">
|
||||
<h5 className="tx-modal-address-from-title">From </h5>
|
||||
<h5 className="tx-modal-address-from-title">{translate('CONFIRM_TX_FROM')} </h5>
|
||||
<h5 className="tx-modal-address-from-address small">{from}</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -42,7 +46,9 @@ class AddressesClass extends Component<StateProps> {
|
|||
<img src={arrow} alt="arrow" />
|
||||
</div>
|
||||
<div className="tx-modal-address-tkn-contract-content">
|
||||
<p className="tx-modal-address-tkn-contract-title">via the {unit} contract</p>
|
||||
<p className="tx-modal-address-tkn-contract-title">
|
||||
{translate('CONFIRM_TX_VIA_CONTRACT', { unit })}
|
||||
</p>
|
||||
<a
|
||||
className="small tx-modal-address-tkn-contract-link"
|
||||
href={ETHAddressExplorer(to)}
|
||||
|
@ -59,7 +65,7 @@ class AddressesClass extends Component<StateProps> {
|
|||
address={toFormatted}
|
||||
/>
|
||||
<div className="tx-modal-address-to-content">
|
||||
<h5 className="tx-modal-address-to-title">To </h5>
|
||||
<h5 className="tx-modal-address-to-title">{translate('CONFIRM_TX_TO')} </h5>
|
||||
<h5 className="small tx-modal-address-to-address">{toFormatted}</h5>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { SerializedTxParams, getParamsFromSerializedTx } from 'selectors/transac
|
|||
import { connect } from 'react-redux';
|
||||
import { getNetworkConfig } from 'selectors/config';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import translate from 'translations';
|
||||
|
||||
interface StateProps extends SerializedTxParams, AllUSDValues {
|
||||
network: NetworkConfig;
|
||||
|
@ -32,7 +33,7 @@ class AmountsClass extends Component<StateProps> {
|
|||
<table className="tx-modal-amount">
|
||||
<tbody>
|
||||
<tr className="tx-modal-amount-send">
|
||||
<td>You'll Send</td>
|
||||
<td>{translate('CONFIRM_TX_SENDING')}</td>
|
||||
<td>
|
||||
<UnitDisplay
|
||||
value={currentValue}
|
||||
|
@ -54,7 +55,7 @@ class AmountsClass extends Component<StateProps> {
|
|||
)}
|
||||
</tr>
|
||||
<tr className="tx-modal-amount-fee">
|
||||
<td>Transaction Fee</td>
|
||||
<td>{translate('CONFIRM_TX_FEE')}</td>
|
||||
<td>
|
||||
<UnitDisplay
|
||||
value={fee}
|
||||
|
@ -77,7 +78,7 @@ class AmountsClass extends Component<StateProps> {
|
|||
</tr>
|
||||
{!isToken && (
|
||||
<tr className="tx-modal-amount-total">
|
||||
<td>Total</td>
|
||||
<td>{translate('CONFIRM_TX_TOTAL')}</td>
|
||||
<td>
|
||||
<UnitDisplay
|
||||
value={total}
|
||||
|
|
|
@ -11,9 +11,9 @@ import {
|
|||
TBroadcastWeb3TransactionRequested
|
||||
} from 'actions/transaction';
|
||||
import { currentTransactionBroadcasting } from 'selectors/transaction';
|
||||
import { translateRaw } from 'translations';
|
||||
import './ConfirmationModalTemplate.scss';
|
||||
import { AppState } from 'reducers';
|
||||
import { translateRaw, translate } from 'translations';
|
||||
|
||||
interface DispatchProps {
|
||||
broadcastLocalTransactionRequested: TBroadcastLocalTransactionRequested;
|
||||
|
@ -73,7 +73,7 @@ class ConfirmationModalTemplateClass extends React.Component<Props, State> {
|
|||
const { timeToRead } = this.state;
|
||||
const buttonPrefix = timeToRead > 0 ? `(${timeToRead}) ` : '';
|
||||
const defaultConfirmButton = {
|
||||
text: buttonPrefix + translateRaw('SENDModal_Yes'),
|
||||
text: buttonPrefix + translateRaw('ACTION_11'),
|
||||
type: 'primary' as IButton['type'],
|
||||
disabled: timeToRead > 0,
|
||||
onClick: this.confirm
|
||||
|
@ -85,7 +85,7 @@ class ConfirmationModalTemplateClass extends React.Component<Props, State> {
|
|||
timeLeft: timeToRead,
|
||||
timePrefix: buttonPrefix,
|
||||
timeLocked: defaultConfirmButton.disabled,
|
||||
defaultText: translateRaw('SENDModal_Yes'),
|
||||
defaultText: translateRaw('ACTION_11'),
|
||||
type: defaultConfirmButton.type
|
||||
})
|
||||
: defaultConfirmButton;
|
||||
|
@ -93,7 +93,7 @@ class ConfirmationModalTemplateClass extends React.Component<Props, State> {
|
|||
const buttons: IButton[] = [
|
||||
confirmButton,
|
||||
{
|
||||
text: translateRaw('SENDModal_No'),
|
||||
text: translate('ACTION_2'),
|
||||
type: 'default',
|
||||
onClick: onClose
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ class ConfirmationModalTemplateClass extends React.Component<Props, State> {
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title="Confirm Transaction"
|
||||
title={translateRaw('CONFIRM_TX_MODAL_TITLE')}
|
||||
buttons={buttons}
|
||||
handleClose={onClose}
|
||||
disableButtons={transactionBroadcasting}
|
||||
|
|
|
@ -9,7 +9,7 @@ export const DataField: React.SFC<{}> = () => (
|
|||
withProps={({ data: { raw }, dataExists, onChange, readOnly }) => (
|
||||
<div className="input-group-wrapper">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">{translate('OFFLINE_Step2_Label_6')}</div>
|
||||
<div className="input-group-header">{translate('OFFLINE_STEP2_LABEL_6')}</div>
|
||||
<Input
|
||||
className={dataExists ? 'is-valid' : 'is-invalid'}
|
||||
type="text"
|
||||
|
|
|
@ -1,26 +1,33 @@
|
|||
import React from 'react';
|
||||
import { translateRaw } from 'translations';
|
||||
import { Link } from 'react-router-dom';
|
||||
import translate from 'translations';
|
||||
import { NewTabLink } from 'components/ui';
|
||||
import { BlockExplorerConfig } from 'types/network';
|
||||
|
||||
export interface TransactionSucceededProps {
|
||||
txHash: string;
|
||||
blockExplorer: BlockExplorerConfig;
|
||||
blockExplorer?: BlockExplorerConfig;
|
||||
}
|
||||
|
||||
const TransactionSucceeded = ({ txHash, blockExplorer }: TransactionSucceededProps) => {
|
||||
const txHashLink = blockExplorer.txUrl(txHash);
|
||||
let verifyBtn: React.ReactElement<string> | undefined;
|
||||
if (blockExplorer) {
|
||||
verifyBtn = (
|
||||
<NewTabLink className="btn btn-xs" href={blockExplorer.txUrl(txHash)}>
|
||||
{translate('VERIFY_TX', { $block_explorer: blockExplorer.name })}
|
||||
</NewTabLink>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>{translateRaw('SUCCESS_3') + txHash}</p>
|
||||
<a
|
||||
className="btn btn-xs btn-info string"
|
||||
href={txHashLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Verify Transaction
|
||||
</a>
|
||||
<p>
|
||||
{translate('SUCCESS_3')} {txHash}
|
||||
</p>
|
||||
{verifyBtn}
|
||||
<Link to={`/tx-status?txHash=${txHash}`} className="btn btn-xs">
|
||||
{translate('NAV_CheckTxStatus')}
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import Modal, { IButton } from 'components/ui/Modal';
|
|||
import { HelpLink } from 'components/ui';
|
||||
import { HELP_ARTICLE } from 'config';
|
||||
import './DisclaimerModal.scss';
|
||||
import translate from 'translations';
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
|
@ -10,7 +11,9 @@ interface Props {
|
|||
}
|
||||
|
||||
const DisclaimerModal: React.SFC<Props> = ({ isOpen, handleClose }) => {
|
||||
const buttons: IButton[] = [{ text: 'Okay', type: 'default', onClick: handleClose }];
|
||||
const buttons: IButton[] = [
|
||||
{ text: translate('ACTION_10'), type: 'default', onClick: handleClose }
|
||||
];
|
||||
return (
|
||||
<Modal isOpen={isOpen} title="Disclaimer" buttons={buttons} handleClose={handleClose}>
|
||||
<p>
|
||||
|
@ -41,7 +44,11 @@ const DisclaimerModal: React.SFC<Props> = ({ isOpen, handleClose }) => {
|
|||
English and, because of this, the English version of our website is the official text.
|
||||
</p>
|
||||
<p>
|
||||
<b>MIT License</b> Copyright © 2015-2017 MyCrypto LLC
|
||||
<b>MIT License</b>
|
||||
<br />
|
||||
Copyright (c) 2015-2017 MyEtherWallet LLC
|
||||
<br />
|
||||
Copyright (c) 2018 MyCrypto, Inc.
|
||||
</p>
|
||||
<p>
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import React from 'react';
|
||||
import './PreFooter.scss';
|
||||
import translate from 'translations';
|
||||
|
||||
interface Props {
|
||||
openModal(): void;
|
||||
|
@ -10,9 +11,8 @@ const PreFooter: React.SFC<Props> = ({ openModal }) => {
|
|||
<section className="pre-footer">
|
||||
<div className="container">
|
||||
<p>
|
||||
MyCrypto.com does not hold your keys for you. We cannot access accounts, recover keys,
|
||||
reset passwords, nor reverse transactions. Protect your keys & always check that you are
|
||||
on correct URL. <a onClick={openModal}>You are responsible for your security.</a>
|
||||
{translate('PREFOOTER_WARNING')}{' '}
|
||||
<a onClick={openModal}>{translate('PREFOOTER_SECURITY_WARNING')}</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -14,6 +14,7 @@ import DisclaimerModal from './DisclaimerModal';
|
|||
import { NewTabLink } from 'components/ui';
|
||||
import OnboardModal from 'containers/OnboardModal';
|
||||
import './index.scss';
|
||||
import { translateRaw } from 'translations';
|
||||
|
||||
const SocialMediaLink = ({ link, text }: Link) => {
|
||||
return (
|
||||
|
@ -58,39 +59,39 @@ const PRODUCT_INFO: Link[] = [
|
|||
{
|
||||
link:
|
||||
'https://chrome.google.com/webstore/detail/etheraddresslookup/pdknmigbbbhmllnmgdfalmedcmcefdfn',
|
||||
text: 'Ether Address Lookup'
|
||||
text: translateRaw('ETHER_ADDRESS_LOOKUP')
|
||||
},
|
||||
{
|
||||
link:
|
||||
'https://chrome.google.com/webstore/detail/ethersecuritylookup/bhhfhgpgmifehjdghlbbijjaimhmcgnf',
|
||||
text: 'Ether Security Lookup'
|
||||
text: translateRaw('ETHER_SECURITY_LOOKUP')
|
||||
},
|
||||
{
|
||||
link: 'https://etherscamdb.info/',
|
||||
text: 'EtherScamDB'
|
||||
text: translateRaw('ETHERSCAMDB')
|
||||
},
|
||||
{
|
||||
link: 'https://www.mycrypto.com/helpers.html',
|
||||
text: 'Helpers & ENS Debugging'
|
||||
text: translateRaw('FOOTER_HELP_AND_DEBUGGING')
|
||||
},
|
||||
{
|
||||
link: 'mailto:press@mycrypto.com',
|
||||
text: 'Press Inquiries'
|
||||
text: translateRaw('FOOTER_PRESS')
|
||||
}
|
||||
];
|
||||
|
||||
const AFFILIATES: Link[] = [
|
||||
{
|
||||
link: ledgerReferralURL,
|
||||
text: 'Buy a Ledger Wallet'
|
||||
text: translateRaw('LEDGER_REFERAL_1')
|
||||
},
|
||||
{
|
||||
link: trezorReferralURL,
|
||||
text: 'Buy a TREZOR'
|
||||
text: translateRaw('TREZOR_REFERAL')
|
||||
},
|
||||
{
|
||||
link: ethercardReferralURL,
|
||||
text: 'Get an ether.card'
|
||||
text: translateRaw('ETHERCARD_REFERAL')
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -165,51 +166,52 @@ export default class Footer extends React.PureComponent<Props, State> {
|
|||
|
||||
<div className="Footer-about-links">
|
||||
<a href="https://mycrypto.com">MyCrypto.com</a>
|
||||
<NewTabLink href={knowledgeBaseURL}>Help & Support</NewTabLink>
|
||||
<NewTabLink href="https://about.mycrypto.com">Our Team</NewTabLink>
|
||||
<NewTabLink href={knowledgeBaseURL}>{translateRaw('FOOTER_SUPPORT')}</NewTabLink>
|
||||
<NewTabLink href="https://about.mycrypto.com">
|
||||
{translateRaw('FOOTER_TEAM')}
|
||||
</NewTabLink>
|
||||
</div>
|
||||
|
||||
<p className="Footer-about-text">
|
||||
MyCrypto is an open-source, client-side tool for generating Ether Wallets, handling
|
||||
ERC-20 tokens, and interacting with the blockchain more easily. Developed by and for
|
||||
the community since 2015, we’re focused on building awesome products that put the
|
||||
power in people’s hands.
|
||||
</p>
|
||||
<p className="Footer-about-text">{translateRaw('FOOTER_ABOUT')}</p>
|
||||
|
||||
<div className="Footer-about-legal">
|
||||
<div className="Footer-about-legal-text">
|
||||
© {new Date().getFullYear()} MyCrypto, Inc.
|
||||
</div>
|
||||
<div className="Footer-about-legal-text">
|
||||
<a onClick={this.toggleModal}>Disclaimer</a>
|
||||
<a onClick={this.toggleModal}>{translateRaw('DISCLAIMER')}</a>
|
||||
</div>
|
||||
<div className="Footer-about-legal-text">v{VERSION}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="Footer-support Footer-section">
|
||||
<h5 className="Footer-support-title">Support Us & Our Friends</h5>
|
||||
<h5 className="Footer-support-title">{translateRaw('FOOTER_AFFILIATE_TITLE')}</h5>
|
||||
<div className="Footer-support-affiliates">
|
||||
{AFFILIATES.map(link => (
|
||||
<NewTabLink key={link.text} href={link.link}>
|
||||
{AFFILIATES.map((link, i) => (
|
||||
<NewTabLink key={i} href={link.link}>
|
||||
{link.text}
|
||||
</NewTabLink>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="Footer-support-donate">
|
||||
<div className="Footer-support-donate-currency">Donate ETH</div>
|
||||
<div className="Footer-support-donate-currency">
|
||||
{translateRaw('DONATE_CURRENCY', { $currency: 'ETH' })}
|
||||
</div>
|
||||
<div className="Footer-support-donate-address">{donationAddressMap.ETH}</div>
|
||||
</div>
|
||||
|
||||
<div className="Footer-support-donate">
|
||||
<div className="Footer-support-donate-currency">Donate BTC</div>
|
||||
<div className="Footer-support-donate-currency">
|
||||
{translateRaw('DONATE_CURRENCY', { $currency: 'BTC' })}
|
||||
</div>
|
||||
<div className="Footer-support-donate-address">{donationAddressMap.BTC}</div>
|
||||
</div>
|
||||
|
||||
<div className="Footer-support-friends">
|
||||
{FRIENDS.map(link => (
|
||||
<NewTabLink key={link.text} href={link.link}>
|
||||
{FRIENDS.map((link, i) => (
|
||||
<NewTabLink key={i} href={link.link}>
|
||||
{link.text}
|
||||
</NewTabLink>
|
||||
))}
|
||||
|
|
|
@ -17,14 +17,14 @@ export const GasLimitField: React.SFC<Props> = ({ customLabel, disabled }) => (
|
|||
<div className="input-group-wrapper AdvancedGas-gas-price">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">
|
||||
{customLabel ? customLabel : translate('TRANS_gas')}
|
||||
{customLabel ? customLabel : translate('TRANS_GAS')}
|
||||
<div className="flex-spacer" />
|
||||
<InlineSpinner active={gasEstimationPending} text="Calculating" />
|
||||
</div>
|
||||
<Input
|
||||
className={gasLimitValidator(raw) ? 'is-valid' : 'is-invalid'}
|
||||
type="number"
|
||||
placeholder="e.g. 21000"
|
||||
placeholder="21000"
|
||||
readOnly={!!readOnly}
|
||||
value={raw}
|
||||
onChange={onChange}
|
||||
|
|
|
@ -13,7 +13,7 @@ interface StateProps {
|
|||
}
|
||||
|
||||
interface OwnProps {
|
||||
withProps(props: CallBackProps);
|
||||
withProps(props: CallBackProps): null | React.ReactElement<any>;
|
||||
onChange(value: React.FormEvent<HTMLInputElement>): void;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,9 +41,9 @@ export default class GenerateKeystoreModal extends React.Component<Props, State>
|
|||
}
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps) {
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
if (nextProps.privateKey !== this.props.privateKey) {
|
||||
this.setState({ privateKey: nextProps.privateKey });
|
||||
this.setState({ privateKey: nextProps.privateKey || '' });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,14 +55,14 @@ export default class GenerateKeystoreModal extends React.Component<Props, State>
|
|||
|
||||
return (
|
||||
<Modal
|
||||
title={translate('Generate Keystore File')}
|
||||
title={translateRaw('GENERATE_KEYSTORE_ACTION')}
|
||||
isOpen={this.props.isOpen}
|
||||
handleClose={this.handleClose}
|
||||
>
|
||||
<form className="GenKeystore" onSubmit={this.handleSubmit}>
|
||||
<div className="input-group-wrapper GenKeystore-field">
|
||||
<label className="input-group input-group-inline-dropdown">
|
||||
<div className="input-group-header">Private Key</div>
|
||||
<label className="input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('X_PRIVKEY2')}</div>
|
||||
<TogglablePassword
|
||||
name="privateKey"
|
||||
value={privateKey}
|
||||
|
@ -74,13 +74,15 @@ export default class GenerateKeystoreModal extends React.Component<Props, State>
|
|||
</label>
|
||||
</div>
|
||||
<div className="input-group-wrapper GenKeystore-field">
|
||||
<label className="input-group input-group-inline-dropdown">
|
||||
<div className="input-group-header">Password</div>
|
||||
<label className="input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('INPUT_PASSWORD_LABEL')}</div>
|
||||
<TogglablePassword
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={this.handleInput}
|
||||
placeholder={translateRaw('Minimum 9 characters')}
|
||||
placeholder={translateRaw('INPUT_PASSWORD_PLACEHOLDER', {
|
||||
$pass_length: MINIMUM_PASSWORD_LENGTH.toString()
|
||||
})}
|
||||
isValid={isPasswordValid}
|
||||
/>
|
||||
</label>
|
||||
|
@ -91,24 +93,20 @@ export default class GenerateKeystoreModal extends React.Component<Props, State>
|
|||
className="GenKeystore-button btn btn-primary btn-block"
|
||||
disabled={!isPrivateKeyValid || !isPasswordValid}
|
||||
>
|
||||
{translate('Generate Keystore File')}
|
||||
{translate('GENERATE_KEYSTORE_ACTION')}
|
||||
</button>
|
||||
) : hasError ? (
|
||||
<p className="alert alert-danger">
|
||||
Keystore generation failed or was invalid. In order to prevent loss of funds, we
|
||||
cannot provide you with a keystore file that may be corrupted. Refresh the page or use
|
||||
a different browser, and try again.
|
||||
</p>
|
||||
<p className="alert alert-danger">{translate('GENERATE_KEYSTORE_FAILED')}</p>
|
||||
) : (
|
||||
<a
|
||||
onClick={this.handleClose}
|
||||
href={keystoreFile.blob}
|
||||
className="GenKeystore-button btn btn-success btn-block"
|
||||
aria-label={translateRaw('x_Keystore')}
|
||||
aria-describedby={translateRaw('x_KeystoreDesc')}
|
||||
aria-label={translateRaw('X_KEYSTORE')}
|
||||
aria-describedby={translateRaw('X_KEYSTOREDESC')}
|
||||
download={keystoreFile.filename}
|
||||
>
|
||||
{translate('Download Keystore File')}
|
||||
{translate('ACTION_12')}
|
||||
</a>
|
||||
)}
|
||||
</form>
|
||||
|
|
|
@ -6,7 +6,7 @@ export const GenerateTransaction: React.SFC<{}> = () => (
|
|||
<GenerateTransactionFactory
|
||||
withProps={({ disabled, isWeb3Wallet, onClick }) => (
|
||||
<button disabled={disabled} className="btn btn-info btn-block" onClick={onClick}>
|
||||
{isWeb3Wallet ? translate('SEND_generate') : translate('DEP_signtx')}
|
||||
{isWeb3Wallet ? translate('SEND_GENERATE') : translate('DEP_SIGNTX')}
|
||||
</button>
|
||||
)}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
.CustomNodeModal {
|
||||
.flex-wrapper {
|
||||
margin: 0px -8px;
|
||||
> .input-group {
|
||||
margin: 0px 8px;
|
||||
> .input-group-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
input[type='checkbox'] {
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import Modal, { IButton } from 'components/ui/Modal';
|
||||
import translate from 'translations';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { CustomNetworkConfig } from 'types/network';
|
||||
import { CustomNodeConfig } from 'types/node';
|
||||
import { TAddCustomNetwork, addCustomNetwork, AddCustomNodeAction } from 'actions/config';
|
||||
|
@ -13,19 +13,13 @@ import {
|
|||
} from 'selectors/config';
|
||||
import { CustomNode } from 'libs/nodes';
|
||||
import { Input } from 'components/ui';
|
||||
import Dropdown from 'components/ui/Dropdown';
|
||||
import './CustomNodeModal.scss';
|
||||
|
||||
const CUSTOM = 'custom';
|
||||
|
||||
interface InputProps {
|
||||
name: string;
|
||||
placeholder?: string;
|
||||
type?: string;
|
||||
autoComplete?: 'off';
|
||||
onFocus?(): void;
|
||||
onBlur?(): void;
|
||||
}
|
||||
const CUSTOM = { label: 'Custom', value: 'custom' };
|
||||
|
||||
interface OwnProps {
|
||||
isOpen: boolean;
|
||||
addCustomNode(payload: AddCustomNodeAction['payload']): void;
|
||||
handleClose(): void;
|
||||
}
|
||||
|
@ -55,7 +49,7 @@ interface State {
|
|||
type Props = OwnProps & StateProps & DispatchProps;
|
||||
|
||||
class CustomNodeModal extends React.Component<Props, State> {
|
||||
public state: State = {
|
||||
public INITIAL_STATE = {
|
||||
name: '',
|
||||
url: '',
|
||||
network: Object.keys(this.props.staticNetworks)[0],
|
||||
|
@ -66,9 +60,17 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
username: '',
|
||||
password: ''
|
||||
};
|
||||
public state: State = this.INITIAL_STATE;
|
||||
|
||||
public componentDidUpdate(prevProps: Props) {
|
||||
// Reset state when modal opens
|
||||
if (!prevProps.isOpen && prevProps.isOpen !== this.props.isOpen) {
|
||||
this.setState(this.INITIAL_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { customNetworks, handleClose, staticNetworks } = this.props;
|
||||
const { customNetworks, handleClose, staticNetworks, isOpen } = this.props;
|
||||
const { network } = this.state;
|
||||
const isHttps = window.location.protocol.includes('https');
|
||||
const invalids = this.getInvalids();
|
||||
|
@ -82,164 +84,157 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
},
|
||||
{
|
||||
type: 'default',
|
||||
text: translate('x_Cancel'),
|
||||
text: translate('ACTION_2'),
|
||||
onClick: handleClose
|
||||
}
|
||||
];
|
||||
|
||||
const conflictedNode = this.getConflictedNode();
|
||||
|
||||
const staticNetwrks = Object.keys(staticNetworks).map(net => {
|
||||
return { label: net, value: net };
|
||||
});
|
||||
const customNetwrks = Object.entries(customNetworks).map(([id, net]) => {
|
||||
return { label: net.name + ' (Custom)', value: id };
|
||||
});
|
||||
const options = [...staticNetwrks, ...customNetwrks, CUSTOM];
|
||||
return (
|
||||
<Modal
|
||||
title={translate('NODE_Title')}
|
||||
isOpen={true}
|
||||
title={translateRaw('NODE_Title')}
|
||||
isOpen={isOpen}
|
||||
buttons={buttons}
|
||||
handleClose={handleClose}
|
||||
maxWidth={580}
|
||||
>
|
||||
<div>
|
||||
{isHttps && <div className="alert alert-warning small">{translate('NODE_Warning')}</div>}
|
||||
{isHttps && <div className="alert alert-warning small">{translate('NODE_WARNING')}</div>}
|
||||
|
||||
{conflictedNode && (
|
||||
<div className="alert alert-warning small">
|
||||
You already have a node called '{conflictedNode.name}' that matches this one, saving
|
||||
this will overwrite it
|
||||
{conflictedNode && (
|
||||
<div className="alert alert-warning small">
|
||||
{translate('CUSTOM_NODE_CONFLICT', { conflictedNode: conflictedNode.name })}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form className="CustomNodeModal">
|
||||
<div className="flex-wrapper">
|
||||
<label className="col-sm-9 input-group flex-grow-1">
|
||||
<div className="input-group-header">{translate('CUSTOM_NODE_NAME')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${this.state.name && invalids.name ? 'invalid' : ''}`}
|
||||
type="text"
|
||||
placeholder="My Node"
|
||||
value={this.state.name}
|
||||
onChange={e => this.setState({ name: e.currentTarget.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="col-sm-3 input-group">
|
||||
<div className="input-group-header">Network</div>
|
||||
<Dropdown
|
||||
className="input-group-dropdown"
|
||||
value={network}
|
||||
options={options}
|
||||
clearable={false}
|
||||
onChange={(e: { label: string; value: string }) =>
|
||||
this.setState({ network: e.value })
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{network === CUSTOM.value && (
|
||||
<div className="flex-wrapper">
|
||||
<label className="col-sm-6 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_NAME')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.customNetworkId && invalids.customNetworkId ? 'invalid' : ''
|
||||
}`}
|
||||
type="text"
|
||||
placeholder="My Custom Network"
|
||||
value={this.state.customNetworkId}
|
||||
onChange={e => this.setState({ customNetworkId: e.currentTarget.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="col-sm-3 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_CURRENCY')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.customNetworkUnit && invalids.customNetworkUnit ? 'invalid' : ''
|
||||
}`}
|
||||
type="text"
|
||||
placeholder="ETH"
|
||||
value={this.state.customNetworkUnit}
|
||||
onChange={e => this.setState({ customNetworkUnit: e.currentTarget.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="col-sm-3 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_CHAIN_ID')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.customNetworkChainId && invalids.customNetworkChainId
|
||||
? 'invalid'
|
||||
: ''
|
||||
}`}
|
||||
type="text"
|
||||
placeholder="1"
|
||||
value={this.state.customNetworkChainId}
|
||||
onChange={e => this.setState({ customNetworkChainId: e.currentTarget.value })}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form>
|
||||
<div className="row">
|
||||
<div className="col-sm-7">
|
||||
<label>{translate('NODE_Name')}</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'name',
|
||||
placeholder: 'My Node'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-5">
|
||||
<label>Network</label>
|
||||
<select
|
||||
className="form-control"
|
||||
name="network"
|
||||
value={network}
|
||||
onChange={this.handleChange}
|
||||
>
|
||||
{Object.keys(staticNetworks).map(net => (
|
||||
<option key={net} value={net}>
|
||||
{net}
|
||||
</option>
|
||||
))}
|
||||
{Object.entries(customNetworks).map(([id, net]) => (
|
||||
<option key={id} value={id}>
|
||||
{net.name} (Custom)
|
||||
</option>
|
||||
))}
|
||||
<option value={CUSTOM}>Custom...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<label className="input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('CUSTOM_NETWORK_URL')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${this.state.url && invalids.url ? 'invalid' : ''}`}
|
||||
type="text"
|
||||
placeholder="https://127.0.0.1:8545/"
|
||||
value={this.state.url}
|
||||
onChange={e => this.setState({ url: e.currentTarget.value })}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</label>
|
||||
|
||||
{network === CUSTOM && (
|
||||
<div className="row">
|
||||
<div className="col-sm-6">
|
||||
<label className="is-required">Network Name</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'customNetworkId',
|
||||
placeholder: 'My Custom Network'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-3">
|
||||
<label className="is-required">Currency</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'customNetworkUnit',
|
||||
placeholder: 'ETH'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
</div>
|
||||
<div className="col-sm-3">
|
||||
<label>Chain ID</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'customNetworkChainId',
|
||||
placeholder: 'e.g. 1'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="hasAuth"
|
||||
checked={this.state.hasAuth}
|
||||
onChange={() => this.setState({ hasAuth: !this.state.hasAuth })}
|
||||
/>
|
||||
<span>{translate('CUSTOM_NETWORK_HTTP_AUTH')}</span>
|
||||
</label>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<label>URL</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'url',
|
||||
placeholder: 'e.g. https://127.0.0.1:8545/',
|
||||
autoComplete: 'off'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
</div>
|
||||
{this.state.hasAuth && (
|
||||
<div className="flex-wrapper ">
|
||||
<label className="col-sm-6 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('INPUT_USERNAME_LABEL')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.username && invalids.username ? 'invalid' : ''
|
||||
}`}
|
||||
type="text"
|
||||
value={this.state.username}
|
||||
onChange={e => this.setState({ username: e.currentTarget.value })}
|
||||
/>
|
||||
</label>
|
||||
<label className="col-sm-6 input-group input-group-inline">
|
||||
<div className="input-group-header">{translate('INPUT_PASSWORD_LABEL')}</div>
|
||||
<Input
|
||||
className={`input-group-input ${
|
||||
this.state.password && invalids.password ? 'invalid' : ''
|
||||
}`}
|
||||
type="password"
|
||||
value={this.state.password}
|
||||
onChange={e => this.setState({ password: e.currentTarget.value })}
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="hasAuth"
|
||||
checked={this.state.hasAuth}
|
||||
onChange={this.handleCheckbox}
|
||||
/>{' '}
|
||||
<span>HTTP Basic Authentication</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.hasAuth && (
|
||||
<div className="row">
|
||||
<div className="col-sm-6">
|
||||
<label className="is-required">Username</label>
|
||||
{this.renderInput({ name: 'username' }, invalids)}
|
||||
</div>
|
||||
<div className="col-sm-6">
|
||||
<label className="is-required">Password</label>
|
||||
{this.renderInput(
|
||||
{
|
||||
name: 'password',
|
||||
type: 'password'
|
||||
},
|
||||
invalids
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
private renderInput(input: InputProps, invalids: { [key: string]: boolean }) {
|
||||
return (
|
||||
<Input
|
||||
className={`${this.state[input.name] && invalids[input.name] ? 'invalid' : ''}`}
|
||||
value={this.state[input.name]}
|
||||
onChange={this.handleChange}
|
||||
autoComplete="off"
|
||||
{...input}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private getInvalids(): { [key: string]: boolean } {
|
||||
const {
|
||||
url,
|
||||
|
@ -278,7 +273,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
}
|
||||
|
||||
// If they have a custom network, make sure info is provided
|
||||
if (network === CUSTOM) {
|
||||
if (network === CUSTOM.value) {
|
||||
if (!customNetworkId) {
|
||||
invalids.customNetworkId = true;
|
||||
}
|
||||
|
@ -315,7 +310,7 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
const { network, url, name, username, password } = this.state;
|
||||
|
||||
const networkId =
|
||||
network === CUSTOM
|
||||
network === CUSTOM.value
|
||||
? this.makeCustomNetworkId(this.makeCustomNetworkConfigFromState())
|
||||
: network;
|
||||
|
||||
|
@ -348,20 +343,10 @@ class CustomNodeModal extends React.Component<Props, State> {
|
|||
return customNodes[config.id];
|
||||
}
|
||||
|
||||
private handleChange = (ev: React.FormEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
const { name, value } = ev.currentTarget;
|
||||
this.setState({ [name as any]: value });
|
||||
};
|
||||
|
||||
private handleCheckbox = (ev: React.FormEvent<HTMLInputElement>) => {
|
||||
const { name } = ev.currentTarget;
|
||||
this.setState({ [name as any]: !this.state[name as keyof State] });
|
||||
};
|
||||
|
||||
private saveAndAdd = () => {
|
||||
const node = this.makeCustomNodeConfigFromState();
|
||||
|
||||
if (this.state.network === CUSTOM) {
|
||||
if (this.state.network === CUSTOM.value) {
|
||||
const network = this.makeCustomNetworkConfigFromState();
|
||||
|
||||
this.props.addCustomNetwork({ config: network, id: node.network });
|
||||
|
|
|
@ -11,19 +11,19 @@ export interface TabLink {
|
|||
|
||||
const tabs: TabLink[] = [
|
||||
{
|
||||
name: 'Account View & Send',
|
||||
name: 'NAV_VIEW',
|
||||
to: '/account'
|
||||
},
|
||||
{
|
||||
name: 'NAV_GenerateWallet',
|
||||
name: 'NAV_GENERATEWALLET',
|
||||
to: '/generate'
|
||||
},
|
||||
{
|
||||
name: 'NAV_Swap',
|
||||
name: 'NAV_SWAP',
|
||||
to: '/swap'
|
||||
},
|
||||
{
|
||||
name: 'NAV_Contracts',
|
||||
name: 'NAV_CONTRACTS',
|
||||
to: '/contracts'
|
||||
},
|
||||
{
|
||||
|
@ -31,19 +31,19 @@ const tabs: TabLink[] = [
|
|||
to: '/ens'
|
||||
},
|
||||
{
|
||||
name: 'Sign & Verify Message',
|
||||
name: 'NAV_SIGN',
|
||||
to: '/sign-and-verify-message'
|
||||
},
|
||||
{
|
||||
name: 'NAV_TxStatus',
|
||||
name: 'NAV_TXSTATUS',
|
||||
to: '/tx-status'
|
||||
},
|
||||
{
|
||||
name: 'Broadcast Transaction',
|
||||
name: 'NAV_BROADCAST',
|
||||
to: '/pushTx'
|
||||
},
|
||||
{
|
||||
name: 'NAV_Help',
|
||||
name: 'NAV_HELP',
|
||||
to: `${knowledgeBaseURL}`,
|
||||
external: true
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
@import "common/sass/variables";
|
||||
@import 'common/sass/variables';
|
||||
|
||||
.NavigationLink {
|
||||
display: inline-block;
|
||||
|
@ -14,7 +14,7 @@
|
|||
min-height: 2.75rem;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
content: '';
|
||||
background: $brand-primary;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
|
@ -45,13 +45,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
#NAV_Swap a:before {
|
||||
content:"";
|
||||
#NAV_SWAP a:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
margin-top: -.1rem;
|
||||
margin-top: -0.1rem;
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
background-image: url('~assets/images/swap.svg');
|
||||
background-image: url('~assets/images/logo-shapeshift-no-text.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
|
|
|
@ -192,12 +192,11 @@ class Header extends Component<Props, State> {
|
|||
|
||||
<Navigation color={!network.isCustom && network.color} />
|
||||
|
||||
{isAddingCustomNode && (
|
||||
<CustomNodeModal
|
||||
addCustomNode={this.addCustomNode}
|
||||
handleClose={this.closeCustomNodeModal}
|
||||
/>
|
||||
)}
|
||||
<CustomNodeModal
|
||||
isOpen={isAddingCustomNode}
|
||||
addCustomNode={this.addCustomNode}
|
||||
handleClose={this.closeCustomNodeModal}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { withRouter, RouteComponentProps } from 'react-router-dom';
|
|||
import Modal, { IButton } from 'components/ui/Modal';
|
||||
import { AppState } from 'reducers';
|
||||
import { resetWallet, TResetWallet } from 'actions/wallet';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
|
||||
interface Props extends RouteComponentProps<{}> {
|
||||
// State
|
||||
|
@ -18,7 +19,7 @@ interface State {
|
|||
}
|
||||
|
||||
class LogOutPromptClass extends React.Component<Props, State> {
|
||||
constructor(props) {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
nextLocation: null,
|
||||
|
@ -42,17 +43,17 @@ class LogOutPromptClass extends React.Component<Props, State> {
|
|||
|
||||
public render() {
|
||||
const buttons: IButton[] = [
|
||||
{ text: 'Log Out', type: 'primary', onClick: this.onConfirm },
|
||||
{ text: 'Cancel', type: 'default', onClick: this.onCancel }
|
||||
{ text: translate('ACTION_7'), type: 'primary', onClick: this.onConfirm },
|
||||
{ text: translate('ACTION_2'), type: 'default', onClick: this.onCancel }
|
||||
];
|
||||
return (
|
||||
<Modal
|
||||
title="You are about to log out"
|
||||
title={translateRaw('WALLET_LOGOUT_MODAL_TITLE')}
|
||||
isOpen={this.state.openModal}
|
||||
handleClose={this.onCancel}
|
||||
buttons={buttons}
|
||||
>
|
||||
<p>Leaving this page will log you out. Are you sure you want to continue?</p>
|
||||
<p>{translate('WALLET_LOGOUT_MODAL_DESC')}</p>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -62,7 +63,7 @@ class LogOutPromptClass extends React.Component<Props, State> {
|
|||
};
|
||||
|
||||
private onConfirm = () => {
|
||||
const { nextLocation } = this.state;
|
||||
const { nextLocation: next } = this.state;
|
||||
this.props.resetWallet();
|
||||
this.setState(
|
||||
{
|
||||
|
@ -70,8 +71,8 @@ class LogOutPromptClass extends React.Component<Props, State> {
|
|||
nextLocation: null
|
||||
},
|
||||
() => {
|
||||
if (nextLocation) {
|
||||
this.props.history.push(nextLocation.pathname);
|
||||
if (next) {
|
||||
this.props.history.push(`${next.pathname}${next.search}${next.hash}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -35,7 +35,7 @@ class NonceField extends React.Component<Props> {
|
|||
<div className="input-group-wrapper Nonce-label">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">
|
||||
{translate('OFFLINE_Step2_Label_5')}
|
||||
{translate('OFFLINE_STEP2_LABEL_5')}
|
||||
<Help
|
||||
size="x1"
|
||||
link="https://support.mycrypto.com/transactions/what-is-nonce.html"
|
||||
|
@ -44,7 +44,7 @@ class NonceField extends React.Component<Props> {
|
|||
<Input
|
||||
className={`Nonce-field-input ${!!value ? 'is-valid' : 'is-invalid'}`}
|
||||
type="number"
|
||||
placeholder="e.g. 7"
|
||||
placeholder="7"
|
||||
value={raw}
|
||||
readOnly={readOnly}
|
||||
onChange={onChange}
|
||||
|
|
|
@ -100,8 +100,8 @@ export default class PaperWallet extends React.Component<Props, {}> {
|
|||
|
||||
return (
|
||||
<div style={styles.container}>
|
||||
<img src={sidebarImg} style={styles.sidebar} />
|
||||
<img src={ethLogo} style={styles.ethLogo} />
|
||||
<img src={sidebarImg} style={styles.sidebar} alt="MyCrypto Logo" />
|
||||
<img src={ethLogo} style={styles.ethLogo} alt="ETH Logo" />
|
||||
|
||||
<div style={styles.block}>
|
||||
<div style={styles.box}>
|
||||
|
@ -111,7 +111,7 @@ export default class PaperWallet extends React.Component<Props, {}> {
|
|||
</div>
|
||||
|
||||
<div style={styles.block}>
|
||||
<img src={notesBg} style={styles.box} />
|
||||
<img src={notesBg} style={styles.box} aria-hidden={true} />
|
||||
<p style={styles.blockText}>AMOUNT / NOTES</p>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { PaperWallet } from 'components';
|
||||
import React from 'react';
|
||||
import { translateRaw } from 'translations';
|
||||
import printElement from 'utils/printElement';
|
||||
import { stripHexPrefix } from 'libs/values';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
|
||||
export const print = (address: string, privateKey: string) => () =>
|
||||
address &&
|
||||
|
@ -38,13 +38,13 @@ const PrintableWallet: React.SFC<Props> = ({ address, privateKey }) => {
|
|||
<PaperWallet address={address} privateKey={pkey} />
|
||||
<a
|
||||
role="button"
|
||||
aria-label={translateRaw('x_Print')}
|
||||
aria-label={translateRaw('X_PRINT')}
|
||||
aria-describedby="x_PrintDesc"
|
||||
className="btn btn-lg btn-primary btn-block"
|
||||
onClick={print(address, pkey)}
|
||||
style={{ margin: '10px auto 0', maxWidth: '260px' }}
|
||||
>
|
||||
{translateRaw('x_Print')}
|
||||
{translate('X_PRINT')}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -5,16 +5,18 @@ import { ConfirmationModal } from 'components/ConfirmationModal';
|
|||
|
||||
export const SendButton: React.SFC<{
|
||||
onlyTransactionParameters?: boolean;
|
||||
toggleDisabled?: boolean;
|
||||
customModal?: typeof ConfirmationModal;
|
||||
}> = ({ onlyTransactionParameters, customModal }) => (
|
||||
}> = ({ onlyTransactionParameters, toggleDisabled, customModal }) => (
|
||||
<SendButtonFactory
|
||||
onlyTransactionParameters={!!onlyTransactionParameters}
|
||||
toggleDisabled={toggleDisabled}
|
||||
Modal={customModal ? customModal : ConfirmationModal}
|
||||
withProps={({ onClick }) => (
|
||||
withProps={({ disabled, onClick }: { disabled: boolean; onClick(): void }) => (
|
||||
<div className="row form-group">
|
||||
<div className="col-xs-12">
|
||||
<button className="btn btn-primary btn-block" onClick={onClick}>
|
||||
{translate('SEND_trans')}
|
||||
<button disabled={disabled} className="btn btn-primary btn-block" onClick={onClick}>
|
||||
{translate('SEND_TRANS')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -20,7 +20,7 @@ export const OfflineBroadcast = connect((state: AppState) => ({ offline: getOffl
|
|||
const BroadCast: React.SFC<{}> = () => (
|
||||
<p>
|
||||
To broadcast this transaction, paste the above into the{' '}
|
||||
<Link to="pushTx">Broadcast Transaction tab</Link> or{' '}
|
||||
<Link to="/pushTx">Broadcast Transaction tab</Link> or{' '}
|
||||
<NewTabLink href="https://etherscan.io/pushTx">etherscan.io/pushTx</NewTabLink>
|
||||
</p>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,6 @@ import React, { Component } from 'react';
|
|||
import { getOffline } from 'selectors/config';
|
||||
import { AppState } from 'reducers';
|
||||
import { connect } from 'react-redux';
|
||||
import { CallbackProps } from '../SendButtonFactory';
|
||||
import { getCurrentTransactionStatus, currentTransactionBroadcasted } from 'selectors/transaction';
|
||||
import { showNotification, TShowNotification } from 'actions/notifications';
|
||||
import { ITransactionStatus } from 'reducers/transaction/broadcast';
|
||||
|
@ -26,7 +25,7 @@ interface DispatchProps {
|
|||
|
||||
interface OwnProps {
|
||||
Modal: typeof ConfirmationModal;
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
withOnClick(onClick: { onClick(): void }): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
const INITIAL_STATE: State = {
|
||||
|
@ -41,7 +40,7 @@ class OnlineSendClass extends Component<Props, State> {
|
|||
public render() {
|
||||
return !this.props.offline ? (
|
||||
<React.Fragment>
|
||||
{this.props.withProps({ onClick: this.openModal })}
|
||||
{this.props.withOnClick({ onClick: this.openModal })}
|
||||
<this.props.Modal isOpen={this.state.showModal} onClose={this.closeModal} />
|
||||
</React.Fragment>
|
||||
) : null;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import translate from 'translations';
|
||||
import { getTransactionFields, makeTransaction } from 'libs/transaction';
|
||||
import { OfflineBroadcast } from './OfflineBroadcast';
|
||||
import { SerializedTransaction } from 'components/renderCbs';
|
||||
import { OnlineSend } from './OnlineSend';
|
||||
import { addHexPrefix } from 'ethereumjs-util';
|
||||
import { getWalletType, IWalletType } from 'selectors/wallet';
|
||||
|
@ -10,60 +9,94 @@ import { connect } from 'react-redux';
|
|||
import { AppState } from 'reducers';
|
||||
import { ConfirmationModal } from 'components/ConfirmationModal';
|
||||
import { TextArea } from 'components/ui';
|
||||
import { getSerializedTransaction } from 'selectors/transaction';
|
||||
|
||||
export interface CallbackProps {
|
||||
disabled: boolean;
|
||||
onClick(): void;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
walletType: IWalletType;
|
||||
serializedTransaction: AppState['transaction']['sign']['local']['signedTransaction'];
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
onlyTransactionParameters?: boolean;
|
||||
toggleDisabled?: boolean;
|
||||
Modal: typeof ConfirmationModal;
|
||||
withProps(props: CallbackProps): React.ReactElement<any> | null;
|
||||
}
|
||||
|
||||
const getStringifiedTx = (serializedTransaction: string) =>
|
||||
const getStringifiedTx = (serializedTransaction: Buffer) =>
|
||||
JSON.stringify(getTransactionFields(makeTransaction(serializedTransaction)), null, 2);
|
||||
|
||||
type Props = StateProps & OwnProps;
|
||||
|
||||
class SendButtonFactoryClass extends Component<Props> {
|
||||
public render() {
|
||||
const { onlyTransactionParameters } = this.props;
|
||||
const {
|
||||
onlyTransactionParameters,
|
||||
serializedTransaction,
|
||||
toggleDisabled,
|
||||
walletType
|
||||
} = this.props;
|
||||
const columnSize = onlyTransactionParameters ? 12 : 6;
|
||||
|
||||
/* Left and right transaction comparision boxes, only displayed when a serialized transaction
|
||||
exists in state */
|
||||
|
||||
// shows the json representation of the transaction
|
||||
const leftTxCompare = serializedTransaction && (
|
||||
<div className={`col-sm-${columnSize}`}>
|
||||
<label>{walletType.isWeb3Wallet ? 'Transaction Parameters' : translate('SEND_RAW')}</label>
|
||||
<TextArea value={getStringifiedTx(serializedTransaction)} rows={4} readOnly={true} />
|
||||
</div>
|
||||
);
|
||||
|
||||
// shows the serialized representation of the transaction
|
||||
// "onlyTransactionParameters" used in broadcast tx so the same serialized tx isnt redundantly
|
||||
// displayed
|
||||
const rightTxCompare = serializedTransaction &&
|
||||
!onlyTransactionParameters && (
|
||||
<div className="col-sm-6">
|
||||
<label>
|
||||
{walletType.isWeb3Wallet
|
||||
? 'Serialized Transaction Parameters'
|
||||
: translate('SEND_SIGNED')}
|
||||
</label>
|
||||
<TextArea
|
||||
value={addHexPrefix(serializedTransaction.toString('hex'))}
|
||||
rows={4}
|
||||
readOnly={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
const shouldDisplayOnlineSend = toggleDisabled || serializedTransaction;
|
||||
|
||||
return (
|
||||
<SerializedTransaction
|
||||
withSerializedTransaction={serializedTransaction => (
|
||||
<React.Fragment>
|
||||
<div className={`col-sm-${columnSize}`}>
|
||||
<label>
|
||||
{this.props.walletType.isWeb3Wallet
|
||||
? 'Transaction Parameters'
|
||||
: translate('SEND_raw')}
|
||||
</label>
|
||||
<TextArea value={getStringifiedTx(serializedTransaction)} rows={4} readOnly={true} />
|
||||
</div>
|
||||
{!onlyTransactionParameters && (
|
||||
<div className="col-sm-6">
|
||||
<label>
|
||||
{this.props.walletType.isWeb3Wallet
|
||||
? 'Serialized Transaction Parameters'
|
||||
: translate('SEND_signed')}
|
||||
</label>
|
||||
<TextArea value={addHexPrefix(serializedTransaction)} rows={4} readOnly={true} />
|
||||
</div>
|
||||
)}
|
||||
<OfflineBroadcast />
|
||||
<OnlineSend withProps={this.props.withProps} Modal={this.props.Modal} />
|
||||
</React.Fragment>
|
||||
<>
|
||||
{leftTxCompare}
|
||||
{rightTxCompare}
|
||||
<OfflineBroadcast />
|
||||
{shouldDisplayOnlineSend && (
|
||||
<OnlineSend
|
||||
withOnClick={({ onClick }) =>
|
||||
this.props.withProps({
|
||||
disabled: !!(toggleDisabled && !serializedTransaction),
|
||||
onClick
|
||||
})
|
||||
}
|
||||
Modal={this.props.Modal}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const SendButtonFactory = connect((state: AppState) => ({
|
||||
walletType: getWalletType(state)
|
||||
walletType: getWalletType(state),
|
||||
serializedTransaction: getSerializedTransaction(state)
|
||||
}))(SendButtonFactoryClass);
|
||||
|
|
|
@ -27,7 +27,7 @@ class SendEverythingClass extends Component<Props> {
|
|||
!readOnly ? (
|
||||
<span className="help-block">
|
||||
<a onClick={this.onSendEverything}>
|
||||
<span className="">{translate('SEND_TransferTotal')}</span>
|
||||
<span>{translate('SEND_TRANSFERTOTAL')}</span>
|
||||
</a>
|
||||
</span>
|
||||
) : null
|
||||
|
|
|
@ -3,6 +3,7 @@ import { connect } from 'react-redux';
|
|||
import { AppState } from 'reducers';
|
||||
import { signaturePending } from 'selectors/transaction';
|
||||
import { Spinner } from 'components/ui';
|
||||
import translate from 'translations';
|
||||
interface StateProps {
|
||||
isSignaturePending: boolean;
|
||||
isHardwareWallet: boolean;
|
||||
|
@ -15,7 +16,7 @@ class SigningStatusClass extends Component<StateProps> {
|
|||
const HWWalletPrompt: React.SFC<{}> = _ =>
|
||||
isHardwareWallet ? (
|
||||
<p>
|
||||
<b>Confirm transaction on hardware wallet</b>
|
||||
<b>{translate('CONFIRM_HARDWARE_WALLET_TRANSACTION')}</b>
|
||||
</p>
|
||||
) : null;
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
margin-top: 15px;
|
||||
|
||||
&-tabs {
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
|
||||
&-link {
|
||||
display: inline-block;
|
||||
padding: 8px;
|
||||
|
@ -26,4 +29,8 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-select {
|
||||
margin-bottom: $space-md;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import Select, { Option } from 'react-select';
|
||||
import { NavLink, RouteComponentProps } from 'react-router-dom';
|
||||
import './SubTabs.scss';
|
||||
|
||||
|
@ -9,32 +10,135 @@ export interface Tab {
|
|||
redirect?: string;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
interface OwnProps {
|
||||
tabs: Tab[];
|
||||
match: RouteComponentProps<{}>['match'];
|
||||
}
|
||||
|
||||
export default class SubTabs extends React.PureComponent<Props> {
|
||||
type Props = OwnProps & RouteComponentProps<{}>;
|
||||
|
||||
interface State {
|
||||
tabsWidth: number;
|
||||
isCollapsed: boolean;
|
||||
}
|
||||
|
||||
export default class SubTabs extends React.PureComponent<Props, State> {
|
||||
public state: State = {
|
||||
tabsWidth: 0,
|
||||
isCollapsed: false
|
||||
};
|
||||
|
||||
private containerEl: HTMLDivElement | null;
|
||||
private tabsEl: HTMLDivElement | null;
|
||||
|
||||
public componentDidMount() {
|
||||
this.measureTabsWidth();
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
// When new tabs come in, we'll need to uncollapse so that they can
|
||||
// be measured and collapsed again, if needed.
|
||||
if (this.props.tabs !== nextProps.tabs) {
|
||||
this.setState({ isCollapsed: false });
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Props) {
|
||||
// New tabs === new measurements
|
||||
if (this.props.tabs !== prevProps.tabs) {
|
||||
this.measureTabsWidth();
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { tabs, match } = this.props;
|
||||
const currentPath = match.url;
|
||||
const { isCollapsed } = this.state;
|
||||
const basePath = match.url;
|
||||
const currentPath = location.pathname;
|
||||
let content: React.ReactElement<string>;
|
||||
|
||||
return (
|
||||
<div className="SubTabs row">
|
||||
<div className="SubTabs-tabs col-sm-12">
|
||||
if (isCollapsed) {
|
||||
const options = tabs.map(tab => ({
|
||||
label: tab.name as string,
|
||||
value: tab.path,
|
||||
disabled: tab.disabled
|
||||
}));
|
||||
|
||||
content = (
|
||||
<div className="SubTabs-select">
|
||||
<Select
|
||||
options={options}
|
||||
value={currentPath.split('/').pop()}
|
||||
onChange={this.handleSelect}
|
||||
searchable={false}
|
||||
clearable={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
// All tabs visible navigation
|
||||
content = (
|
||||
<div className="SubTabs-tabs" ref={el => (this.tabsEl = el)}>
|
||||
{tabs.map((t, i) => (
|
||||
// Same as normal Link, but knows when it's active, and applies activeClassName
|
||||
<NavLink
|
||||
className={`SubTabs-tabs-link ${t.disabled ? 'is-disabled' : ''}`}
|
||||
activeClassName="is-active"
|
||||
to={currentPath + '/' + t.path}
|
||||
key={i}
|
||||
>
|
||||
{t.name}
|
||||
</NavLink>
|
||||
<SubTabLink tab={t} basePath={basePath} className="SubTabs-tabs-link" key={i} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="SubTabs" ref={el => (this.containerEl = el)}>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private handleSelect = ({ value }: Option) => {
|
||||
this.props.history.push(`${this.props.match.url}/${value}`);
|
||||
};
|
||||
|
||||
// Tabs become a dropdown if they would wrap
|
||||
private handleResize = () => {
|
||||
if (!this.containerEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isCollapsed: this.state.tabsWidth >= this.containerEl.offsetWidth
|
||||
});
|
||||
};
|
||||
|
||||
// Store the tab width for future
|
||||
private measureTabsWidth = () => {
|
||||
if (this.tabsEl) {
|
||||
this.setState({ tabsWidth: this.tabsEl.offsetWidth }, () => {
|
||||
this.handleResize();
|
||||
});
|
||||
} else {
|
||||
// Briefly show, measure, collapse again still not enough room
|
||||
this.setState({ isCollapsed: false }, this.measureTabsWidth);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
interface SubTabLinkProps {
|
||||
tab: Tab;
|
||||
basePath: string;
|
||||
className: string;
|
||||
onClick?(ev: React.MouseEvent<HTMLAnchorElement>): void;
|
||||
}
|
||||
|
||||
const SubTabLink: React.SFC<SubTabLinkProps> = ({ tab, className, basePath, onClick }) => (
|
||||
<NavLink
|
||||
className={`${className} ${tab.disabled ? 'is-disabled' : ''}`}
|
||||
activeClassName="is-active"
|
||||
to={basePath + '/' + tab.path}
|
||||
onClick={onClick}
|
||||
>
|
||||
{tab.name}
|
||||
</NavLink>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import BN from 'bn.js';
|
||||
import { translateRaw } from 'translations';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
inputGasPrice,
|
||||
|
@ -10,7 +9,8 @@ import {
|
|||
getNonceRequested,
|
||||
TGetNonceRequested,
|
||||
reset,
|
||||
TReset
|
||||
TReset,
|
||||
ResetAction
|
||||
} from 'actions/transaction';
|
||||
import { fetchCCRatesRequested, TFetchCCRatesRequested } from 'actions/rates';
|
||||
import { getNetworkConfig, getOffline } from 'selectors/config';
|
||||
|
@ -21,6 +21,7 @@ import AdvancedGas, { AdvancedOptions } from './components/AdvancedGas';
|
|||
import './TXMetaDataPanel.scss';
|
||||
import { getGasPrice } from 'selectors/transaction';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import { translateRaw } from 'translations';
|
||||
|
||||
type SliderStates = 'simple' | 'advanced';
|
||||
|
||||
|
@ -44,6 +45,7 @@ interface DefaultProps {
|
|||
}
|
||||
|
||||
interface OwnProps {
|
||||
resetIncludeExcludeProperties?: ResetAction['payload'];
|
||||
initialState?: SliderStates;
|
||||
disableToggle?: boolean;
|
||||
advancedGasOptions?: AdvancedOptions;
|
||||
|
@ -69,7 +71,7 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
|||
|
||||
public componentDidMount() {
|
||||
if (!this.props.offline) {
|
||||
this.props.reset();
|
||||
this.props.reset(this.props.resetIncludeExcludeProperties);
|
||||
this.props.fetchCCRates([this.props.network.unit]);
|
||||
this.props.getNonceRequested();
|
||||
}
|
||||
|
@ -112,8 +114,8 @@ class TXMetaDataPanel extends React.Component<Props, State> {
|
|||
<div className="help-block">
|
||||
<a className="Gas-toggle" onClick={this.toggleAdvanced}>
|
||||
{showAdvanced
|
||||
? `- ${translateRaw('Back to simple')}`
|
||||
: `+ ${translateRaw('Advanced Settings')}`}
|
||||
? `- ${translateRaw('TRANS_SIMPLE')}`
|
||||
: `+ ${translateRaw('TRANS_ADVANCED')}`}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import { translateRaw } from 'translations';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import './AdvancedGas.scss';
|
||||
import { TToggleAutoGasLimit, toggleAutoGasLimit } from 'actions/config';
|
||||
|
@ -74,7 +74,7 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
<div className="input-group-wrapper AdvancedGas-gas-price">
|
||||
<label className="input-group">
|
||||
<div className="input-group-header">
|
||||
{translate('OFFLINE_Step2_Label_3')} (gwei)
|
||||
{translateRaw('OFFLINE_STEP2_LABEL_3')} (gwei)
|
||||
</div>
|
||||
<Input
|
||||
className={!!gasPrice.raw && !validGasPrice ? 'is-invalid' : ''}
|
||||
|
@ -89,7 +89,7 @@ class AdvancedGas extends React.Component<Props, State> {
|
|||
|
||||
{gasLimitField && (
|
||||
<div className="AdvancedGas-gas-limit">
|
||||
<GasLimitField customLabel={translateRaw('OFFLINE_Step2_Label_4')} />
|
||||
<GasLimitField customLabel={translateRaw('OFFLINE_STEP2_LABEL_4')} />
|
||||
</div>
|
||||
)}
|
||||
{nonceField && (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import Slider from 'rc-slider';
|
||||
import translate, { translateRaw } from 'translations';
|
||||
import Slider, { createSliderWithTooltip } from 'rc-slider';
|
||||
import translate from 'translations';
|
||||
import FeeSummary from './FeeSummary';
|
||||
import './SimpleGas.scss';
|
||||
import { AppState } from 'reducers';
|
||||
|
@ -16,12 +16,13 @@ import { getEstimates, getIsEstimating } from 'selectors/gas';
|
|||
import { Wei, fromWei } from 'libs/units';
|
||||
import { gasPriceDefaults } from 'config';
|
||||
import { InlineSpinner } from 'components/ui/InlineSpinner';
|
||||
const SliderWithTooltip = Slider.createSliderWithTooltip(Slider);
|
||||
import { TInputGasPrice } from 'actions/transaction';
|
||||
const SliderWithTooltip = createSliderWithTooltip(Slider);
|
||||
|
||||
interface OwnProps {
|
||||
gasPrice: AppState['transaction']['fields']['gasPrice'];
|
||||
inputGasPrice(rawGas: string);
|
||||
setGasPrice(rawGas: string);
|
||||
setGasPrice: TInputGasPrice;
|
||||
inputGasPrice(rawGas: string): void;
|
||||
}
|
||||
|
||||
interface StateProps {
|
||||
|
@ -39,14 +40,22 @@ interface ActionProps {
|
|||
|
||||
type Props = OwnProps & StateProps & ActionProps;
|
||||
|
||||
interface State {
|
||||
hasSetRecommendedGasPrice: boolean;
|
||||
}
|
||||
|
||||
class SimpleGas extends React.Component<Props> {
|
||||
public state: State = {
|
||||
hasSetRecommendedGasPrice: false
|
||||
};
|
||||
|
||||
public componentDidMount() {
|
||||
this.fixGasPrice();
|
||||
this.props.fetchGasEstimates();
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
if (!this.props.gasEstimates && nextProps.gasEstimates) {
|
||||
if (!this.state.hasSetRecommendedGasPrice && nextProps.gasEstimates) {
|
||||
this.setState({ hasSetRecommendedGasPrice: true });
|
||||
this.props.setGasPrice(nextProps.gasEstimates.fast.toString());
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +80,7 @@ class SimpleGas extends React.Component<Props> {
|
|||
<div className="SimpleGas row form-group">
|
||||
<div className="SimpleGas-title">
|
||||
<div className="flex-wrapper">
|
||||
<label>{translateRaw('Transaction Fee')} </label>
|
||||
<label>{translate('CONFIRM_TX_FEE')} </label>
|
||||
<div className="flex-spacer" />
|
||||
<InlineSpinner active={noncePending || gasLimitPending} text="Calculating" />
|
||||
</div>
|
||||
|
@ -99,8 +108,8 @@ class SimpleGas extends React.Component<Props> {
|
|||
disabled={isGasEstimating}
|
||||
/>
|
||||
<div className="SimpleGas-slider-labels">
|
||||
<span>{translate('Cheap')}</span>
|
||||
<span>{translate('Fast')}</span>
|
||||
<span>{translate('TX_FEE_SCALE_LEFT')}</span>
|
||||
<span>{translate('TX_FEE_SCALE_RIGHT')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<FeeSummary
|
||||
|
@ -120,21 +129,6 @@ class SimpleGas extends React.Component<Props> {
|
|||
this.props.inputGasPrice(gasGwei.toString());
|
||||
};
|
||||
|
||||
private fixGasPrice() {
|
||||
const { gasPrice, gasEstimates } = this.props;
|
||||
if (!gasEstimates) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the gas price is above or below our minimum, bring it in line
|
||||
const gasPriceGwei = this.getGasPriceGwei(gasPrice.value);
|
||||
if (gasPriceGwei < gasEstimates.safeLow) {
|
||||
this.props.setGasPrice(gasEstimates.safeLow.toString());
|
||||
} else if (gasPriceGwei > gasEstimates.fastest) {
|
||||
this.props.setGasPrice(gasEstimates.fastest.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private getGasPriceGwei(gasPriceValue: Wei) {
|
||||
return parseFloat(fromWei(gasPriceValue, 'gwei'));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ interface Props {
|
|||
toggleAriaLabel?: string;
|
||||
isValid?: boolean;
|
||||
isVisible?: boolean;
|
||||
validity?: 'valid' | 'invalid' | 'semivalid';
|
||||
readOnly?: boolean;
|
||||
|
||||
// Textarea-only props
|
||||
isTextareaWhenVisible?: boolean;
|
||||
|
@ -55,24 +55,21 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
|
|||
disabled,
|
||||
ariaLabel,
|
||||
toggleAriaLabel,
|
||||
validity,
|
||||
isTextareaWhenVisible,
|
||||
isValid,
|
||||
onChange,
|
||||
onFocus,
|
||||
onBlur,
|
||||
handleToggleVisibility
|
||||
handleToggleVisibility,
|
||||
readOnly
|
||||
} = this.props;
|
||||
const { isVisible } = this.state;
|
||||
const validClass = validity
|
||||
? `is-${validity}`
|
||||
: isValid === null || isValid === undefined ? '' : isValid ? 'is-valid' : 'is-invalid';
|
||||
|
||||
return (
|
||||
<div className={`TogglablePassword input-group input-group-inline-dropdown ${className}`}>
|
||||
<div className={`TogglablePassword input-group input-group-inline`}>
|
||||
{isTextareaWhenVisible && isVisible ? (
|
||||
<TextArea
|
||||
className={validClass}
|
||||
className={`${className} ${!isValid ? 'invalid' : ''}`}
|
||||
value={value}
|
||||
name={name}
|
||||
disabled={disabled}
|
||||
|
@ -83,6 +80,7 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
|
|||
placeholder={placeholder}
|
||||
rows={this.props.rows || 3}
|
||||
aria-label={ariaLabel}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
|
@ -90,12 +88,13 @@ export default class TogglablePassword extends React.PureComponent<Props, State>
|
|||
name={name}
|
||||
disabled={disabled}
|
||||
type={isVisible ? 'text' : 'password'}
|
||||
className={`${validClass}`}
|
||||
className={`${className} ${!isValid ? 'invalid' : ''} border-rad-right-0`}
|
||||
placeholder={placeholder}
|
||||
onChange={onChange}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
aria-label={ariaLabel}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
)}
|
||||
<span
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import translate from 'translations';
|
||||
import { Identicon, UnitDisplay, NewTabLink, TextArea, Address } from 'components/ui';
|
||||
import { TransactionData, TransactionReceipt } from 'libs/nodes';
|
||||
import { TransactionData, TransactionReceipt } from 'types/transactions';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import './TransactionDataTable.scss';
|
||||
|
||||
|
@ -71,7 +71,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
|
|||
|
||||
const rows: TableRow[] = [
|
||||
{
|
||||
label: 'Status',
|
||||
label: translate('TX_STATUS'),
|
||||
data: (
|
||||
<React.Fragment>
|
||||
<strong className={`TxData-row-data-status is-${statusType}`}>{statusMsg}</strong>
|
||||
|
@ -86,15 +86,15 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
|
|||
)
|
||||
},
|
||||
{
|
||||
label: translate('x_TxHash'),
|
||||
label: translate('X_TXHASH'),
|
||||
data: <MaybeLink href={explorer.tx}>{data.hash}</MaybeLink>
|
||||
},
|
||||
{
|
||||
label: 'Block Number',
|
||||
label: translate('TX_BLOCK_NUMB'),
|
||||
data: receipt && <MaybeLink href={explorer.block}>{receipt.blockNumber}</MaybeLink>
|
||||
},
|
||||
{
|
||||
label: translate('OFFLINE_Step1_Label_1'),
|
||||
label: translate('OFFLINE_STEP1_LABEL_1'),
|
||||
data: (
|
||||
<MaybeLink href={explorer.from}>
|
||||
<Identicon address={data.from} size="26px" />
|
||||
|
@ -103,7 +103,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
|
|||
)
|
||||
},
|
||||
{
|
||||
label: translate('OFFLINE_Step2_Label_1'),
|
||||
label: translate('OFFLINE_STEP2_LABEL_1'),
|
||||
data: (
|
||||
<MaybeLink href={explorer.to}>
|
||||
<Identicon address={data.to} size="26px" />
|
||||
|
@ -112,23 +112,23 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
|
|||
)
|
||||
},
|
||||
{
|
||||
label: translate('SEND_amount_short'),
|
||||
label: translate('SEND_AMOUNT_SHORT'),
|
||||
data: <UnitDisplay value={data.value} unit="ether" symbol={network.unit} />
|
||||
},
|
||||
{
|
||||
label: translate('OFFLINE_Step2_Label_3'),
|
||||
label: translate('OFFLINE_STEP2_LABEL_3'),
|
||||
data: <UnitDisplay value={data.gasPrice} unit="gwei" symbol="Gwei" />
|
||||
},
|
||||
{
|
||||
label: translate('OFFLINE_Step2_Label_4'),
|
||||
label: translate('OFFLINE_STEP2_LABEL_4'),
|
||||
data: <UnitDisplay value={data.gas} unit="wei" />
|
||||
},
|
||||
{
|
||||
label: 'Gas Used',
|
||||
label: translate('TX_GAS_USED'),
|
||||
data: receipt && <UnitDisplay value={receipt.gasUsed} unit="wei" />
|
||||
},
|
||||
{
|
||||
label: 'Transaction Fee',
|
||||
label: translate('CONFIRM_TX_FEE'),
|
||||
data: receipt && (
|
||||
<UnitDisplay
|
||||
value={receipt.gasUsed.mul(data.gasPrice)}
|
||||
|
@ -138,7 +138,7 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
|
|||
)
|
||||
},
|
||||
{
|
||||
label: translate('New contract address'),
|
||||
label: translate('NEW_CONTRACT_ADDR'),
|
||||
data: receipt &&
|
||||
receipt.contractAddress && (
|
||||
<MaybeLink href={explorer.contract}>
|
||||
|
@ -147,11 +147,11 @@ const TransactionDataTable: React.SFC<Props> = ({ data, receipt, network }) => {
|
|||
)
|
||||
},
|
||||
{
|
||||
label: translate('OFFLINE_Step2_Label_5'),
|
||||
label: translate('OFFLINE_STEP2_LABEL_5'),
|
||||
data: data.nonce
|
||||
},
|
||||
{
|
||||
label: translate('TRANS_data'),
|
||||
label: translate('TRANS_DATA'),
|
||||
data: hasInputData ? <TextArea value={data.input} disabled={true} /> : null
|
||||
}
|
||||
];
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
&-data {
|
||||
text-align: left;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
&-loading {
|
||||
|
|
|
@ -8,7 +8,7 @@ import { Spinner } from 'components/ui';
|
|||
import TransactionDataTable from './TransactionDataTable';
|
||||
import { AppState } from 'reducers';
|
||||
import { NetworkConfig } from 'types/network';
|
||||
import { TransactionState } from 'reducers/transactions';
|
||||
import { TransactionState } from 'types/transactions';
|
||||
import './TransactionStatus.scss';
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -44,7 +44,6 @@ class TransactionStatus extends React.Component<Props> {
|
|||
if (tx && tx.data) {
|
||||
content = (
|
||||
<React.Fragment>
|
||||
<h2 className="TxStatus-title">Transaction Found</h2>
|
||||
<div className="TxStatus-data">
|
||||
<TransactionDataTable network={network} data={tx.data} receipt={tx.receipt} />
|
||||
</div>
|
||||
|
@ -53,13 +52,13 @@ class TransactionStatus extends React.Component<Props> {
|
|||
} else if (tx && tx.error) {
|
||||
content = (
|
||||
<div className="TxStatus-error">
|
||||
<h2 className="TxStatus-error-title">{translate('tx_notFound')}</h2>
|
||||
<p className="TxStatus-error-desc">{translate('tx_notFound_1')}</p>
|
||||
<h2 className="TxStatus-error-title">{translate('TX_NOTFOUND')}</h2>
|
||||
<p className="TxStatus-error-desc">{translate('TX_NOTFOUND_1')}</p>
|
||||
<ul className="TxStatus-error-list">
|
||||
<li>Make sure you copied the Transaction Hash correctly</li>
|
||||
<li>{translate('tx_notFound_2')}</li>
|
||||
<li>{translate('tx_notFound_3')}</li>
|
||||
<li>{translate('tx_notFound_4')}</li>
|
||||
<li>{translate('TX_NOTFOUND_5')}</li>
|
||||
<li>{translate('TX_NOTFOUND_2')}</li>
|
||||
<li>{translate('TX_NOTFOUND_3')}</li>
|
||||
<li>{translate('TX_NOTFOUND_4')}</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,22 +1,20 @@
|
|||
import React from 'react';
|
||||
import Markdown from 'react-markdown';
|
||||
import { translateRaw } from 'translations';
|
||||
|
||||
interface Props {
|
||||
translationKey: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
const Translate = ({ translationKey }: Props) => {
|
||||
const source = translateRaw(translationKey);
|
||||
const TranslateMarkdown = ({ source }: Props) => {
|
||||
return (
|
||||
<Markdown
|
||||
escapeHtml={true}
|
||||
unwrapDisallowed={true}
|
||||
allowedTypes={['link', 'emphasis', 'strong', 'code', 'root', 'inlineCode']}
|
||||
renderers={{ root: 'span' }}
|
||||
renderers={{ root: React.Fragment }}
|
||||
source={source}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default Translate;
|
||||
export default TranslateMarkdown;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue