diff --git a/.eslintrc.yml b/.eslintrc.yml index b0d68de7d..a3d15d411 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -7,7 +7,6 @@ settings: import/core-modules: ## don't lint for these missing packages in package.json - electron ## 'electron' is only needed as devDependency / global installation - rules: # "off" or 0 - turn the rule off # "warn" or 1 - turn the rule on as a warning (doesn’t affect exit code) @@ -22,7 +21,12 @@ rules: no-underscore-dangle: off comma-dangle: - error - - only-multiline + - only-multiline ## no comma after last item if one line, though allow comma if multiline + import/no-extraneous-dependencies: ## checks if required modules are missing in packages.json + - error + - devDependencies: ## declares files, whose imports belong to devDependencies + - "**/scripts/*.js" + - "**/*.test.js" globals: # don't warn about missing declarations i18n: true diff --git a/README.md b/README.md index 1ca1bb7bc..ece726016 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Join the chat at https://gitter.im/ethereum/mist](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/mist?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status develop branch](https://travis-ci.org/ethereum/mist.svg?branch=develop)](https://travis-ci.org/ethereum/mist) [![Code Climate](https://codeclimate.com/github/ethereum/mist/badges/gpa.svg)](https://codeclimate.com/github/ethereum/mist) +[![dependencies Status](https://david-dm.org/ethereum/mist/status.svg)](https://david-dm.org/ethereum/mist) +[![devDependencies Status](https://david-dm.org/ethereum/mist/dev-status.svg)](https://david-dm.org/ethereum/mist?type=dev) The Mist browser is the tool of choice to browse and use Ðapps. @@ -42,7 +44,7 @@ To run mist in development you need [Node.js NPM](https://nodejs.org) and [Meteo $ npm install -g electron@1.3.13 $ npm install -g gulp -And some futher tools to help with downloading and unzipping client nodes: +And some further tools to help with downloading and unzipping client nodes: _Linux:_ diff --git a/errorPages/400.html b/errorPages/400.html index 06d5acc2d..e4ceb706d 100644 --- a/errorPages/400.html +++ b/errorPages/400.html @@ -7,7 +7,7 @@ background-color: #f0f0f0; color: #ACACAC; text-shadow: 0 -1px #fff; - font: 20px Helvetica Neue, Arial; + font: 20px Source Sans Pro, Helvetica Neue, Arial; font-weight: 200; text-align: center; padding: 10px; diff --git a/errorPages/404.html b/errorPages/404.html new file mode 100644 index 000000000..c0eafb982 --- /dev/null +++ b/errorPages/404.html @@ -0,0 +1,19 @@ + + + Error 404 + + + + ﴾๏๏﴿

+ URL not found. + + \ No newline at end of file diff --git a/errorPages/500.html b/errorPages/500.html new file mode 100644 index 000000000..57aee2e60 --- /dev/null +++ b/errorPages/500.html @@ -0,0 +1,19 @@ + + + Error 500 + + + + (ノಠ益ಠ)ノ

+ Oops.. Something went wrong! + + \ No newline at end of file diff --git a/interface/client/styles/networkIndicator.import.less b/interface/client/styles/networkIndicator.import.less index c46672228..0cc4165a0 100644 --- a/interface/client/styles/networkIndicator.import.less +++ b/interface/client/styles/networkIndicator.import.less @@ -1,3 +1,4 @@ +// Applies to both onboarding and splash screen .network-indicator { position: absolute; top: 10px; @@ -9,8 +10,10 @@ opacity: 0.8; color: @colorGrey; text-transform: uppercase; +} - .unknown { +// Applies only to the splash screen +.splash-screen .network-indicator .unknown { position: absolute; top: 155px; text-align: center; @@ -21,5 +24,4 @@ font-weight: normal; text-transform: none; color: #fff; - } -} +} \ No newline at end of file diff --git a/interface/client/templates/popupWindows/onboardingScreen.html b/interface/client/templates/popupWindows/onboardingScreen.html index 738d3bb17..3447cf330 100644 --- a/interface/client/templates/popupWindows/onboardingScreen.html +++ b/interface/client/templates/popupWindows/onboardingScreen.html @@ -113,27 +113,24 @@

{{i18n "mist.popupWindows.onboarding.learnIt"}}

diff --git a/interface/client/templates/popupWindows/onboardingScreen.js b/interface/client/templates/popupWindows/onboardingScreen.js index eec33cbd9..34ee0aedc 100644 --- a/interface/client/templates/popupWindows/onboardingScreen.js +++ b/interface/client/templates/popupWindows/onboardingScreen.js @@ -38,11 +38,11 @@ Template['popupWindows_onboardingScreen'].onCreated(function() { if (syncing === true) { web3.reset(true); - - } else if (_.isObject(syncing)) { + } else if(_.isObject(syncing)) { // loads syncing data and adds it to old by using 'extend' var oldData = TemplateVar.get(template, 'syncing'); - TemplateVar.set(template, 'syncing', _.extend(oldData || {}, syncing || {})); + + TemplateVar.set(template, 'syncing', _.extend(oldData||{}, syncing||{})); } else { TemplateVar.set(template, 'syncing', false); @@ -78,7 +78,7 @@ Template['popupWindows_onboardingScreen'].helpers({ return (account) ? account.toLowerCase() : ''; }, /** - Updates the Sync Message live + Updates the Sync Data @method syncStatus */ @@ -86,42 +86,61 @@ Template['popupWindows_onboardingScreen'].helpers({ // This functions loops trhough numbers while waiting for the node to respond var template = Template.instance(); + Meteor.clearInterval(template._intervalId); // Create an interval to quickly iterate trough the numbers template._intervalId = Meteor.setInterval(function() { // load the sync information - var syncing = TemplateVar.get(template, 'syncing'); + var syncing = TemplateVar.get(template, 'syncing'); - // Calculates a block t display that is always getting 1% closer to target - syncing._displayBlock = (syncing._displayBlock + (syncing.currentBlock - syncing._displayBlock) / 100) || syncing.currentBlock; + if (syncing) { + // If it's syncing, then it's not ready + TemplateVar.set(template, 'readyToLaunch', false); - syncing._displayStatesDownload = Number(syncing._displayStatesDownload + (syncing.pulledStates / syncing.knownStates - syncing._displayStatesDownload) / 100) || syncing.pulledStates / syncing.knownStates; + // Calculates a block t display that is always getting a few % closer to target + syncing._displayBlock = (syncing._displayBlock + 2*(syncing.currentBlock - syncing._displayBlock) / 100 ) || Number(syncing.startingBlock); + syncing._displayStatesDownload = Number(syncing._displayStatesDownload + (syncing.pulledStates/(1 +syncing.knownStates) - syncing._displayStatesDownload) / 100 ) || Number(syncing.pulledStates)/Number(syncing.knownStates + 1); - // Calculates progress - syncing.progress = Math.round(((syncing._displayBlock - syncing.startingBlock) / (syncing.highestBlock - syncing.startingBlock)) * 100); + // Calculates progress + syncing.progress = 100 * (syncing._displayBlock - syncing.startingBlock) / (1 + Number(syncing.highestBlock) - syncing.startingBlock); - // Makes fancy strings - syncing.blockDiff = numeral(syncing.highestBlock - syncing.currentBlock).format('0,0'); - syncing.highestBlockString = numeral(syncing.highestBlock).format('0,0'); - syncing.displayBlock = numeral(Math.round(syncing._displayBlock)).format('0,0'); - syncing.statesPercent = numeral(Math.round(syncing._displayStatesDownload * 10000) / 100).format('0.00'); + // Makes fancy strings + syncing.blockDiff = numeral(syncing.highestBlock - syncing.currentBlock).format('0,0'); + syncing.highestBlockString = numeral(syncing.highestBlock).format('0,0'); + syncing.displayBlock = numeral(Math.round(syncing._displayBlock)).format('0,0'); + syncing.statesPercent = numeral(Math.round(syncing._displayStatesDownload*10000)/100).format('0.00'); - // Saves the data back to the object - TemplateVar.set(template, 'syncing', syncing); + // Saves the data back to the object + TemplateVar.set(template, 'syncing', syncing); - // Only show states if they are less than 50% downloaded - if (Math.round(1000 * Number(syncing._displayStatesDownload)) !== Math.round(1000 * Number(syncing.pulledStates / syncing.knownStates))) { - TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessageWithStates', syncing)); - } else { - TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessage', syncing)); - } + // If it's close enough, show the synced button + + if (Number(syncing.highestBlock) - syncing.currentBlock < 100 ) { + TemplateVar.set(template, 'readyToLaunch', true); + } + + // Only show states if they are changing + if (Math.round(1000*Number(syncing._displayStatesDownload)) !== Math.round(1000*Number(syncing.pulledStates/(syncing.knownStates+1)))) { + TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessageWithStates', syncing)); + } else if (syncing.displayBlock == '0') { + TemplateVar.set(template, "syncStatusMessageLive", ''); + } else { + TemplateVar.set(template, "syncStatusMessageLive", TAPi18n.__('mist.popupWindows.onboarding.syncMessage', syncing)); + } + } }, 50); + }, + /** + Updates the Sync Message live - return TemplateVar.get(template, "syncStatusMessageLive"); + @method syncStatusMessage + */ + 'syncStatusMessage' : function() { + return TemplateVar.get("syncStatusMessageLive"); } }); @@ -136,12 +155,14 @@ Template['popupWindows_onboardingScreen'].events({ if (TemplateVar.get('testnet')) { ipc.send('onBoarding_changeNet', false); TemplateVar.set('testnet', false); + TemplateVar.set('syncing', null); } }, 'click .start-testnet': function(e, template) { if (!TemplateVar.get('testnet')) { ipc.send('onBoarding_changeNet', true); TemplateVar.set('testnet', true); + TemplateVar.set('syncing', null); } TemplateVar.set('currentActive', 'testnet'); @@ -154,17 +175,20 @@ Template['popupWindows_onboardingScreen'].events({ 'click .goto-account': function() { TemplateVar.set('currentActive', 'account'); }, - 'click .goto-tutorial-1': function() { - TemplateVar.set('currentActive', 'tutorial-1'); - TemplateVar.set('readyToLaunch', true); + 'click .goto-tutorial-1': function(){ + TemplateVar.set('currentActive','tutorial-1'); + if (!TemplateVar.get('syncing')) + TemplateVar.set('readyToLaunch', true); }, - 'click .goto-tutorial-2': function() { - TemplateVar.set('currentActive', 'tutorial-2'); - TemplateVar.set('readyToLaunch', true); + 'click .goto-tutorial-2': function(){ + TemplateVar.set('currentActive','tutorial-2'); + if (!TemplateVar.get('syncing')) + TemplateVar.set('readyToLaunch', true); }, - 'click .goto-tutorial-3': function() { - TemplateVar.set('currentActive', 'tutorial-3'); - TemplateVar.set('readyToLaunch', true); + 'click .goto-tutorial-3': function(){ + TemplateVar.set('currentActive','tutorial-3'); + if (!TemplateVar.get('syncing')) + TemplateVar.set('readyToLaunch', true); }, /** Start the application diff --git a/interface/client/templates/popupWindows/splashScreen.js b/interface/client/templates/popupWindows/splashScreen.js index fa08643dc..4aea7fd79 100644 --- a/interface/client/templates/popupWindows/splashScreen.js +++ b/interface/client/templates/popupWindows/splashScreen.js @@ -190,9 +190,9 @@ Template['popupWindows_splashScreen'].helpers({ syncData._displayKnownStates = Number(syncData.knownStates || 0); } else { // Increment each them slowly to match target number - syncData._displayBlock += (Number(syncData.currentBlock) - syncData._displayBlock) / 10; - syncData._displayState += (Number(syncData.pulledStates || 0) - syncData._displayState) / 10; - syncData._displayKnownStates += (Number(syncData.knownStates || 0) - syncData._displayKnownStates) / 10; + syncData._displayBlock += (Number(syncData.currentBlock) - syncData._displayBlock) / 100; + syncData._displayState += (Number(syncData.pulledStates || 0) - syncData._displayState) / 100; + syncData._displayKnownStates += (Number(syncData.knownStates || 0) - syncData._displayKnownStates) / 100; } // Create the fancy strings @@ -225,7 +225,7 @@ Template['popupWindows_splashScreen'].helpers({ } } - }, 100); + }, 10); return TemplateVar.get(template, "syncStatusMessageLive"); } diff --git a/interface/client/templates/views/webview.js b/interface/client/templates/views/webview.js index b64aa6ec6..e28d4b848 100644 --- a/interface/client/templates/views/webview.js +++ b/interface/client/templates/views/webview.js @@ -60,6 +60,10 @@ Template['views_webview'].onRendered(function(){ webviewLoadStop.call(this, tabId, e); }); + // show error pages + webview.addEventListener('did-fail-load', showError.bind(webview, tabId)); + webview.addEventListener('crashed', showError.bind(webview, tabId)); + // navigate page, and redirect to browser tab if necessary webview.addEventListener('will-navigate', webviewLoadStart.bind(webview, tabId)); webview.addEventListener('did-get-redirect-request', webviewLoadStart.bind(webview, tabId)); @@ -122,6 +126,10 @@ Template['views_webview'].helpers({ }}); } + // allow error pages + if(url && url.indexOf('file://'+ dirname + '/errorPages/') === 0) { + return url; + } // CHECK URL and throw error if not allowed if(!Helpers.sanitizeUrl(url, true)) { @@ -137,7 +145,7 @@ Template['views_webview'].helpers({ return 'file://'+ dirname + '/errorPages/400.html'; } - // remove redirect + // add url if(url) { template.url = url; Tabs.update(this._id, {$set: { diff --git a/interface/client/templates/webviewEvents.js b/interface/client/templates/webviewEvents.js index 26e10bfc8..332676911 100644 --- a/interface/client/templates/webviewEvents.js +++ b/interface/client/templates/webviewEvents.js @@ -1,4 +1,31 @@ +showError = function(tabId, e){ + if(e.isMainFrame || e.killed) { + var url, + path = 'file://'+ dirname + '/errorPages/'; + + if(e.killed) { + e.errorCode = 500; + } + + switch(e.errorCode) { + case -105: + url = path +'404.html'; + break; + case 500: + url = path +'500.html'; + break; + } + + if(url) { + Tabs.update(tabId, {$set: { + redirect: url + }}); + } + } +}; + + webviewChangeUrl = function(tabId, e){ if(e.type === 'did-navigate-in-page' && !e.isMainFrame) return; diff --git a/interface/i18n/mist.en.i18n.json b/interface/i18n/mist.en.i18n.json index 2b0b04f29..39df06f00 100644 --- a/interface/i18n/mist.en.i18n.json +++ b/interface/i18n/mist.en.i18n.json @@ -64,6 +64,10 @@ "ethereumNode": "Ethereum Node", "network": "Network", "mainNetwork": "Main Network", + "nodeMode": "Chain download", + "fullNode": "Store full blockchain", + "lightNode": "Use light Node (experimental!)", + "mainNetwork": "Main Network", "startMining": "⛏ Start Mining (Testnet only)", "stopMining": "⛏ Stop Mining" }, @@ -226,7 +230,7 @@ "lookupDataExplainer": "Look this up on the internet" }, "onboarding": { - "description" : "Ethereum is a public blockchain that features a turing complete programming for building solid, decentralized applications.", + "description" : "Ethereum is a platform for decentralized blockchain apps with a fully featured programming language", "goToTestnet" : "Use the test network", "goToTestnetDescription" : "Test the technology freely in a sandboxed testnet, without using real ether.", "gotoMainnet" : "Use the main network", @@ -253,6 +257,7 @@ "downloadingBlocks": "Downloading blocks", "syncMessage": "Block __displayBlock__ of __highestBlockString__", "syncMessageWithStates": "Block __displayBlock__ of __highestBlockString__ (Chain structure __statesPercent__%)", + "startingSync": "Getting ready to sync..", "tutorial1Description" : "

Now the only thing left to do is wait for the download to finish. Here's some reading suggestions:

Make your own money

Make a cryptocurrency with a fixed market supply, tokens representing real world assets, etc

", "tutorial2Description" : "

Create a crowdsale

Raise funds for a common goal, fully trustable without a third party. Sidestep the hurdle of traditional funding system and go directly to the source by funding an organization via the blockchain.

", "tutorial3Description" : "

Create a blockchain organization

Create an autonomous organization with rules on spending money and making decisions for you and your investors.

", diff --git a/modules/preloader/include/mistAPI.js b/modules/preloader/include/mistAPI.js index 996dcbf57..78cfbad3b 100644 --- a/modules/preloader/include/mistAPI.js +++ b/modules/preloader/include/mistAPI.js @@ -56,6 +56,9 @@ module.exports = () => { ipcRenderer.send('mistAPI_requestAccount'); }, + solidity: { + version: packageJson.dependencies.solc, + }, sounds: { bip: function playSound() { ipcRenderer.sendToHost('mistAPI_sound', `file://${__dirname}/../../../sounds/bip.mp3`); diff --git a/modules/preloader/include/openPopup.js b/modules/preloader/include/openPopup.js index 9f53b4ec5..ddaa73720 100644 --- a/modules/preloader/include/openPopup.js +++ b/modules/preloader/include/openPopup.js @@ -26,6 +26,6 @@ document.addEventListener('click', (e) => { nodeIntegration: false, } }); - win.loadURL(node.href, true); + win.loadURL(node.href); } }, false); diff --git a/package.json b/package.json index a01013951..01d56387c 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "url": "https://github.com/ethereum/mist.git" }, "scripts": { + "postinstall": "cd interface && yarn", "ci": "gulp --platform=linux" }, "main": "main.js", diff --git a/tests/mocha-in-browser/spec/general-spec.js b/tests/mocha-in-browser/spec/general-spec.js index d5773843b..3c6d8aabb 100644 --- a/tests/mocha-in-browser/spec/general-spec.js +++ b/tests/mocha-in-browser/spec/general-spec.js @@ -26,6 +26,7 @@ describe('General', function () { 'requestAccount', 'sounds', 'menu', + 'solidity' ]; expect(mist).to.have.all.keys(allowedAttributes); @@ -34,6 +35,10 @@ describe('General', function () { it('should return platform', function () { expect(mist.platform).to.be.oneOf(['darwin', 'win32', 'freebsd', 'linux', 'sunos']); }); + + it('should report solidity version', function () { + expect(mist.solidity.version).to.match(/^\d\.\d{1,2}\.\d{1,2}$/); // match examples: 0.4.6, 0.5.10, 0.10.0 + }); }); describe('mist.menu', function () {