diff --git a/Dockerfile b/Dockerfile index 155841287..1a7edb62e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,6 +84,7 @@ COPY . /counterwallet RUN rm -rf /counterwallet/build WORKDIR /counterwallet RUN git rev-parse HEAD + RUN cd src; bower --allow-root --config.interactive=false update; cd .. RUN cd src/vendors/bitcoinjs-lib; npm install; browserify --standalone bitcoinjs src/index.js | uglifyjs -c --mangle reserved=['BigInteger','ECPair','Point'] -o bitcoinjs.min.js; cd ../../../ RUN npm install diff --git a/src/css/misc.css b/src/css/misc.css index b33bba490..c029b5e20 100644 --- a/src/css/misc.css +++ b/src/css/misc.css @@ -137,7 +137,7 @@ textarea.quickAccessUrl { padding: 5px 15px; } -#langSelector a img { +#langSelector a img { filter : alpha(opacity=50); opacity : 0.5; } diff --git a/src/js/components/balances.js b/src/js/components/balances.js index 6c7c5d388..3033c284a 100644 --- a/src/js/components/balances.js +++ b/src/js/components/balances.js @@ -420,6 +420,24 @@ function SendModalViewModel() { self.assetDisp = ko.observable(); self.rawBalance = ko.observable(null); self.divisible = ko.observable(); + self.feeOption = ko.observable('optimal'); + self.customFee = ko.observable(null).extend({ + validation: [{ + validator: function(val, self) { + return self.feeOption() === 'custom' ? val : true; + }, + message: i18n.t('field_required'), + params: self + }], + isValidCustomFeeIfSpecified: self + }); + + self.feeOption.subscribeChanged(function(newValue, prevValue) { + if(newValue !== 'custom') { + self.customFee(null); + self.customFee.isModified(false); + } + }); self.destAddress = ko.observable('').extend({ required: true, @@ -627,6 +645,7 @@ function SendModalViewModel() { self.validationModel = ko.validatedObservable({ destAddress: self.destAddress, quantity: self.quantity, + customFee: self.customFee, pubkey1: self.pubkey1, pubkey2: self.pubkey2, pubkey3: self.pubkey3, @@ -730,6 +749,29 @@ function SendModalViewModel() { self.show = function(fromAddress, asset, assetDisp, rawBalance, isDivisible, resetForm) { if (asset === KEY_ASSET.BTC && rawBalance === null) { + /*WALLET.doTransaction(self.address(), "create_send", + { + source: self.address(), + destination: self.destAddress(), + quantity: denormalizeQuantity(parseFloat(self.quantity()), self.divisible()), + asset: self.asset(), + _asset_divisible: self.divisible(), + _pubkeys: additionalPubkeys.concat(self._additionalPubkeys), + _fee_option: self.feeOption(), + _custom_fee: self.customFee() + }, + function(txHash, data, endpoint, addressType, armoryUTx) { + var message = "" + (armoryUTx ? i18n.t("will_be_sent") : i18n.t("were_sent")) + " "; + WALLET.showTransactionCompleteDialog(message + " " + i18n.t(ACTION_PENDING_NOTICE), message, armoryUTx); + } + ); + self.shown(false); + trackEvent('Balances', 'Send', self.asset()); + } + + self.show = function(fromAddress, asset, assetDisp, rawBalance, isDivisible, resetForm) { + if (asset == 'BTC' && rawBalance == null) {*/ + return bootbox.alert(i18n.t("cannot_send_server_unavailable")); } assert(rawBalance, "Balance is null or undefined?"); @@ -741,6 +783,7 @@ function SendModalViewModel() { self.assetDisp(assetDisp); self.rawBalance(rawBalance); self.divisible(isDivisible); + $('#sendFeeOption').select2("val", self.feeOption()); //hack self.shown(true); $('#MemoType').select2("val", self.memoType()); // hack to set select2 value diff --git a/src/js/components/balances_assets.js b/src/js/components/balances_assets.js index 452239780..867d07ab6 100644 --- a/src/js/components/balances_assets.js +++ b/src/js/components/balances_assets.js @@ -61,6 +61,49 @@ function CreateAssetModalViewModel() { isValidPositiveQuantityOrZero: self, isValidQtyForDivisibility: self }); + self.feeOption = ko.observable('optimal'); + self.customFee = ko.observable(null).extend({ + validation: [{ + validator: function(val, self) { + return self.feeOption() === 'custom' ? val : true; + }, + message: i18n.t('field_required'), + params: self + }], + isValidCustomFeeIfSpecified: self + }); + + self.hasXCPForNamedAsset = ko.computed(function() { + return self.xcpBalance() >= ASSET_CREATION_FEE_XCP; + }); + self.hasXCPForSubAsset = ko.computed(function() { + return self.xcpBalance() >= SUBASSET_CREATION_FEE_XCP; + }); + + self.ownedNamedAssets = ko.computed(function() { //stores BuySellAddressInDropdownItemModel objects + if (!self.address()) return []; + var ownedAssets = []; + //Get a list of all of my available assets this address owns + var assets = WALLET.getAddressObj(self.address()).assets(); + for (var i = 0; i < assets.length; i++) { + if(assets[i].isMine() && assets[i].assetType() === 'named') { + ownedAssets.push(new ParentAssetInDropdownItemModel(assets[i].ASSET)); + } + } + + ownedAssets.sort(function(left, right) { + return left.ASSET == right.ASSET ? 0 : (left.ASSET > right.ASSET ? -1 : 1); + }); + + return ownedAssets; + }, self); + + self.feeOption.subscribeChanged(function(newValue, prevValue) { + if(newValue !== 'custom') { + self.customFee(null); + self.customFee.isModified(false); + } + }); self.hasXCPForNamedAsset = ko.computed(function() { return self.xcpBalance() >= ASSET_CREATION_FEE_XCP; @@ -91,6 +134,7 @@ function CreateAssetModalViewModel() { name: self.name, description: self.description, quantity: self.quantity, + customFee: self.customFee }); self.generateRandomId = function() { @@ -103,6 +147,8 @@ function CreateAssetModalViewModel() { self.description(''); self.divisible(true); self.quantity(null); + self.feeOption('optimal'); + self.customFee(null); self.validationModel.errors.showAllMessages(false); self.feeController.reset(); } @@ -126,6 +172,32 @@ function CreateAssetModalViewModel() { self.doAction = function() { WALLET.doTransactionWithTxHex(self.address(), "create_issuance", self.buildCreateAssetTransactionData(), self.feeController.getUnsignedTx(), + +/* // this was on a conflict merge + var quantity = parseFloat(self.quantity()); + var rawQuantity = denormalizeQuantity(quantity, self.divisible()); + + if (rawQuantity > MAX_INT) { + bootbox.alert(i18n.t("issuance_quantity_too_high")); + return false; + } + + var name = self.name(); + if(self.tokenNameType() === 'subasset' && self.selectedParentAsset()) { + name = self.selectedParentAsset() + '.' + self.name(); + } + WALLET.doTransaction(self.address(), "create_issuance", + { + source: self.address(), + asset: name, + quantity: rawQuantity, + divisible: self.divisible(), + description: self.description(), + transfer_destination: null, + _fee_option: self.feeOption(), + _custom_fee: self.customFee() + },*/ + function(txHash, data, endpoint, addressType, armoryUTx) { var message = ""; var name = data.asset; @@ -191,6 +263,7 @@ function CreateAssetModalViewModel() { self.address(address); self.tokenNameType('numeric'); self.generateRandomId(); + $('#createAssetFeeOption').select2("val", self.feeOption()); //hack self.shown(true); trackDialogShow('CreateAsset'); } diff --git a/src/js/components/exchange.js b/src/js/components/exchange.js index 05ebc75ac..b1a3c18d0 100644 --- a/src/js/components/exchange.js +++ b/src/js/components/exchange.js @@ -198,9 +198,16 @@ function ExchangeViewModel() { assert(self.asset2Raw().includes('.')); self.asset2Longname(self.asset2Raw()); } + + self.buyFeeOption('optimal'); + self.buyCustomFee(null); + self.sellFeeOption('optimal'); + self.sellCustomFee(null); + $('#buyFeeOption').select2("val", self.buyFeeOption()); //hack + $('#sellFeeOption').select2("val", self.sellFeeOption()); //hack }); - //VALIDATION MODELS + //VALIDATION MODELS self.validationModelBaseOrders = ko.validatedObservable({ asset1Raw: self.asset1Raw, asset2Raw: self.asset2Raw @@ -244,6 +251,17 @@ function ExchangeViewModel() { isValidPositiveQuantity: self, quoteDivisibilityIsOk: self }); + self.sellFeeOption = ko.observable('optimal'); + self.sellCustomFee = ko.observable(null).extend({ + validation: [{ + validator: function(val, self) { + return self.sellFeeOption() === 'custom' ? val : true; + }, + message: i18n.t('field_required'), + params: self + }], + isValidCustomFeeIfSpecified: self + }); self.sellPriceHasFocus = ko.observable(); self.sellAmountHasFocus = ko.observable(); self.sellTotalHasFocus = ko.observable(); @@ -251,6 +269,13 @@ function ExchangeViewModel() { self.selectedAddressForSell = ko.observable(); self.availableBalanceForSell = ko.observable(); + self.sellFeeOption.subscribeChanged(function(newValue, prevValue) { + if(newValue !== 'custom') { + self.sellCustomFee(null); + self.sellCustomFee.isModified(false); + } + }); + self.availableAddressesForSell = ko.computed(function() { //stores BuySellAddressInDropdownItemModel objects if (!self.baseAsset()) return null; //must have a sell asset selected //Get a list of all of my available addresses with the specified sell asset balance @@ -328,6 +353,7 @@ function ExchangeViewModel() { sellAmount: self.sellAmount, sellPrice: self.sellPrice, sellTotal: self.sellTotal, + sellCustomFee: self.sellCustomFee }); @@ -412,6 +438,8 @@ function ExchangeViewModel() { expiration: expiration, _fee_option: 'custom', _custom_fee: self.sellFeeController.getCustomFee() +/* _fee_option: self.sellFeeOption(), + _custom_fee: self.sellCustomFee()*/ } } @@ -428,6 +456,17 @@ function ExchangeViewModel() { buildTransactionData: self.buildSellTransactionData, address: self.selectedAddressForSell }); +/* + var onSuccess = function(txHash, data, endpoint, addressType, armoryUTx) { + trackEvent('Exchange', 'Sell', self.dispAssetPair()); + + var message = ""; + if (armoryUTx) { + message = i18n.t("you_sell_order_will_be_placed", self.sellAmount(), self.dispBaseAsset()); + } else { + message = i18n.t("you_sell_order_has_been_placed", self.sellAmount(), self.dispBaseAsset()); + } +*/ @@ -513,6 +552,18 @@ function ExchangeViewModel() { isValidPositiveQuantity: self, quoteDivisibilityIsOk: self }); + self.buyFeeOption = ko.observable('optimal'); + self.buyCustomFee = ko.observable(null).extend({ + validation: [{ + validator: function(val, self) { + return self.buyFeeOption() === 'custom' ? val : true; + }, + message: i18n.t('field_required'), + params: self + }], + isValidCustomFeeIfSpecified: self + }); + self.buyPriceHasFocus = ko.observable(); self.buyAmountHasFocus = ko.observable(); self.buyTotalHasFocus = ko.observable(); @@ -520,6 +571,13 @@ function ExchangeViewModel() { self.selectedAddressForBuy = ko.observable(); self.availableBalanceForBuy = ko.observable(); + self.buyFeeOption.subscribeChanged(function(newValue, prevValue) { + if(newValue !== 'custom') { + self.buyCustomFee(null); + self.buyCustomFee.isModified(false); + } + }); + self.availableAddressesForBuy = ko.computed(function() { //stores BuySellAddressInDropdownItemModel objects if (!self.quoteAsset()) return null; //must have a sell asset selected //Get a list of all of my available addresses with the specified sell asset balance @@ -601,6 +659,7 @@ function ExchangeViewModel() { buyTotal: self.buyTotal, buyPrice: self.buyPrice, buyAmount: self.buyAmount, + buyCustomFee: self.buyCustomFee }); self.selectSellOrder = function(order, notFromClick) { @@ -774,13 +833,13 @@ function ExchangeViewModel() { self.displayTopUserPairs = function(data) { for (var p in data) { var classes = ['top_user_pair']; - + if (data[p]['trend'] > 0) { classes.push('txt-color-greenDark'); } else if (data[p]['trend'] < 0) { classes.push('txt-color-red'); } - + if (parseFloat(data[p]['progression']) > 0) { classes.push('progression-up'); } else if (parseFloat(data[p]['progression']) < 0) { @@ -790,7 +849,7 @@ function ExchangeViewModel() { if (data[p]['my_order_count']) { classes.push('with-open-order'); } - + classes.push("pair_" + data[p]['base_asset'] + data[p]['quote_asset']); data[p]['pair_classes'] = classes.join(' '); } @@ -1074,7 +1133,7 @@ function ExchangeViewModel() { self.asset1Raw(data['asset_longname'] || data['asset']); //gotta do a manual update...doesn't play well with knockout } else if ($($e.target).attr('name') == 'asset2Raw') { self.asset2Raw(data['asset_longname'] || data['asset']); //gotta do a manual update...doesn't play well with knockout - } + } }); }); } @@ -1354,7 +1413,7 @@ function OpenOrdersViewModel() { return order.get_asset == item['asset']; //matches asset name or asset longname }); order.get_asset_disp = match['asset_longname'] || match['asset']; - + order.give_quantity = data[i].give_quantity; order.get_quantity = data[i].get_quantity; order.give_remaining = Math.max(data[i].give_remaining, 0); @@ -1502,7 +1561,7 @@ function OrderMatchesViewModel() { return order_match.get_asset == item['asset']; //matches asset name or asset longname }); order_match.get_asset_disp = match['asset_longname'] || match['asset']; - + order_match.status = data[i].status; order_match.block_index = data[i].block_index; diff --git a/src/js/components/feed_pending_actions.js b/src/js/components/feed_pending_actions.js index 300da6192..2bb84f5a7 100644 --- a/src/js/components/feed_pending_actions.js +++ b/src/js/components/feed_pending_actions.js @@ -22,7 +22,7 @@ PendingActionViewModel.calcText = function(category, data) { // resolve asset longname // asset_longname = data['asset_longname'] !== undefined ? data['asset_longname'] : (data['_asset_longname'] !== undefined ? data['_asset_longname'] : WALLET.getAddressObj(data['source']).getAssetObj(data['asset']).ASSET_LONGNAME); if (data['asset_longname'] !== undefined) { - asset_longname = data['asset_longname']; + asset_longname = data['asset_longname']; } else { if (data['_asset_longname'] !== undefined) { asset_longname = data['_asset_longname']; diff --git a/src/pages/balances.html b/src/pages/balances.html index 0eb9a6544..3206ab078 100644 --- a/src/pages/balances.html +++ b/src/pages/balances.html @@ -383,6 +383,7 @@
+ @@ -673,8 +696,8 @@