diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 01fbc666f8..5f1e680eee 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -40,6 +40,7 @@ import { } from '../../common/utils/storageManager'; import { isProduction } from '../../common/utils/tools'; import GTM from '../../common/gtm'; +import { saveBeforeUnload } from './blockly/utils'; let realityCheckTimeout; @@ -92,14 +93,6 @@ const chart = new Chart(api); const tradingView = new TradingView(); -const setBeforeUnload = off => { - if (off) { - window.onbeforeunload = null; - } else { - window.onbeforeunload = () => 'You have some unsaved blocks, do you want to save them before you exit?'; - } -}; - const showRealityCheck = () => { $('.blocker').show(); $('.reality-check').show(); @@ -474,7 +467,7 @@ export default class View { $('#toggleHeaderButton').click(() => this.showHeader($('#header').is(':hidden'))); $('#logout, #toolbox-logout').click(() => { - setBeforeUnload(true); + saveBeforeUnload(); logout(); hideRealityCheck(); }); @@ -612,7 +605,7 @@ export default class View { $('#login, #toolbox-login') .bind('click.login', () => { - setBeforeUnload(true); + saveBeforeUnload(); document.location = getOAuthURL(); }) .text(translate('Log in')); diff --git a/src/botPage/view/blockly/blocks/shared.js b/src/botPage/view/blockly/blocks/shared.js index 7e5ed841fe..c7f61cb17d 100644 --- a/src/botPage/view/blockly/blocks/shared.js +++ b/src/botPage/view/blockly/blocks/shared.js @@ -16,6 +16,10 @@ let purchaseChoices = [[translate('Click to select'), '']]; export const getPurchaseChoices = () => purchaseChoices; const filterPurchaseChoices = (contractType, oppositesName) => { + if (oppositesName.toLowerCase() === 'na') { + return [[translate('Not available'), 'na']]; + } + const { [oppositesName]: tradeTypes } = config.opposites; let tmpPurchaseChoices = tradeTypes.filter(k => @@ -86,46 +90,79 @@ const getActiveMarket = markets => fieldGeneratorMapping.MARKET_LIST = () => { const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets()); + + if (Object.keys(markets).length === 0) { + return [[translate('Not available'), 'na']]; + } return Object.keys(markets).map(e => [markets[e].name, e]); }; fieldGeneratorMapping.SUBMARKET_LIST = block => () => { const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets()); const marketName = block.getFieldValue('MARKET_LIST'); - if (!marketName || marketName === 'Invalid') { - return [['', 'Invalid']]; + const submarketOptions = []; + + if (Object.keys(markets).length > 0 && marketName !== 'na') { + const marketObj = markets[marketName]; + + if (marketObj) { + const submarkets = getActiveSubMarket(marketObj.submarkets); + + submarketOptions.push( + ...Object.keys(submarkets) + .map(e => [submarkets[e].name, e]) + // Filter out markets we don't have contracts for + .filter(submarket => !['energy'].includes(submarket[1])) + ); + } } - const submarkets = getActiveSubMarket(markets[marketName].submarkets); - return ( - Object.keys(submarkets) - .map(e => [submarkets[e].name, e]) - // Filter out markets we don't have contracts for - .filter(submarket => !['energy'].includes(submarket[1])) - ); + + if (submarketOptions.length === 0) { + return [[translate('Not available'), 'na']]; + } + + return submarketOptions; }; fieldGeneratorMapping.SYMBOL_LIST = block => () => { const markets = getActiveMarket(symbolApi.activeSymbols.getMarkets()); const submarketName = block.getFieldValue('SUBMARKET_LIST'); - if (!submarketName || submarketName === 'Invalid') { - return [['', '']]; + const symbolOptions = []; + + if (Object.keys(markets).length > 0 && submarketName !== 'na') { + const marketName = block.getFieldValue('MARKET_LIST'); + const marketObj = markets[marketName]; + + if (marketObj) { + const { submarkets } = marketObj; + + if (Object.keys(submarkets).length > 0 && submarkets[submarketName]) { + const symbols = getActiveSymbols(submarkets[submarketName].symbols); + + symbolOptions.push( + ...Object.keys(symbols) + .map(e => [symbols[e].display, symbols[e].symbol]) + // Filter out symbols we don't have contracts for (these symbols have only forward-starting) + .filter(symbol => !['frxGBPNOK', 'frxUSDNOK', 'frxUSDNEK', 'frxUSDSEK'].includes(symbol[1])) + ); + } + } } - const marketName = block.getFieldValue('MARKET_LIST'); - const submarkets = getActiveSubMarket(markets[marketName].submarkets); - const symbols = getActiveSymbols(submarkets[submarketName].symbols); - return ( - Object.keys(symbols) - .map(e => [symbols[e].display, symbols[e].symbol]) - // Filter out symbols we don't have contracts for (these symbols have only forward-starting) - .filter(symbol => !['frxGBPNOK', 'frxUSDNOK', 'frxUSDNEK', 'frxUSDSEK'].includes(symbol[1])) - ); + + if (symbolOptions.length === 0) { + return [[translate('Not available'), 'na']]; + } + + return symbolOptions; }; fieldGeneratorMapping.TRADETYPECAT_LIST = block => () => { const symbol = block.getFieldValue('SYMBOL_LIST'); - if (!symbol) { - return [['', '']]; + + if (!symbol || symbol === 'na') { + return [[translate('Not available'), 'na']]; } + const allowedCategories = symbolApi.getAllowedCategories(symbol.toLowerCase()); return Object.keys(config.conditionsCategoryName) .filter(e => allowedCategories.indexOf(e) >= 0) @@ -134,9 +171,11 @@ fieldGeneratorMapping.TRADETYPECAT_LIST = block => () => { fieldGeneratorMapping.TRADETYPE_LIST = block => () => { const tradeTypeCat = block.getFieldValue('TRADETYPECAT_LIST'); - if (!tradeTypeCat) { - return [['', '']]; + + if (!tradeTypeCat || tradeTypeCat === 'na') { + return [[translate('Not available'), 'na']]; } + return ( config.conditionsCategory[tradeTypeCat] .map(e => [config.opposites[e.toUpperCase()].map(c => c[Object.keys(c)[0]]).join('/'), e]) diff --git a/src/botPage/view/blockly/blocks/trade/components.js b/src/botPage/view/blockly/blocks/trade/components.js index a99f248f9b..8c1deefb66 100644 --- a/src/botPage/view/blockly/blocks/trade/components.js +++ b/src/botPage/view/blockly/blocks/trade/components.js @@ -8,7 +8,7 @@ export const marketDropdown = block => { block .appendDummyInput('MARKETDEFINITION') .appendField(`${translate('Market')}:`) - .appendField(new Blockly.FieldDropdown(fieldGeneratorMapping.MARKET_LIST), 'MARKET_LIST') + .appendField(new Blockly.FieldDropdown(fieldGeneratorMapping.MARKET_LIST()), 'MARKET_LIST') .appendField('>') .appendField(new Blockly.FieldDropdown(fieldGeneratorMapping.SUBMARKET_LIST(block)), 'SUBMARKET_LIST') .appendField('>') @@ -28,10 +28,10 @@ export const contractTypes = block => { if (!block.getInput('CONTRACT_TYPE')) { const getContractTypes = () => { const tradeType = block.getFieldValue('TRADETYPE_LIST'); - if (tradeType) { + if (tradeType && tradeType !== 'na') { return [[translate('Both'), 'both'], ...oppositesToDropdown(config.opposites[tradeType.toUpperCase()])]; } - return [['', '']]; + return [[translate('Not available'), 'na']]; }; block .appendDummyInput('CONTRACT_TYPE') diff --git a/src/botPage/view/blockly/blocks/trade/index.js b/src/botPage/view/blockly/blocks/trade/index.js index 77dc0f9139..0327010694 100644 --- a/src/botPage/view/blockly/blocks/trade/index.js +++ b/src/botPage/view/blockly/blocks/trade/index.js @@ -107,6 +107,12 @@ Blockly.Blocks.trade = { resetTradeFields(this, ev); } + if (ev.type === Blockly.Events.BLOCK_CREATE && ev.group !== 'load') { + const marketField = this.getField('MARKET_LIST'); + marketField.setValue(''); + marketField.setValue(marketField.menuGenerator_[0][1]); // eslint-disable-line + } + decorateTrade(ev); }, }; diff --git a/src/botPage/view/blockly/blocks/trade/tools.js b/src/botPage/view/blockly/blocks/trade/tools.js index ad62dbdf0b..96439a86c7 100644 --- a/src/botPage/view/blockly/blocks/trade/tools.js +++ b/src/botPage/view/blockly/blocks/trade/tools.js @@ -9,7 +9,7 @@ export const getParentValue = (block, fieldName) => { export const updateInputList = block => { const tradeType = getParentValue(block, 'TRADETYPE_LIST'); - if (tradeType) { + if (Blockly.Blocks[tradeType]) { Blockly.Blocks[tradeType].init.call(block); } }; diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index 9bf3a102b3..b5f1a80f47 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -14,6 +14,9 @@ import { removeUnavailableMarkets, strategyHasValidTradeTypeCategory, cleanBeforeExport, + importFile, + saveBeforeUnload, + removeParam, } from './utils'; import Interpreter from '../../bot/Interpreter'; import { createErrorAndEmit } from '../../common/error'; @@ -24,14 +27,6 @@ import { showDialog } from '../../bot/tools'; import GTM from '../../../common/gtm'; import { parseQueryString } from '../../../common/utils/tools'; -const setBeforeUnload = off => { - if (off) { - window.onbeforeunload = null; - } else { - window.onbeforeunload = () => 'You have some unsaved blocks, do you want to save them before you exit?'; - } -}; - const disableStrayBlocks = () => { const topBlocks = Blockly.mainWorkspace.getTopBlocks(); topBlocks.forEach(block => { @@ -48,7 +43,7 @@ const disableStrayBlocks = () => { }; const disposeBlocksWithLoaders = () => { Blockly.mainWorkspace.addChangeListener(ev => { - setBeforeUnload(); + saveBeforeUnload(); if (ev.type === 'delete' && ev.oldXml.getAttribute('type') === 'loader' && ev.group !== 'undo') { deleteBlocksLoadedBy(ev.blockId, ev.group); } @@ -220,7 +215,6 @@ const repaintDefaultColours = () => { export default class _Blockly { constructor() { - this.blocksXmlStr = ''; this.generatedJs = ''; // eslint-disable-next-line no-underscore-dangle Blockly.WorkspaceSvg.prototype.preloadAudio_ = () => {}; // https://github.com/google/blockly/issues/299 @@ -264,22 +258,47 @@ export default class _Blockly { renderInstance(); addBlocklyTranslation().then(() => { const defaultStrat = parseQueryString().strategy; - const xmlFile = defaultStrat ? `xml/${defaultStrat}.xml` : 'xml/main.xml'; + const xmlFile = `xml/${defaultStrat}.xml`; - $.get(xmlFile, main => { + const loadDomToWorkspace = dom => { repaintDefaultColours(); overrideBlocklyDefaultShape(); - this.blocksXmlStr = Blockly.Xml.domToPrettyText(main); - Blockly.Xml.domToWorkspace(main.getElementsByTagName('xml')[0], workspace); + Blockly.Xml.domToWorkspace(dom, workspace); this.zoomOnPlusMinus(); disposeBlocksWithLoaders(); setTimeout(() => { - setBeforeUnload(true); + saveBeforeUnload(); Blockly.mainWorkspace.cleanUp(); Blockly.mainWorkspace.clearUndo(); }, 0); - resolve(); - }); + }; + + const getFile = xml => { + importFile(xml) + .then(dom => { + loadDomToWorkspace(dom.getElementsByTagName('xml')[0]); + resolve(); + }) + .catch(text => { + if (text) { + const previousStrat = Blockly.Xml.textToDom(text); + loadDomToWorkspace(previousStrat); + resolve(); + } else { + getFile('xml/main.xml'); + } + + if (defaultStrat) { + globalObserver.emit('Notify', { + className: 'warn', + message : translate('The strategy you tried to load is invalid'), + position : 'right', + }); + } + }); + }; + + getFile(xmlFile); }); }); }); @@ -294,10 +313,13 @@ export default class _Blockly { } } resetWorkspace() { - Blockly.Events.setGroup('reset'); - Blockly.mainWorkspace.clear(); - Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(this.blocksXmlStr), Blockly.mainWorkspace); - Blockly.Events.setGroup(false); + importFile('xml/main.xml').then(dom => { + Blockly.Events.setGroup('reset'); + Blockly.mainWorkspace.clear(); + Blockly.Xml.domToWorkspace(dom.getElementsByTagName('xml')[0], Blockly.mainWorkspace); + Blockly.Events.setGroup(false); + this.cleanUp(); + }); } /* eslint-disable class-methods-use-this */ cleanUp() { @@ -358,6 +380,8 @@ export default class _Blockly { } }); + removeParam('strategy'); + try { if (xml.hasAttribute('collection') && xml.getAttribute('collection') === 'true') { loadBlocks(xml, dropEvent); @@ -372,7 +396,7 @@ export default class _Blockly { save(arg) { const { filename, collection } = arg; - setBeforeUnload(true); + saveBeforeUnload(); const xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace); cleanBeforeExport(xml); diff --git a/src/botPage/view/blockly/utils.js b/src/botPage/view/blockly/utils.js index cacb8aa752..33eed2584c 100644 --- a/src/botPage/view/blockly/utils.js +++ b/src/botPage/view/blockly/utils.js @@ -506,3 +506,44 @@ export const cleanBeforeExport = xml => { } }); }; + +export const importFile = xml => + new Promise((resolve, reject) => { + $.get(xml, dom => { + resolve(dom); + }).catch(() => { + const previousWorkspaceText = localStorage.getItem('previousStrat'); + reject(previousWorkspaceText); + }); + }); + +export const saveBeforeUnload = () => { + window.onbeforeunload = () => { + const currentDom = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace); + localStorage.setItem('previousStrat', Blockly.Xml.domToPrettyText(currentDom)); + return null; + }; +}; + +export const removeParam = key => { + const sourceURL = window.location.href; + let rtn = sourceURL.split('?')[0]; + let paramsArr = []; + const queryString = sourceURL.indexOf('?') !== -1 ? sourceURL.split('?')[1] : ''; + if (queryString !== '') { + paramsArr = queryString.split('&'); + for (let i = paramsArr.length - 1; i >= 0; i -= 1) { + const paramPair = paramsArr[i]; + const paramKey = paramPair.split('='); + const param = paramKey[0]; + if (param === key) { + paramsArr.splice(i, 1); + } + } + if (paramsArr.length) { + rtn = `${rtn}?${paramsArr.join('&')}`; + } + } + + window.history.pushState({}, window.title, rtn); +}; diff --git a/src/indexPage/endpoint.js b/src/indexPage/endpoint.js index 872d037d90..705ae59162 100644 --- a/src/indexPage/endpoint.js +++ b/src/indexPage/endpoint.js @@ -65,7 +65,7 @@ function addEndpoint(e) { setStorage('config.server_url', serverUrl); setStorage('config.app_id', appId); - const urlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:.[\w.-]+)+[\w-._~:\/?#[\]@!$&'()*+,;=.]+$/; + const urlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:.[\w.-]+)+[\w-._~:?#[\]@!$&'()*+,;=.]+$/; if (!urlReg.test(serverUrl)) { $('#error')