diff --git a/.codeclimate.yml b/.codeclimate.yml index 212ffea..804d002 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -20,5 +20,4 @@ ratings: ## enables GPA rating - "**.js" exclude_paths: -- "interface/client/lib/signatures.js" - "tests/" diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..9650bc3 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules +interface/client/lib/signatures.js diff --git a/.eslintrc.yml b/.eslintrc.yml index 59576da..44e26c0 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -19,15 +19,19 @@ rules: arrow-body-style: off prefer-arrow-callback: off no-underscore-dangle: off + object-curly-spacing: off + func-names: off + global-require: off + class-methods-use-this: off comma-dangle: - error - 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/build-dist.js" - "**/tests/_base.js" - "**/*.test.js" + - "**/gulpTasks/*.js" globals: # don't warn about missing declarations i18n: true diff --git a/.mention-bot b/.mention-bot deleted file mode 100644 index 4e426ab..0000000 --- a/.mention-bot +++ /dev/null @@ -1,13 +0,0 @@ -{ - "actions": ["labeled"], - "withLabel": "please review", - "maxReviewers": 3, - "numFilesToCheck": 5, - "message": "@pullRequester, thanks! @reviewers, please review this.", - "findPotentialReviewers": true, - "fileBlacklist": ["*.md, signatures.js, interface/.meteor"], - "skipAlreadyAssignedPR": false, - "skipAlreadyMentionedPR": false, - "userBlacklist": ["hiddentao"], - "assignToReviewer": false -} diff --git a/.travis.yml b/.travis.yml index 9b47151..f17d061 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,8 +28,6 @@ matrix: - sudo dpkg --add-architecture i386 && sudo add-apt-repository ppa:ubuntu-wine/ppa -y - sudo apt-get update -q - sudo apt-get install --no-install-recommends -y mono-devel ca-certificates-mono wine1.8 - after_script: - - makensis -V2 scripts/windows-installer.nsi # LINUX @@ -71,14 +69,14 @@ install: script: - if [[ $TRAVIS_BRANCH != "master" ]]; then unset CSC_LINK CSC_KEY_PASSWORD; fi # disable macOS code-signing (production certificate) on develop branch - - travis_wait 60 gulp mist --platform $GULP_PLATFORM - - if [[ $TRAVIS_BRANCH == "master" ]]; then travis_wait 60 gulp wallet --platform $GULP_PLATFORM; fi # also build wallet if on master branch - - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; sleep 3; fi - - if [[ $GULP_PLATFORM != "win" ]]; then gulp test-mist; fi + - if [[ $GULP_PLATFORM == "mac" ]]; then travis_wait 60 gulp --$GULP_PLATFORM; fi # increase timeout for slower mac builds + - if [[ $GULP_PLATFORM != "mac" ]]; then gulp --$GULP_PLATFORM; fi + - if [[ $TRAVIS_BRANCH == "master" ]]; then travis_wait 60 gulp --wallet --$GULP_PLATFORM; fi # also build wallet if on master branch + - if [[ $TRAVIS_OS_NAME == "linux" ]]; then export DISPLAY=:99.0; sh -e /etc/init.d/xvfb start; sleep 3; fi # prepare for integration testing + - if [[ $GULP_PLATFORM != "win" ]]; then gulp test; fi after_success: - - gulp mist-checksums --platform $GULP_PLATFORM - - if [[ $TRAVIS_BRANCH == "master" ]]; then gulp wallet-checksums --platform $GULP_PLATFORM; fi + - if [[ $TRAVIS_BRANCH == "master" ]]; then gulp uploadQueue --$GULP_PLATFORM; fi notifications: webhooks: diff --git a/README.md b/README.md index d7a10eb..02acdfc 100644 --- a/README.md +++ b/README.md @@ -30,43 +30,35 @@ Once a Mist version is released the Meteor frontend part is bundled using `meteo ### Dependencies -Requirements: +To run mist in development you need: -* Electron v1.3.13 -* Node v6.0 or above -* [Yarn package manager](https://yarnpkg.com/) +- [Node.js](https://nodejs.org) `v6.x` (use the prefered installation method for your OS) +- [Meteor](https://www.meteor.com/install) javascript app framework +- [Yarn](https://yarnpkg.com/) package manager +- [Electron](http://electron.atom.io/) `v1.3.13` cross platform desktop app framework +- [Gulp](http://gulpjs.com/) build and automation system -To run mist in development you need [Node.js NPM](https://nodejs.org) and [Meteor](https://www.meteor.com/install) and electron installed: +Install the later ones via: $ curl https://install.meteor.com/ | sh - $ npm install -g electron@1.3.13 - $ npm install -g gulp + $ curl -o- -L https://yarnpkg.com/install.sh | bash + $ yarn global add electron@1.3.13 + $ yarn global add gulp -And some further tools to help with downloading and unzipping client nodes: +### Initialisation -_Linux:_ - - $ apt-get install unzip - - -### Installation - -Now you're ready to install Mist: +Now you're ready to initialise Mist for development: $ git clone https://github.com/ethereum/mist.git $ cd mist - $ git submodule update --init $ yarn To update Mist in the future, run: $ cd mist - $ git pull && git submodule update + $ git pull $ yarn -#### Options -It may be preferable to only download platform-specific nodes by passing the `--platform` flag, please refer to the [options section](#platform). - ### Run Mist For development we start the interface with a Meteor server for autoreload etc. @@ -144,60 +136,61 @@ $ electron . --rpc ~/Library/Ethereum/geth.ipc --node-networkid 1234 --node-dat _NOTE: since `ipcpath` is also a Mist option you do not need to also include a `--node-ipcpath` option._ -You can also run `geth` separately yourself with the same options prior to start -Mist normally. +You can also launch `geth` separately with the same options prior starting +Mist. ### Deployment +Our build system relies on [gulp](http://gulpjs.com/) and [electron-builder](https://github.com/electron-userland/electron-builder/). -To create a binaries you need to install [`electron-builder` dependencies](https://github.com/electron-userland/electron-builder/wiki/Multi-Platform-Build#macos): +#### Dependencies + +[meteor-build-client](https://github.com/frozeman/meteor-build-client) bundles the [meteor](https://www.meteor.com/)-based interface. Install it via: - // tools for the windows binaries - $ brew install wine --without-x11 mono makensis - // tools for the Linux binaries - $ brew install gnu-tar libicns graphicsmagick xz - // general dependencies $ npm install -g meteor-build-client -To generate the binaries simply run: +Furthermore cross-platform builds require additional [`electron-builder` dependencies](https://github.com/electron-userland/electron-builder/wiki/Multi-Platform-Build#linux). On macOS those are: - $ cd mist - $ gulp + // windows deps + $ brew install wine --without-x11 mono makensis + + // linux deps + $ brew install gnu-tar libicns graphicsmagick xz - // Or to generate the wallet (using the https://github.com/ethereum/meteor-dapp-wallet -> master) - $ gulp wallet +#### Generate packages -This will generate the binaries inside the `dist_mist/release` or `dist_wallet/release` folder. +To generate the binaries for Mist run: -#### Options + $ gulp -##### platform +To generate the Ethereum Wallet (this will pack the one Ðapp from https://github.com/ethereum/meteor-dapp-wallet): -Additional you can only build the windows, linux, mac or all binary by using the `platform` option: + $ gulp --wallet - $ gulp update-nodes --platform mac +The generated binaries will be under `dist_mist/release` or `dist_wallet/release`. - // And - $ gulp mist --platform mac - // Or - $ gulp mist --platform mac,win +#### Options +##### platform -Options are: +To build binaries for specific platforms (default: all available) use the following flags: -- `mac` (Mac OSX) -- `win` (Windows) -- `linux` (Linux) -- `all` (default) + // on mac + $ gulp --win --linux --mac + // on linux + $ gulp --win --linux + + // on win + $ gulp --win ##### walletSource -With the `walletSource` you can specify the branch to use, default ist `master`: +With the `walletSource` you can specify the Wallet branch to use, default is `master`: - $ gulp mist --walletSource develop + $ gulp --wallet --walletSource develop Options are: @@ -206,28 +199,25 @@ Options are: - `develop` - `local` Will try to build the wallet from [mist/]../meteor-dapp-wallet/app -##### mist-checksums | wallet-checksums +*Note: applicable only when combined with `--wallet`* + +#### Checksums -Spits out the SHA256 checksums of distributables. +Spits out the MD5 checksums of distributables. It expects installer/zip files to be in the generated folders e.g. `dist_mist/release` - $ gulp mist-checksums + $ gulp checksums [--wallet] - 3f726fff186b85c600ea2459413d0bf5ada2dbc98877764efbefa545f96eb975 ./dist_mist/release/Mist-0.8.1-ia32.exe - ab4d26d5ebc66e9aba0fa610071266bacbb83faacbb7ed0dd2acb24386190bdb ./dist_mist/release/Mist-0.8.1.exe - 909b0fb4c7b09b731b2a442c457747e04ffdd9c03b6edc06079ae05a46200d13 ./dist_mist/release/Mist-0.8.1-ia32.deb - e114d6188963dfdae0489abf4e8923da58b39ff9cdbaad26e803af27c7ce55d1 ./dist_mist/release/Mist-0.8.1.deb - 930787dd2f5ed6931068bff9244bccc01f397f552c48ded0f08e515e276dd080 ./dist_mist/release/Mist-0.8.1.dmg -### Code signing for production +## Testing -**As of [#972](https://github.com/ethereum/mist/pull/972) we've updated the build process and thus need to redo code-signing.** +First make sure to build Mist with: + $ gulp [--wallet] -## Testing +Then run the tests: -First make sure to build Mist with: -`gulp mist --platform [mac,linux]` or `gulp wallet --platform [mac,linux]`. + $ gulp test [--wallet] -Then run `gulp test-mist` or `gulp test-wallet`, accordingly. +*Note: Integration tests are not yet supported on Windows.* diff --git a/clientBinaries.json b/clientBinaries.json index f0c720a..e26ab18 100644 --- a/clientBinaries.json +++ b/clientBinaries.json @@ -2,36 +2,36 @@ { "clients": { "Geth": { - "version": "1.5.9", + "version": "1.6.0", "platforms": { "linux": { "x64": { "download": { - "url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.5.9-a07539fb.tar.gz", + "url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.6.0-facc47cb.tar.gz", "type": "tar", - "md5": "a0b0dda353a655a65c6d3d809ee852fd", - "bin": "geth-linux-amd64-1.5.9-a07539fb/geth" + "md5": "048b0618696b040f6d3ff18ee2e5763a", + "bin": "geth-linux-amd64-1.6.0-facc47cb/geth" }, "bin": "geth", "commands": { "sanity": { "args": ["version"], - "output": [ "Geth", "1.5.9" ] + "output": [ "Geth", "1.6.0" ] } } }, "ia32": { "download": { - "url": "https://gethstore.blob.core.windows.net/builds/geth-linux-386-1.5.9-a07539fb.tar.gz", + "url": "https://gethstore.blob.core.windows.net/builds/geth-linux-386-1.6.0-facc47cb.tar.gz", "type": "tar", - "md5": "3198330842cbfc6baa7e7b2624f8f6bd", - "bin": "geth-linux-386-1.5.9-a07539fb/geth" + "md5": "44215076b4d50e079878bcabeaae2117", + "bin": "geth-linux-386-1.6.0-facc47cb/geth" }, "bin": "geth", "commands": { "sanity": { "args": ["version"], - "output": [ "Geth", "1.5.9" ] + "output": [ "Geth", "1.6.0" ] } } } @@ -39,16 +39,16 @@ "mac": { "x64": { "download": { - "url": "https://gethstore.blob.core.windows.net/builds/geth-darwin-amd64-1.5.9-a07539fb.tar.gz", + "url": "https://gethstore.blob.core.windows.net/builds/geth-darwin-amd64-1.6.0-facc47cb.tar.gz", "type": "tar", - "md5": "6a797a930a0930ec88b810e6b2d204eb", - "bin": "geth-darwin-amd64-1.5.9-a07539fb/geth" + "md5": "446308f1d702bf5d30892bda43c25d23", + "bin": "geth-darwin-amd64-1.6.0-facc47cb/geth" }, "bin": "geth", "commands": { "sanity": { "args": ["version"], - "output": [ "Geth", "1.5.9" ] + "output": [ "Geth", "1.6.0" ] } } } @@ -56,31 +56,31 @@ "win": { "x64": { "download": { - "url": "https://gethstore.blob.core.windows.net/builds/geth-windows-amd64-1.5.9-a07539fb.zip", + "url": "https://gethstore.blob.core.windows.net/builds/geth-windows-amd64-1.6.0-facc47cb.zip", "type": "zip", - "md5": "057cdfdf4347ec9d5c0bf0b964f1b97b", - "bin": "geth-windows-amd64-1.5.9-a07539fb\\geth.exe" + "md5": "e4bb82c4d7078269a932cb7d46c5acb4", + "bin": "geth-windows-amd64-1.6.0-facc47cb\\geth.exe" }, "bin": "geth.exe", "commands": { "sanity": { "args": ["version"], - "output": [ "Geth", "1.5.9" ] + "output": [ "Geth", "1.6.0" ] } } }, "ia32": { "download": { - "url": "https://gethstore.blob.core.windows.net/builds/geth-windows-386-1.5.9-a07539fb.zip", + "url": "https://gethstore.blob.core.windows.net/builds/geth-windows-386-1.6.0-facc47cb.zip", "type": "zip", - "md5": "fa3511985215e151bafdc080e9b1fef6", - "bin": "geth-windows-386-1.5.9-a07539fb\\geth.exe" + "md5": "0935e22c5626c73b9f0b556b3323c71a", + "bin": "geth-windows-386-1.6.0-facc47cb\\geth.exe" }, "bin": "geth.exe", "commands": { "sanity": { "args": ["version"], - "output": [ "Geth", "1.5.9" ] + "output": [ "Geth", "1.6.0" ] } } } diff --git a/gulpTasks/building.js b/gulpTasks/building.js new file mode 100644 index 0000000..31a6a4f --- /dev/null +++ b/gulpTasks/building.js @@ -0,0 +1,233 @@ +const _ = require('underscore'); +const builder = require('electron-builder'); +const del = require('del'); +const exec = require('child_process').exec; +const fs = require('fs'); +const gulp = require('gulp'); +const options = require('../gulpfile.js').options; +const path = require('path'); +const Q = require('bluebird'); +const shell = require('shelljs'); +const version = require('../package.json').version; + + +const type = options.type; +const applicationName = (options.wallet) ? 'Ethereum Wallet' : 'Mist'; + + +gulp.task('clean-dist', (cb) => { + return del([ + `./dist_${type}/**/*`, + './meteor-dapp-wallet' + ], cb); +}); + + +gulp.task('copy-app-source-files', () => { + return gulp.src([ + './main.js', + './clientBinaries.json', + './modules/**', + './tests/**/*.*', + '!./tests/wallet/*', + `./icons/${type}/*`, + './sounds/*', + 'customProtocols.js' + ], { + base: './' + }) + .pipe(gulp.dest(`./dist_${type}/app`)); +}); + + +gulp.task('copy-build-folder-files', () => { + return gulp.src([ + `./icons/${type}/*`, + './interface/public/images/dmg-background.jpg' + ]) + .pipe(gulp.dest(`./dist_${type}/build`)); +}); + + +gulp.task('switch-production', (cb) => { + fs.writeFile(`./dist_${type}/app/config.json`, JSON.stringify({ + production: true, + mode: type + }), cb); +}); + + +gulp.task('bundling-interface', (cb) => { + const bundle = (additionalCommands) => { + exec(`cd interface \ + && meteor-build-client ${path.join('..', `dist_${type}`, 'app', 'interface')} -p "" \ + ${additionalCommands}`, + (err, stdout) => { + console.log(stdout); + cb(err); + }); + }; + + if (type === 'wallet') { + if (options.walletSource === 'local') { + console.log('Use local wallet at ../meteor-dapp-wallet/app'); + bundle(`&& cd ../../meteor-dapp-wallet/app \ + && meteor-build-client ../../mist/dist_${type}/app/interface/wallet -p ""`); + } else { + console.log(`Pulling https://github.com/ethereum/meteor-dapp-wallet/tree/${options.walletSource} "${options.walletSource}" branch...`); + bundle(`&& cd ../dist_${type} \ + && git clone --depth 1 https://github.com/ethereum/meteor-dapp-wallet.git \ + && cd meteor-dapp-wallet/app \ + && meteor-build-client ../../app/interface/wallet -p "" \ + && cd ../../ \ + && rm -rf meteor-dapp-wallet`); + } + } else { + bundle(); + } +}); + + +gulp.task('copy-i18n', () => { + return gulp.src([ + './interface/i18n/*.*', + './interface/project-tap.i18n' + ], { + base: './' + }) + .pipe(gulp.dest(`./dist_${type}/app`)); +}); + + +gulp.task('build-dist', (cb) => { + const appPackageJson = _.extend({}, require('../package.json'), { // eslint-disable-line global-require + name: applicationName.replace(/\s/, ''), + productName: applicationName, + description: applicationName, + homepage: 'https://github.com/ethereum/mist', + build: { + appId: `com.ethereum.${type}`, + category: 'public.app-category.productivity', + asar: true, + directories: { + buildResources: '../build', + output: '../dist' + }, + linux: { + target: [ + 'zip', + 'deb' + ] + }, + win: { + target: [ + 'zip' + ] + }, + dmg: { + background: '../build/dmg-background.jpg', + iconSize: 128, + contents: [{ + x: 441, + y: 448, + type: 'link', + path: '/Applications' + }, + { + x: 441, + y: 142, + type: 'file' + } + ] + } + } + }); + + fs.writeFileSync( + path.join(__dirname, `../dist_${type}`, 'app', 'package.json'), + JSON.stringify(appPackageJson, null, 2), 'utf-8' + ); + + const targets = []; + if (options.mac) targets.push(builder.Platform.MAC); + if (options.win) targets.push(builder.Platform.WINDOWS); + if (options.linux) targets.push(builder.Platform.LINUX); + + builder.build({ + targets: builder.createTargets(targets, null, 'all'), + projectDir: path.join(__dirname, `../dist_${type}`, 'app'), + config: { + afterPack(params) { + return Q.try(() => { + shell.cp( + [ + path.join(__dirname, '..', 'LICENSE'), + path.join(__dirname, '..', 'README.md'), + path.join(__dirname, '..', 'AUTHORS') + ], + params.appOutDir + ); + }); + } + } + }) + .finally(() => { + cb(); + }); +}); + + +gulp.task('release-dist', (done) => { + const distPath = path.join(__dirname, `../dist_${type}`, 'dist'); + const releasePath = path.join(__dirname, `../dist_${type}`, 'release'); + + shell.rm('-rf', releasePath); + shell.mkdir('-p', releasePath); + + const appNameHypen = applicationName.replace(/\s/, '-'); + const appNameNoSpace = applicationName.replace(/\s/, ''); + const versionDashed = version.replace(/\./g, '-'); + + const cp = (inputPath, outputPath) => { + shell.cp(path.join(distPath, inputPath), path.join(releasePath, outputPath)); + }; + + _.each(options.activePlatforms, (platform) => { + switch (platform) { // eslint-disable-line default-case + case 'win': + cp( + `${applicationName}-${version}-ia32-win.zip`, `${appNameHypen}-win32-${versionDashed}.zip`); + cp( + `${applicationName}-${version}-win.zip`, `${appNameHypen}-win64-${versionDashed}.zip`); + break; + case 'mac': + cp( + path.join('mac', `${applicationName}-${version}.dmg`), `${appNameHypen}-macosx-${versionDashed}.dmg`); + break; + case 'linux': + cp( + `${appNameNoSpace}_${version}_i386.deb`, `${appNameHypen}-linux32-${versionDashed}.deb`); + cp( + `${appNameNoSpace}-${version}-ia32.zip`, `${appNameHypen}-linux32-${versionDashed}.zip`); + cp( + `${appNameNoSpace}_${version}_amd64.deb`, `${appNameHypen}-linux64-${versionDashed}.deb`); + cp( + `${appNameNoSpace}-${version}.zip`, `${appNameHypen}-linux64-${versionDashed}.zip`); + break; + } + }); + + done(); +}); + + +gulp.task('build-nsis', (cb) => { + const typeString = `-DTYPE=${type}`; + const appNameString = `-DAPPNAME=${applicationName.replace(/\s/, '-')}`; + const versionParts = version.split('.'); + const versionString = `-DVERSIONMAJOR=${versionParts[0]} -DVERSIONMINOR=${versionParts[1]} -DVERSIONBUILD=${versionParts[2]}`; + + const cmdString = `makensis ${versionString} ${typeString} ${appNameString} scripts/windows-installer.nsi`; + + exec(cmdString, cb); +}); diff --git a/gulpTasks/maintenance.js b/gulpTasks/maintenance.js new file mode 100644 index 0000000..a8ed790 --- /dev/null +++ b/gulpTasks/maintenance.js @@ -0,0 +1,172 @@ +/* eslint-disable +global-require +*/ + +const _ = require('underscore'); +const cmp = require('semver-compare'); +const compare = require('json-structure-diff').compareJSONObjects; +const fs = require('fs'); +const got = require('got'); +const gulp = require('gulp'); +const parseJson = require('xml2js').parseString; +const clientBinaries = require('../clientBinaries.json'); + + +gulp.task('update-nodes', (cb) => { + const clientBinariesGeth = clientBinaries.clients.Geth; + const localGethVersion = clientBinariesGeth.version; + const newJson = clientBinaries; + const geth = newJson.clients.Geth; + + // Query latest geth version + got('https://api.github.com/repos/ethereum/go-ethereum/releases/latest', { json: true }) + .then((response) => { + return response.body.tag_name; + }) + // Return tag name (e.g. 'v1.5.0') + .then((tagName) => { + const latestGethVersion = tagName.match(/\d+\.\d+\.\d+/)[0]; + + // Compare to current geth version in clientBinaries.json + if (cmp(latestGethVersion, localGethVersion)) { + geth.version = latestGethVersion; + + // Query commit hash (first 8 characters) + got(`https://api.github.com/repos/ethereum/go-ethereum/commits/${tagName}`, { json: true }) + .then((response) => { + return String(response.body.sha).substr(0, 8); + }) + .then((hash) => { + let blobs; // azure blobs + + // Query Azure assets for md5 hashes + got('https://gethstore.blob.core.windows.net/builds?restype=container&comp=list', { xml: true }) + .then((response) => { + parseJson(response.body, (err, data) => { // eslint-disable-line + if (err) return cb(err); + + blobs = data.EnumerationResults.Blobs[0].Blob; + }); + + // For each platform/arch in clientBinaries.json + _.keys(geth.platforms).forEach((platform) => { + _.keys(geth.platforms[platform]).forEach((arch) => { + // Update URL + let url = geth.platforms[platform][arch].download.url; + url = url.replace(/\d+\.\d+\.\d+-[a-z0-9]{8}/, `${latestGethVersion}-${hash}`); + geth.platforms[platform][arch].download.url = url; + + // Update bin name (path in archive) + let bin = geth.platforms[platform][arch].download.bin; + bin = bin.replace(/\d+\.\d+\.\d+-[a-z0-9]{8}/, `${latestGethVersion}-${hash}`); + geth.platforms[platform][arch].download.bin = bin; + + // Update expected sanity-command version output + geth.platforms[platform][arch].commands.sanity.output[1] = + String(latestGethVersion); + + // Update md5 checksum + blobs.forEach((blob) => { + if (String(blob.Name) === _.last(geth.platforms[platform][arch].download.url.split('/'))) { + const sum = new Buffer(blob.Properties[0]['Content-MD5'][0], 'base64'); + + geth.platforms[platform][arch].download.md5 = sum.toString('hex'); + } + }); + }); + }); + }) + // Update clientBinares.json + .then(() => { + fs.writeFile('./clientBinaries.json', JSON.stringify(newJson, null, 4)); + cb(); + }); + }); + } else return cb(); // Already up-to-date + }) + .catch(cb); +}); + + +gulp.task('download-signatures', (cb) => { + got('https://www.4byte.directory/api/v1/signatures/?page_size=20000&ordering=created_at', { + json: true + }) + .then((res) => { + if (res.statusCode !== 200) { + throw new Error(res.statusText); + } + + const signatures = {}; + + _.each(res.body.results, (e) => { + signatures[e.hex_signature] = signatures[e.hex_signature] || []; + signatures[e.hex_signature].push(e.text_signature); + }); + + fs.writeFileSync('interface/client/lib/signatures.js', `window.SIGNATURES = ${JSON.stringify(signatures, null, 4)};`); + + cb(); + }) + .catch(cb); +}); + + +gulp.task('update-i18n', (cb) => { + /** + * This script will update Mist's i18n files + * - adds missing english strings to all translations + * - removes obsolet keys from translations + */ + + const mistEN = require('../interface/i18n/mist.en.i18n.json'); // eslint-disable-line no-unused-vars + const appEN = require('../interface/i18n/app.en.i18n.json'); // eslint-disable-line no-unused-vars + + try { + ['mist', 'app'].forEach((mode) => { + const en = { + parent: 'en', + content: eval(`${mode}EN`) // eslint-disable-line no-eval + }; + + const files = fs.readdirSync('./interface/i18n'); + + files.forEach((file) => { + if (file.indexOf(`${mode}`) !== -1 && file.indexOf(`${mode}.en`) === -1) { + const langJson = require(`../interface/i18n/${file}`); // eslint-disable-line import/no-dynamic-require + const lang = { + parent: 'lang', + content: langJson + }; + let error; + + // remove unnecessary keys + error = compare([lang, en]); + if (error) { + error.forEach((diff) => { + if (diff.typeOfComparedParent === 'undefined') { + eval(`delete lang.content.${diff.parent.slice(diff.parent.indexOf('.') + 1)}`); // eslint-disable-line no-eval + } + }); + } + + // add missing keys + error = compare([en, lang]); + if (error) { + error.forEach((diff) => { + if (diff.typeOfComparedParent !== diff.typeOfParent && diff.parent !== 'en.mist.applicationMenu.view.languages' && diff.parent !== 'en.mist.applicationMenu.view.langCodes') { + eval(`lang.content.${diff.comparedParent.slice(diff.comparedParent.indexOf('.') + 1)} = en.content.${diff.parent.slice(diff.parent.indexOf('.') + 1)}`); // eslint-disable-line no-eval + } + }); + } + + fs.writeFileSync(`./interface/i18n/${file}`, JSON.stringify(lang.content, null, 4)); + } + }); + }); + } catch (e) { + console.log(e); + } finally { + cb(); // eslint-disable-line callback-return + } +}); diff --git a/gulpTasks/publishing.js b/gulpTasks/publishing.js new file mode 100644 index 0000000..005e51f --- /dev/null +++ b/gulpTasks/publishing.js @@ -0,0 +1,107 @@ +const _ = require('underscore'); +const Q = require('bluebird'); +const fs = require('fs'); +const githubUpload = Q.promisify(require('gh-release-assets')); +const got = require('got'); +const gulp = require('gulp'); +const options = require('../gulpfile.js').options; +const path = require('path'); +const shell = require('shelljs'); +const version = require('../package.json').version; + + +const checksums = []; +const type = options.type; + + +gulp.task('checksums', (cb) => { + const releasePath = `./dist_${type}/release`; + const files = fs.readdirSync(releasePath); + + let command; + let argument = ''; + + switch (process.platform) { + case 'darwin': + command = 'md5'; + break; + case 'win32': + command = 'certUtil -hashfile'; + argument = 'md5'; + break; + default: + command = 'md5sum'; + } + + files.forEach((file) => { + const sum = shell.exec(`${command} "${file}" ${argument}`, { + cwd: releasePath + }); + + if (sum.code !== 0) { + Error(`Error executing shasum: ${sum.stderr}`); + } + + // store checksums for 'upload-binaries' task + checksums.push(sum.stdout); + }); + + cb(); +}); + + +gulp.task('upload-binaries', (cb) => { + // if CI detected only upload if on master branch + if (process.env.CI && process.env.TRAVIS_BRANCH !== 'master') return; + + // personal access token (public_repo) must be set using travis' ENVs + const GITHUB_TOKEN = process.env.GITHUB_TOKEN; + + // query github releases + got(`https://api.github.com/repos/ethereum/mist/releases?access_token=${GITHUB_TOKEN}`, { json: true }) + // filter draft with current version's tag + .then((res) => { + const draft = res.body[_.indexOf(_.pluck(res.body, 'tag_name'), `v${version}`)]; + + if (draft === undefined) throw new Error(`Couldn't find github release draft for v${version} release tag`); + + return draft; + }) + // upload binaries from release folders if in draft mode + .then((draft) => { // eslint-disable-line consistent-return + if (draft.draft === true) { + const dir = `dist_${type}/release`; + const files = fs.readdirSync(dir); + const filePaths = _.map(files, (file) => { return path.join(dir, file); }); + + // check if draft already contains target binaries + // note: github replaces spaces in filenames with dots + const existingAssets = _.intersection(files.map((file) => { return file.replace(/\s/g, '.'); }), _.pluck(draft.assets, 'name')); + if (!_.isEmpty(existingAssets)) throw new Error(`Github release draft already contains assets (${existingAssets}); will not upload, please remove and trigger rebuild`); + + return githubUpload({ + url: `https://uploads.github.com/repos/ethereum/mist/releases/${draft.id}/assets{?name}`, + token: [GITHUB_TOKEN], + assets: filePaths + }).then((res) => { + console.log(`Successfully uploaded ${res} to v${version} release draft.`); + }) + // append checksums to draft text + .then(() => { + if (draft.body && checksums) { + got.patch(`https://api.github.com/repos/ethereum/mist/releases/${draft.id}?access_token=${GITHUB_TOKEN}`, { + body: JSON.stringify({ + body: `${draft.body}\n\n## Checksums\n\`\`\`\n${checksums.join('')}\`\`\`` + }) + }); + } + }); + } + }) + .catch((err) => { + console.log(err); + }) + .then(() => { + cb(); + }); +}); diff --git a/gulpTasks/testing.js b/gulpTasks/testing.js new file mode 100644 index 0000000..39efa8c --- /dev/null +++ b/gulpTasks/testing.js @@ -0,0 +1,14 @@ +const gulp = require('gulp'); +const mocha = require('gulp-spawn-mocha'); +const options = require('../gulpfile.js').options; + + +gulp.task('test', () => { + return gulp.src([ + `./tests/${options.type}/${options.test}.test.js` + ]).pipe(mocha({ + timeout: 60000, + ui: 'exports', + reporter: 'spec' + })); +}); diff --git a/gulpfile.js b/gulpfile.js index 4f96ef1..519e429 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,569 +1,84 @@ /* eslint-disable import/no-extraneous-dependencies, -no-console, strict, -prefer-spread, -arrow-body-style, -import/no-unresolved */ +prefer-spread +*/ 'use strict'; const _ = require('underscore'); -const path = require('path'); const gulp = require('gulp'); -const exec = require('child_process').exec; -const del = require('del'); -const runSeq = require('run-sequence'); -const merge = require('merge-stream'); -const flatten = require('gulp-flatten'); -const shell = require('shelljs'); -const mocha = require('gulp-spawn-mocha'); const minimist = require('minimist'); -const fs = require('fs'); -const got = require('got'); -const Q = require('bluebird'); -const githubUpload = Q.promisify(require('gh-release-assets')); -const cmp = require('semver-compare'); -const parseJson = require('xml2js').parseString; +const runSeq = require('run-sequence'); -const options = minimist(process.argv.slice(2), { - string: ['platform', 'walletSource'], +// parse commandline arguments +const args = process.argv.slice(2); +const platforms = (process.platform === 'darwin') ? ['mac', 'linux', 'win'] : ['linux', 'win']; +const options = minimist(args, { + string: ['walletSource', 'test'], + boolean: _.flatten(['wallet', platforms]), default: { - platform: 'all', + wallet: false, walletSource: 'master', + test: 'basic', }, }); -if (options.platform.indexOf(',') !== -1) { - options.platform = options.platform.replace(/ +/g, '').split(','); -} else { - options.platform = options.platform.split(' '); -} - -// CONFIG -let type = 'mist'; -let applicationName = 'Mist'; -const electronVersion = require('electron/package.json').version; -const packJson = require('./package.json'); - -const version = packJson.version; - -const osArchList = [ - 'mac-x64', - 'linux-x64', - 'linux-ia32', - 'win-x64', - 'win-ia32', -]; - +// echo version info and usage hints +console.log('Mist version:', require('./package.json').version); +console.log('Electron version:', require('electron/package.json').version); -console.log('You can select a platform like: --platform '); -console.log('Mist version:', version); -console.log('Electron version:', electronVersion); - -if (_.contains(options.platform, 'all')) { - options.platform = ['win', 'linux', 'mac']; +if (_.isEmpty(_.intersection(args, ['--wallet']))) { + console.log('Many gulp tasks can be run in wallet mode using: --wallet'); } -console.log('Selected platform:', options.platform); - - -function platformIsActive(osArch) { - for (const p of options.platform) { - if (osArch.indexOf(p) >= 0) { - return true; - } - } - return false; +const platformFlags = platforms.map((platform) => { return `--${platform}`; }); +if (_.isEmpty(_.intersection(args, platformFlags))) { + console.log(`To specify a platform (default: all) use: ${platformFlags.join(' ')}`); + _.each(platforms, (platform) => { options[platform] = true; }); // activate all platform flags } -// / -------------------------------------------------------------- - -// TASKS -gulp.task('set-variables-mist', () => { - type = 'mist'; - applicationName = 'Mist'; -}); -gulp.task('set-variables-wallet', () => { - type = 'wallet'; - applicationName = 'Ethereum Wallet'; -}); - - -gulp.task('clean:dist', (cb) => { - return del([ - `./dist_${type}/**/*`, - './meteor-dapp-wallet', - ], cb); -}); - - -// BUNLDE PROCESS - -gulp.task('copy-app-source-files', ['clean:dist'], () => { - return gulp.src([ - './tests/**/*.*', - '!./tests/wallet/*.*', - `./icons/${type}/*`, - './modules/**/**/**/*', - './sounds/*', - './*.js', - './clientBinaries.json', - '!gulpfile.js', - ], { - base: './' - }) - .pipe(gulp.dest(`./dist_${type}/app`)); -}); - - -gulp.task('copy-app-folder-files', ['copy-app-source-files'], (done) => { - const ret = shell.exec( - `cp -a ${__dirname}/node_modules ${__dirname}/dist_${type}/app/node_modules` - ); - - if (ret.code !== 0) { - console.error('Error symlinking node_modules'); - - return done(ret.stderr); - } - - return done(); -}); - - -gulp.task('copy-build-folder-files', ['clean:dist', 'copy-app-folder-files'], () => { - return gulp.src([ - `./icons/${type}/*`, - './interface/public/images/dmg-background.jpg', - ], { - base: './' - }) - .pipe(flatten()) - .pipe(gulp.dest(`./dist_${type}/build`)); -}); - - -gulp.task('copy-node-folder-files', ['clean:dist'], () => { - const streams = []; - - _.each(osArchList, (osArch) => { - if (platformIsActive(osArch)) { - // copy eth node binaries - streams.push(gulp.src([ - `./nodes/eth/${osArch}/*`, - ]) - .pipe(gulp.dest(`./dist_${type}/app/nodes/eth/${osArch}`))); - } - }); - - return merge.apply(null, streams); -}); - - -gulp.task('copy-files', [ - 'clean:dist', - 'copy-app-folder-files', - 'copy-build-folder-files', - 'copy-node-folder-files', -]); - - -gulp.task('switch-production', ['copy-files'], (cb) => { - fs.writeFileSync(`${__dirname}/dist_${type}/app/config.json`, JSON.stringify({ - production: true, - mode: type, - })); - - cb(); -}); - - -gulp.task('bundling-interface', ['switch-production'], (cb) => { - if (type === 'mist') { - exec(`cd interface && meteor-build-client ../dist_${type}/app/interface -p ""`, (err, stdout) => { - console.log(stdout); - - cb(err); - }); - } - - if (type === 'wallet') { - if (options.walletSource === 'local') { - console.log('Use local wallet at ../meteor-dapp-wallet/app'); - exec(`cd interface/ && meteor-build-client ../dist_${type}/app/interface/ -p "" &&` + - `cd ../../meteor-dapp-wallet/app && meteor-build-client ../../mist/dist_${type}/app/interface/wallet -p ""`, (err, stdout) => { - console.log(stdout); - - cb(err); - }); - } else { - console.log(`Pulling https://github.com/ethereum/meteor-dapp-wallet/tree/${options.walletSource} "${options.walletSource}" branch...`); - exec(`cd interface/ && meteor-build-client ../dist_${type}/app/interface/ -p "" &&` + - `cd ../dist_${type}/ && git clone --depth 1 https://github.com/ethereum/meteor-dapp-wallet.git && cd meteor-dapp-wallet/app && meteor-build-client ../../app/interface/wallet -p "" && cd ../../ && rm -rf meteor-dapp-wallet`, (err, stdout) => { - console.log(stdout); - - cb(err); - }); - } - } -}); - - -// needs to be copied, so the backend can use it -gulp.task('copy-i18n', ['bundling-interface'], () => { - return gulp.src([ - './interface/i18n/*.*', - './interface/project-tap.i18n', - ], { - base: './' - }) - .pipe(gulp.dest(`./dist_${type}/app`)); -}); - - -gulp.task('build-dist', ['copy-i18n'], (cb) => { - console.log('Bundling platforms: ', options.platform); +// prepare global variables (shared with other gulp task files) +options.type = (options.wallet) ? 'wallet' : 'mist'; +options.platforms = platforms; +options.activePlatforms = _.keys(_.pick(_.pick(options, platforms), (key) => { return key; })); - const appPackageJson = _.extend({}, packJson, { - name: applicationName.replace(/\s/, ''), - productName: applicationName, - description: applicationName, - homepage: 'https://github.com/ethereum/mist', - build: { - appId: `com.ethereum.${type}`, - category: 'public.app-category.productivity', - asar: true, - files: [ - '**/*', - '!nodes', - 'build-dist.js', - ], - extraFiles: [ - 'nodes/eth/${os}-${arch}', // eslint-disable-line no-template-curly-in-string - ], - linux: { - target: [ - 'zip', - 'deb', - ], - }, - win: { - target: [ - 'zip', - //'squirrel', - ], - }, - dmg: { - background: '../build/dmg-background.jpg', - iconSize: 128, - contents: [{ - x: 441, - y: 448, - type: 'link', - path: '/Applications', - }, - { - x: 441, - y: 142, - type: 'file', - } - ], - }, - }, - directories: { - buildResources: '../build', - app: '.', - output: '../dist', - }, - }); +exports.options = options; - fs.writeFileSync( - path.join(__dirname, `dist_${type}`, 'app', 'package.json'), - JSON.stringify(appPackageJson, null, 2), - 'utf-8' - ); - // Copy build script - shell.cp( - path.join(__dirname, 'scripts', 'build-dist.js'), - path.join(__dirname, `dist_${type}`, 'app') - ); +// import gulp tasks +require('require-dir')('./gulpTasks'); - // run build script - const oses = `--${options.platform.join(' --')}`; - const ret = shell.exec(`./build-dist.js --type ${type} ${oses}`, { - cwd: path.join(__dirname, `dist_${type}`, 'app'), - }); +// tasks +gulp.task('default', ['buildQueue']); - if (ret.code !== 0) { - console.error(ret.stdout); - console.error(ret.stderr); - return cb(new Error('Error building distributables')); - } +gulp.task('buildQueue', (cb) => { + const tasks = []; - console.log(ret.stdout); + tasks.push('clean-dist'); + tasks.push('copy-app-source-files'); + tasks.push('copy-build-folder-files'); + tasks.push('switch-production'); + tasks.push('bundling-interface'); + tasks.push('copy-i18n'); + tasks.push('build-dist'); + tasks.push('release-dist'); + if (options.win) tasks.push('build-nsis'); - return cb(); + runSeq.apply(null, _.flatten([tasks, cb])); }); -gulp.task('release-dist', ['build-dist'], (done) => { - const distPath = path.join(__dirname, `dist_${type}`, 'dist'); - const releasePath = path.join(__dirname, `dist_${type}`, 'release'); +gulp.task('uploadQueue', (cb) => { + const tasks = []; - shell.rm('-rf', releasePath); - shell.mkdir('-p', releasePath); + tasks.push('checksums'); + tasks.push('upload-binaries'); - const appNameHypen = applicationName.replace(/\s/, '-'); - const appNameNoSpace = applicationName.replace(/\s/, ''); - const versionDashed = version.replace(/\./g, '-'); - - const cp = (inputPath, outputPath) => { - shell.cp(path.join(distPath, inputPath), path.join(releasePath, outputPath)); - }; - - _.each(osArchList, (osArch) => { - if (platformIsActive(osArch)) { - switch (osArch) { // eslint-disable-line default-case - case 'win-ia32': - cp(`${applicationName}-${version}-ia32-win.zip`, `${appNameHypen}-win32-${versionDashed}.zip`); - break; - case 'win-x64': - cp(`${applicationName}-${version}-win.zip`, `${appNameHypen}-win64-${versionDashed}.zip`); - break; - case 'mac-x64': - cp(path.join('mac', `${applicationName}-${version}.dmg`), `${appNameHypen}-macosx-${versionDashed}.dmg`); - break; - case 'linux-ia32': - cp(`${appNameNoSpace}_${version}_i386.deb`, `${appNameHypen}-linux32-${versionDashed}.deb`); - cp(`${appNameNoSpace}-${version}-ia32.zip`, `${appNameHypen}-linux32-${versionDashed}.zip`); - break; - case 'linux-x64': - cp(`${appNameNoSpace}_${version}_amd64.deb`, `${appNameHypen}-linux64-${versionDashed}.deb`); - cp(`${appNameNoSpace}-${version}.zip`, `${appNameHypen}-linux64-${versionDashed}.zip`); - break; - } - } - - if (platformIsActive('win') && type === 'mist') { - runSeq('build-nsis'); - } - }); - - done(); -}); - -gulp.task('upload-binaries', () => { - // token must be set using travis' ENVs - const GITHUB_TOKEN = process.env.GITHUB_TOKEN; - - // query github releases - return got(`https://api.github.com/repos/ethereum/mist/releases?access_token=${GITHUB_TOKEN}`, { - json: true, - }) - // filter draft with current version's tag - .then((res) => { - const draft = res.body[_.indexOf(_.pluck(res.body, 'tag_name'), `v${version}`)]; - - if (draft === undefined) throw new Error(`Couldn't find github release draft for v${version} release tag`); - - return draft; - }) - // upload binaries from release folders - .then((draft) => { - const dir = `dist_${type}/release`; - const files = fs.readdirSync(dir); - const filePaths = _.map(files, (file) => { return path.join(dir, file); }); - - // check if draft already contains target binaries - const existingAssets = _.intersection(files, _.pluck(draft.assets, 'name')); - if (!_.isEmpty(existingAssets)) throw new Error(`Github release draft already contains assets (${existingAssets}); will not upload, please remove and trigger rebuild`); - - return githubUpload({ - url: `https://uploads.github.com/repos/ethereum/mist/releases/${draft.id}/assets{?name}`, - token: [GITHUB_TOKEN], - assets: filePaths, - }).then((res) => { - console.log(`Successfully uploaded ${res} to v${version} release draft.`); - }); - }) - .catch((err) => { - console.log(err); - }); -}); - -gulp.task('get-release-checksums', (done) => { - const releasePath = `./dist_${type}/release`; - - const files = fs.readdirSync(releasePath); - - for (const file of files) { - const sha = shell.exec(`shasum -a 256 "${file}"`, { - cwd: releasePath - }); - - if (sha.code !== 0) { - return done(new Error(`Error executing shasum: ${sha.stderr}`)); - } - } - - return done(); -}); - -gulp.task('update-nodes', (cb) => { - const clientBinaries = require('./clientBinaries.json'); // eslint-disable-line global-require - const clientBinariesGeth = clientBinaries.clients.Geth; - const localGethVersion = clientBinariesGeth.version; - const newJson = clientBinaries; - const geth = newJson.clients.Geth; - - // Query latest geth version - got('https://api.github.com/repos/ethereum/go-ethereum/releases/latest', { json: true }) - .then((response) => { - return response.body.tag_name; - }) - // Return tag name (e.g. 'v1.5.0') - .then((tagName) => { - const latestGethVersion = tagName.match(/\d+\.\d+\.\d+/)[0]; - - // Compare to current geth version in clientBinaries.json - if (cmp(latestGethVersion, localGethVersion)) { - geth.version = latestGethVersion; - - // Query commit hash (first 8 characters) - got(`https://api.github.com/repos/ethereum/go-ethereum/commits/${tagName}`, { json: true }) - .then((response) => { - return String(response.body.sha).substr(0, 8); - }) - .then((hash) => { - let blobs; // geth blobs - - // Query Azure assets for md5 hashes - got('https://gethstore.blob.core.windows.net/builds?restype=container&comp=list', { - xml: true, - }) - .then((response) => { - parseJson(response.body, (err, data) => { - blobs = data.EnumerationResults.Blobs[0].Blob; - }); - - // For each platform/arch in clientBinaries.json - _.keys(geth.platforms).forEach((platform) => { - _.keys(geth.platforms[platform]).forEach((arch) => { - // Update URL - let url = geth.platforms[platform][arch].download.url; - url = url.replace(/\d+\.\d+\.\d+-[a-z0-9]{8}/, `${latestGethVersion}-${hash}`); - geth.platforms[platform][arch].download.url = url; - - // Update bin name (path in archive) - let bin = geth.platforms[platform][arch].download.bin; - bin = bin.replace(/\d+\.\d+\.\d+-[a-z0-9]{8}/, `${latestGethVersion}-${hash}`); - geth.platforms[platform][arch].download.bin = bin; - - // Update expected sanity-command version output - geth.platforms[platform][arch].commands.sanity.output[1] = - String(latestGethVersion); - - // Update md5 checksum - blobs.forEach((blob) => { - if (String(blob.Name) === _.last(geth.platforms[platform][arch].download.url.split('/'))) { - const sum = new Buffer(blob.Properties[0]['Content-MD5'][0], 'base64'); - - geth.platforms[platform][arch].download.md5 = sum.toString('hex'); - } - }); - }); - }); - }) - // Update clientBinares.json - .then(() => { - fs.writeFile('./clientBinaries.json', JSON.stringify(newJson, null, 4)); - cb(); - }); - }); - } else cb(); // Already up-to-date - }) - .catch(cb); -}); - -gulp.task('download-signatures', (cb) => { - got('https://www.4byte.directory/api/v1/signatures/?page_size=20000&ordering=created_at', { - json: true, - }) - .then((res) => { - if (res.statusCode !== 200) { - throw new Error(res.statusText); - } - - const signatures = {}; - - _.each(res.body.results, (e) => { - signatures[e.hex_signature] = signatures[e.hex_signature] || []; - signatures[e.hex_signature].push(e.text_signature); - }); - - fs.writeFileSync('interface/client/lib/signatures.js', `window.SIGNATURES = ${JSON.stringify(signatures, null, 4)};`); - - cb(); - }) - .catch(cb); -}); - -gulp.task('taskQueue', ['release-dist'], (cb) => { - if (process.env.TRAVIS_BRANCH === 'master') { - runSeq('upload-binaries', cb); - } -}); - -// MIST task -gulp.task('mist', (cb) => { - runSeq('set-variables-mist', 'taskQueue', cb); -}); - -// WALLET task -gulp.task('wallet', (cb) => { - runSeq('set-variables-wallet', 'taskQueue', cb); -}); - -// WALLET task -gulp.task('mist-checksums', (cb) => { - runSeq('set-variables-mist', 'get-release-checksums', cb); -}); -gulp.task('wallet-checksums', (cb) => { - runSeq('set-variables-wallet', 'get-release-checksums', cb); + runSeq.apply(null, _.flatten([tasks, cb])); }); -gulp.task('build-nsis', (cb) => { - const versionParts = version.split('.'); - const versionString = `-DVERSIONMAJOR=${versionParts[0]} -DVERSIONMINOR=${versionParts[1]} -DVERSIONBUILD=${versionParts[2]}`; - const cmdString = `makensis -V3 ${versionString} scripts/windows-installer.nsi`; - console.log(cmdString); - shell.exec(cmdString, cb); -}); - - -const testApp = (app) => { - return gulp.src([ - `./tests/${app}/*.test.js`, - ]).pipe(mocha({ - timeout: 60000, - ui: 'exports', - reporter: 'spec', - })); -}; - -gulp.task('test-wallet', () => { - testApp('wallet'); -}); - -gulp.task('test-mist', () => { - testApp('mist'); -}); - - -gulp.task('default', ['mist']); diff --git a/interface/.meteor/packages b/interface/.meteor/packages index c97f960..2b36f0b 100644 --- a/interface/.meteor/packages +++ b/interface/.meteor/packages @@ -14,7 +14,6 @@ frozeman:template-var frozeman:reactive-timer frozeman:storage frozeman:global-notifications -numeral:numeral reactive-var@1.0.11 sacha:spin chuangbo:cookie @@ -42,3 +41,6 @@ standard-minifier-css@1.3.2 standard-minifier-js@1.2.1 tap:i18n-bundler shell-server +ecmascript +numeral:numeral +numeral:languages diff --git a/interface/.meteor/versions b/interface/.meteor/versions index 0870d46..cbff712 100644 --- a/interface/.meteor/versions +++ b/interface/.meteor/versions @@ -71,6 +71,7 @@ mongo@1.1.14 mongo-id@1.0.6 mrt:jquery-ui-sortable@1.10.3 npm-mongo@2.2.11_2 +numeral:languages@1.5.3 numeral:numeral@1.5.3_1 observe-sequence@1.0.14 ordered-dict@1.0.9 diff --git a/interface/client/appStart.js b/interface/client/appStart.js index 3f28f49..ad9bfbf 100644 --- a/interface/client/appStart.js +++ b/interface/client/appStart.js @@ -1,27 +1,21 @@ - - -// STOP here if not MAIN WINDOW -if(location.hash) - return; - /** The init function of Mist @method mistInit */ -mistInit = function(){ +mistInit = function () { console.info('Initialise Mist Interface'); EthBlocks.init(); - Tabs.onceSynced.then(function() { - if (0 <= location.search.indexOf('reset-tabs')) { + Tabs.onceSynced.then(function () { + if (location.search.indexOf('reset-tabs') >= 0) { console.info('Resetting UI tabs'); - + Tabs.remove({}); } - if(!Tabs.findOne('browser')) { + if (!Tabs.findOne('browser')) { console.debug('Insert tabs'); Tabs.insert({ @@ -30,61 +24,76 @@ mistInit = function(){ redirect: 'https://ethereum.org', position: 0 }); + } else { + Tabs.upsert( + { _id: 'browser' }, + { + $set: { position: 0 } + } + ); } // overwrite wallet on start again, but use $set to dont remove titles - Tabs.upsert({_id: 'wallet'}, {$set: { - url: 'https://wallet.ethereum.org', - redirect: 'https://wallet.ethereum.org', - position: 1, - permissions: { - admin: true + Tabs.upsert( + { _id: 'wallet' }, + { + $set: { + url: 'https://wallet.ethereum.org', + redirect: 'https://wallet.ethereum.org', + position: 1, + permissions: { + admin: true + } } - } - }); + }); // Sets browser as default tab if: // 1) there's no record of selected tab // 2) data is corrupted (no saved tab matches localstore) - if(!LocalStore.get('selectedTab') || !Tabs.findOne(LocalStore.get('selectedTab'))){ + if (!LocalStore.get('selectedTab') || !Tabs.findOne(LocalStore.get('selectedTab'))) { LocalStore.set('selectedTab', 'wallet'); } }); }; -Meteor.startup(function(){ +Meteor.startup(function () { console.info('Meteor starting up...'); - EthAccounts.init(); - mistInit(); + if (!location.hash) { // Main window + EthAccounts.init(); + mistInit(); + } console.debug('Setting language'); // SET default language - if(Cookie.get('TAPi18next')) { + if (Cookie.get('TAPi18next')) { TAPi18n.setLanguage(Cookie.get('TAPi18next')); } else { - var userLang = navigator.language || navigator.userLanguage, - availLang = TAPi18n.getLanguages(); + const userLang = navigator.language || navigator.userLanguage; + const availLang = TAPi18n.getLanguages(); // set default language if (_.isObject(availLang) && availLang[userLang]) { TAPi18n.setLanguage(userLang); - } else if (_.isObject(availLang) && availLang[userLang.substr(0,2)]) { - TAPi18n.setLanguage(userLang.substr(0,2)); + } else if (_.isObject(availLang) && availLang[userLang.substr(0, 2)]) { + TAPi18n.setLanguage(userLang.substr(0, 2)); } else { TAPi18n.setLanguage('en'); } } // change moment and numeral language, when language changes - Tracker.autorun(function(){ - if(_.isString(TAPi18n.getLanguage())) { - var lang = TAPi18n.getLanguage().substr(0,2); + Tracker.autorun(function () { + if (_.isString(TAPi18n.getLanguage())) { + const lang = TAPi18n.getLanguage().substr(0, 2); moment.locale(lang); - numeral.language(lang); + try { + numeral.language(lang); + } catch (err) { + console.error(`numeral.js couldn't set number formating: ${err.message}`); + } EthTools.setLocale(lang); } }); }); - diff --git a/interface/client/collections.js b/interface/client/collections.js index b854505..a58ed19 100644 --- a/interface/client/collections.js +++ b/interface/client/collections.js @@ -6,9 +6,9 @@ // BROWSER RELATED // Contains the accounts -Tabs = new Mongo.Collection('tabs', {connection: null}); -LastVisitedPages = new Mongo.Collection('last-visted-pages', {connection: null}); -History = new Mongo.Collection('history', {connection: null}); +Tabs = new Mongo.Collection('tabs', { connection: null }); +LastVisitedPages = new Mongo.Collection('last-visted-pages', { connection: null }); +History = new Mongo.Collection('history', { connection: null }); // Sync collection from and to the backend loki.js if (typeof window.dbSync !== 'undefined') { diff --git a/interface/client/lib/ethereum/1_web3js_init.js b/interface/client/lib/ethereum/1_web3js_init.js index fc80d40..270935e 100644 --- a/interface/client/lib/ethereum/1_web3js_init.js +++ b/interface/client/lib/ethereum/1_web3js_init.js @@ -1,10 +1,10 @@ // set providor -if(typeof web3 !== 'undefined') { - console.info('Web3 already initialized, re-using provider.'); +if (typeof web3 !== 'undefined') { + console.info('Web3 already initialized, re-using provider.'); - web3 = new Web3(web3.currentProvider); + web3 = new Web3(web3.currentProvider); } else { - console.info('Web3 not yet initialized, doing so now with HttpProvider.'); + console.info('Web3 not yet initialized, doing so now with HttpProvider.'); - web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); + web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:8545')); } diff --git a/interface/client/lib/helpers/helperFunctions.js b/interface/client/lib/helpers/helperFunctions.js index bb6dbb3..aa9e662 100644 --- a/interface/client/lib/helpers/helperFunctions.js +++ b/interface/client/lib/helpers/helperFunctions.js @@ -39,8 +39,8 @@ Get the webview from either and ID, or the string "browser" @method getWebview @param {String} id The Id of a tab or the string "browser" */ -Helpers.getWebview = function(id){ - return $('webview[data-id="'+ id +'"]')[0]; +Helpers.getWebview = function (id) { + return $('webview[data-id="' + id + '"]')[0]; }; /** @@ -50,22 +50,24 @@ Get tab by url and return the id @param {String} url @return {String} id */ -Helpers.getTabIdByUrl= function(url, returnEmpty){ +Helpers.getTabIdByUrl = function (url, returnEmpty) { var tabs = Tabs.find().fetch(); url = Helpers.sanitizeUrl(url); - var foundTab = _.find(tabs, function(tab){ - if(tab._id === 'browser' || !tab.url) - return false; - var tabOrigin = new URL(tab.url).origin; - return (url && new URL(url).origin.indexOf(tabOrigin) === 0); - }); + var foundTab = _.find(tabs, function (tab) { + if (tab._id === 'browser' || !tab.url) { + return false; + } + var tabOrigin = new URL(tab.url).origin; + return (url && new URL(url).origin.indexOf(tabOrigin) === 0); + }); // switch tab to browser - if(foundTab) + if (foundTab) { foundTab = foundTab._id; - else + } else { foundTab = 'browser'; + } return foundTab; }; @@ -76,10 +78,11 @@ Format Urls, e.g add a default protocol if on is missing. @method formatUrl @param {String} url **/ -Helpers.formatUrl = function(url){ +Helpers.formatUrl = function (url) { // add http:// if no protocol is present - if(url && url.indexOf('://') === -1) - url = 'http://'+ url; + if (url && url.indexOf('://') === -1) { + url = 'http://' + url; + } return url; }; @@ -90,13 +93,13 @@ Sanatizes URLs to prevent phishing and XSS attacks @method sanitizeUrl @param {String} url **/ -Helpers.sanitizeUrl = function(url, returnEmptyURL){ +Helpers.sanitizeUrl = function (url, returnEmptyURL) { url = String(url); url = url.replace(/[\t\n\r\s]+/g, ''); url = url.replace(/^[:\/]{1,3}/i, 'http://'); - if(returnEmptyURL && /^(?:file|javascript|data):/i.test(url)) { + if (returnEmptyURL && /^(?:file|javascript|data):/i.test(url)) { url = false; } @@ -128,7 +131,7 @@ Helpers.generateBreadcrumb = function (url) { return el === ''; }); - return new Spacebars.SafeString(filteredUrl.protocol +'//'+ _.flatten(['' + filteredUrl.host + ' ', pathname]).join(' ▸ ')); + return new Spacebars.SafeString(filteredUrl.protocol + '//' + _.flatten(['' + filteredUrl.host + ' ', pathname]).join(' ▸ ')); }; /** @@ -139,7 +142,7 @@ Clear localStorage Helpers.getLocalStorageSize = function () { var size = 0; - if(localStorage) { + if (localStorage) { _.each(Object.keys(localStorage), function (key) { size += localStorage[key].length * 2 / 1024 / 1024; }); @@ -188,7 +191,9 @@ Helpers.selectTabWithOffset = function (offset) { currentTabIndex = tabList.indexOf(LocalStore.get('selectedTab')); newTabIndex = (currentTabIndex + offset) % tabList.length; - if (newTabIndex < 0) newTabIndex = tabList.length - 1; + if (newTabIndex < 0) { + newTabIndex = tabList.length - 1; + } LocalStore.set('selectedTab', tabList[newTabIndex]); }; diff --git a/interface/client/lib/helpers/templateHelpers.js b/interface/client/lib/helpers/templateHelpers.js index ac8137c..98d7ae7 100644 --- a/interface/client/lib/helpers/templateHelpers.js +++ b/interface/client/lib/helpers/templateHelpers.js @@ -16,7 +16,7 @@ A simple template helper to log objects in the console. @method (debug) **/ -Template.registerHelper('debug', function(object){ +Template.registerHelper('debug', function (object) { console.log(object); }); @@ -25,7 +25,7 @@ Returns the current block @method (CurrentBlock) **/ -Template.registerHelper('CurrentBlock', function(){ +Template.registerHelper('CurrentBlock', function () { return EthBlocks.latest; }); @@ -35,7 +35,7 @@ Return the dirname. @method (dirname) **/ -Template.registerHelper('dirname', function(){ +Template.registerHelper('dirname', function () { return window.dirname; }); @@ -44,7 +44,7 @@ Return the Mist API. @method (mist) **/ -Template.registerHelper('mist', function(){ +Template.registerHelper('mist', function () { return window.mist; }); @@ -54,7 +54,7 @@ Return the app mode. @method (mode) **/ -Template.registerHelper('mode', function(){ +Template.registerHelper('mode', function () { return window.mistMode; }); @@ -63,7 +63,7 @@ Return the friendly app name. @method (appName) **/ -Template.registerHelper('appName', function(){ +Template.registerHelper('appName', function () { return window.mistMode === 'mist' ? 'Mist' : 'Ethereum Wallet'; }); @@ -72,8 +72,8 @@ Return the app icon path. @method (iconPath) **/ -Template.registerHelper('appIconPath', function(){ - return 'file://'+ window.dirname +'/icons/'+ window.mistMode +'/icon2x.png'; +Template.registerHelper('appIconPath', function () { + return 'file://' + window.dirname + '/icons/' + window.mistMode + '/icon2x.png'; }); /** @@ -81,7 +81,7 @@ Get the current user agent @method (useragent) **/ -Template.registerHelper('useragent', function(){ +Template.registerHelper('useragent', function () { return navigator.userAgent + ' Ethereum ' + (window.mistMode === 'mist' ? 'Mist' : 'Wallet'); }); @@ -90,8 +90,8 @@ Get all accounts @method (accounts) **/ -Template.registerHelper('accounts', function(identity){ - return EthAccounts.find({}, {sort: {name: 1}}); +Template.registerHelper('accounts', function (identity) { + return EthAccounts.find({}, { sort: { name: 1 } }); }); /** @@ -107,16 +107,18 @@ Return the right wallet icon @method (walletIcon) **/ -Template.registerHelper('walletIcon', function(){ +Template.registerHelper('walletIcon', function () { var icon = ''; - if(this.type === 'wallet') { - if(Helpers.isWatchOnly(this._id)) + if (this.type === 'wallet') { + if (Helpers.isWatchOnly(this._id)) { icon = ''; - else + } else { icon = ''; - } else if(this.type === 'account') + } + } else if (this.type === 'account') { icon = ''; + } return new Spacebars.SafeString(icon); }); @@ -128,12 +130,13 @@ Get the account name or display the address @method (accountNameOrAddress) @param {String} address */ -Template.registerHelper('accountNameOrAddress', function(address){ - var account = EthAccounts.findOne({address: address}); - if(account) +Template.registerHelper('accountNameOrAddress', function (address) { + var account = EthAccounts.findOne({ address: address }); + if (account) { return account.name; - else + } else { return address; + } }); /** @@ -176,7 +179,3 @@ Formats a number. Template.registerHelper('formatBalance', Helpers.formatBalance); - - - - diff --git a/interface/client/lib/signatures.js b/interface/client/lib/signatures.js index 30234c6..df02e99 100644 --- a/interface/client/lib/signatures.js +++ b/interface/client/lib/signatures.js @@ -10187,4 +10187,4 @@ window.SIGNATURES = { "0x4e077f2a": [ "addGasEther()" ] -}; \ No newline at end of file +}; diff --git a/interface/client/lib/thirdParty.js b/interface/client/lib/thirdParty.js index 225b818..f35a8d9 100644 --- a/interface/client/lib/thirdParty.js +++ b/interface/client/lib/thirdParty.js @@ -16,4 +16,4 @@ Meteor.Spinner.options = { zIndex: 2e9, // The z-index (defaults to 2000000000) top: '50%', // Top position relative to parent left: '50%' // Left position relative to parent -}; \ No newline at end of file +}; diff --git a/interface/client/mistAPIBackend.js b/interface/client/mistAPIBackend.js index d40f76b..7a0f057 100644 --- a/interface/client/mistAPIBackend.js +++ b/interface/client/mistAPIBackend.js @@ -38,7 +38,7 @@ mistAPIBackend = function (event) { // console.trace('mistAPIBackend event', event); if (event.channel === 'setWebviewId') { - Tabs.update(template.data._id, { $set: { webviewId: webview.getWebContents().id }}); + Tabs.update(template.data._id, { $set: { webviewId: webview.getWebContents().id } }); } // Send TEST DATA diff --git a/interface/client/styles/animations.import.less b/interface/client/styles/animations.import.less index 83e8766..c60b81f 100644 --- a/interface/client/styles/animations.import.less +++ b/interface/client/styles/animations.import.less @@ -20,22 +20,12 @@ aside.sidebar { // ANIMATION transition: max-width @animationSpeed; } - - } - } - - &.full-tabs { - nav > ul > li:not(.selected) { - transform: translate3d(-100%,0,0); - } - nav > ul > li.selected button.slide-out { - transition: none; } } } -// Browser bar +// Browser bar .browser-bar { .url-input { transition: opacity 0.1s; @@ -52,7 +42,7 @@ aside.sidebar { transition-timing-function: ease-out; transition-timing-function: cubic-bezier(0.000, 1.125, 0.335, 1.650); height: @gridHeight*1.5; - + &>* { position: absolute; left: @gridWidth*2; @@ -61,11 +51,11 @@ aside.sidebar { } .password-repeat { - transform: rotateX( 180deg ); + transform: rotateX( 180deg ); } - + &.repeat-field { - transform: rotateX( 180deg ); + transform: rotateX( 180deg ); } } -} \ No newline at end of file +} diff --git a/interface/client/styles/browserbar.import.less b/interface/client/styles/browserbar.import.less index 4eef85d..4b35d1a 100644 --- a/interface/client/styles/browserbar.import.less +++ b/interface/client/styles/browserbar.import.less @@ -4,10 +4,10 @@ top: @gridHeight * 1.5; left: @widthSideBar; right: 0; - z-index: 100; + z-index: 3; height: @gridHeight * 2; font-family: @sourceSansPro; - + button.icon { padding: @gridHeight/4 @gridWidth/4; @@ -15,7 +15,7 @@ border: 0; color: @colorLinkFocus; } - } + } .app-bar { position: relative; @@ -26,7 +26,7 @@ background: #fff;//#F6F6F6; // border-radius: 3px; // box-shadow: 0 2px 0 rgba(0, 0, 0, 0.25); - height: @gridHeight*1.7; + height: @gridHeight*1.7; overflow: hidden; // ANIMATION @@ -45,7 +45,7 @@ line-height: 21px; white-space: nowrap; margin-top: -1px; - + &.has-icon { padding-left: @gridWidth * 0.84; } @@ -73,7 +73,7 @@ float: left; margin-right: 8px; text-transform: uppercase; - } + } .connect-button, .dapp-info span { line-height: 21px; display: inline-block; @@ -144,17 +144,17 @@ span { color: @colorLinkBlur; - } + } } &:hover > .url-input { opacity: 1; - transition: opacity 0.2s ease-in-out 0.1s; + transition: opacity 0.2s ease-in-out 0.1s; } &:hover > .url-breadcrumb { opacity: 0; - word-spacing: -3px; + word-spacing: -3px; transition: opacity 0.2s ease-in-out 0.1s, word-spacing 0.2s ease-in-out; } } @@ -216,7 +216,7 @@ button.cancel { font-weight: 300; - } + } } } } @@ -235,7 +235,7 @@ margin-left: @gridWidth /2; margin-right: @gridWidth /2; } - + } } .linux .browser-bar { @@ -252,7 +252,7 @@ } .url-breadcrumb { visibility: hidden; - word-spacing: -3px; + word-spacing: -3px; transition: word-spacing 0.5s ease-in-out; } .reload { @@ -262,12 +262,12 @@ .color-pulse { animation-name: color-pulse; - animation-duration: 0.5s; + animation-duration: 0.5s; animation-iteration-count: infinite; - animation-timing-function: linear; - background: linear-gradient(to left, + animation-timing-function: linear; + background: linear-gradient(to left, darken(@colorLinkActive, 30%) 0, - saturate(@colorLinkActive, 100%), + saturate(@colorLinkActive, 100%), darken(@colorLinkActive, 30%)) !important; background-size: 200px !important; background-repeat: repeat-x; @@ -278,72 +278,72 @@ @keyframes color-pulse { 0% { transform: scale(0.992) translateZ(0); - opacity: 0.7; + opacity: 0.7; background-position-x:0px; } 50% { transform: scale(1) translateZ(0); - opacity: 1; - } + opacity: 1; + } 100% { transform: scale(0.992) translateZ(0); - opacity: 0.7; + opacity: 0.7; background-position-x:-200px; - } + } } @colorBlockchain: #c3d825; .color-ping { animation-name: color-ping; - animation-duration: 12s; + animation-duration: 12s; animation-iteration-count: infinite; - animation-timing-function: ease-out; - background-image: linear-gradient(to top, - saturate(@colorLinkActive, 100%), + animation-timing-function: ease-out; + background-image: linear-gradient(to top, + saturate(@colorLinkActive, 100%), @colorGrayDark) !important; background-size: 150px 150px; background-position-y:0; - + -webkit-background-clip: text !important; -webkit-text-fill-color: transparent; } @keyframes color-ping { - 0% { - transform: scale(1) translateZ(0); + 0% { + transform: scale(1) translateZ(0); background-position-y:0; } - 5% { - transform: scale(0.95) translateZ(0); + 5% { + transform: scale(0.95) translateZ(0); background-position-y:30px; } 100% { background-color: @colorGrayDark; - transform: scale(1) translateZ(0); + transform: scale(1) translateZ(0); background-position-y:150px; - } + } } .rotating { animation-name: rotating; - animation-duration: 0.5s; + animation-duration: 0.5s; animation-iteration-count: infinite; - animation-timing-function: linear; + animation-timing-function: linear; } @keyframes rotating { 0% { transform: rotate(0deg) translate3d(0, 0, 0); - } + } 100% { transform: rotate(-180deg) translate3d(0, 1px, 0); - } + } } @media screen and (max-width: 960px ) { @@ -351,12 +351,12 @@ width: 0; text-overflow: clip; padding-left: @gridWidth * 0.75; - } + } } @media screen and (max-width: 800px ) { .browser-bar div.dapp-info { width: 0; text-overflow: clip; - } + } } diff --git a/interface/client/styles/constants.import.less b/interface/client/styles/constants.import.less index 663f84b..a20b979 100644 --- a/interface/client/styles/constants.import.less +++ b/interface/client/styles/constants.import.less @@ -1,4 +1,4 @@ -@widthSideBar: @gridWidth * 6.5; +@widthSideBar: 78px; @colorLinkActive: #4A90E2; @colorLinkBlur: #494949; diff --git a/interface/client/styles/elements.import.less b/interface/client/styles/elements.import.less index b131145..f30f22b 100644 --- a/interface/client/styles/elements.import.less +++ b/interface/client/styles/elements.import.less @@ -33,19 +33,10 @@ ul.no-bullets { } } - -.node-info { - position: absolute; - right: @defaultMargin; - font-size: 0.9em; - color: @colorTextSecondary; -} - - .danger { display: inline-block; padding: 1px 2px; border-radius: 3px; color: @colorWhite; background-color: @colorError; -} \ No newline at end of file +} diff --git a/interface/client/styles/layout.import.less b/interface/client/styles/layout.import.less index 14c49b1..f8dc05b 100644 --- a/interface/client/styles/layout.import.less +++ b/interface/client/styles/layout.import.less @@ -32,6 +32,7 @@ .darwin { aside { top: @gridHeight * 2; + height: calc(100% - @gridHeight * 2); } div.browser-bar { @@ -75,31 +76,6 @@ button h1, button h2, button h3, button p { cursor: pointer; } -aside { - z-index: 4; - position: absolute; - top: @gridHeight * 2; - left: 0; - bottom: 0; - // padding-top: @gridHeight/2; - width: @widthSideBar; - - - &:before { - content: " "; - display: block; - position: absolute; - z-index: 2; - top: 0; - left: @widthSideBar; - width: 10px; - bottom: 0; - // box-shadow: @shadowStandard; - } -} -.linux aside { - top: 0; -} main { @@ -141,12 +117,11 @@ html { left: 0; right: 0; bottom: 0; - box-shadow: @shadowStandard; + border-left: 1px solid #E2E2E2; background: #FFF; overflow: hidden; opacity: 1; z-index:10; - border-top-left-radius: 3px; &.hidden { visibility: hidden; @@ -177,3 +152,53 @@ webview { .linux webview { top: 0; } + + +// Scrollbars +// Reference: https://gist.github.com/devinrhode2/2573411 +aside.sidebar { + ::-webkit-scrollbar { + width: 7px; + background-color: rgba(0,0,0,0); + -webkit-border-radius: 100px; + } + ::-webkit-scrollbar-thumb { + background-color: rgba(0, 0, 0, 0.09); + } + + ::-webkit-scrollbar-thumb:vertical { + background: rgba(0,0,0,0.5); + -webkit-border-radius: 100px; + } + ::-webkit-scrollbar-thumb:vertical:active { + background: rgba(0,0,0,0.61); + -webkit-border-radius: 100px; + background-clip: padding-box; + border: 2px solid rgba(0, 0, 0, 0); + } + + ::-webkit-scrollbar { + width: 10px; + } + + ::-webkit-scrollbar-thumb:vertical { + background-clip: padding-box; + border: 2px solid rgba(0, 0, 0, 0); + min-height: 30px; + } + + .submenu-container { + ::-webkit-scrollbar-thumb:vertical { + background: rgba(255,255,255,0.5); + border: 2px solid rgba(0, 0, 0, 0); + background-clip: padding-box; + } + ::-webkit-scrollbar-thumb:vertical:active { + background: rgba(255,255,255,0.61); + border: 2px solid rgba(0, 0, 0, 0); + background-clip: padding-box; + } + + } + +} diff --git a/interface/client/styles/menu.import.less b/interface/client/styles/menu.import.less index 5f2abf8..496fc47 100644 --- a/interface/client/styles/menu.import.less +++ b/interface/client/styles/menu.import.less @@ -1,342 +1,317 @@ +.gradient-tip { + content: ''; + display: block; + width: 100%; + height: @gridHeight * 0.75; + left: 0px; + position: absolute; + pointer-events: none; +} -aside.sidebar { - overflow: hidden; - - ul { - z-index: 10; - position: relative; - .dapp-vertical-menu; - top: 0; - padding-left: @gridWidth/4; - - li { - position: relative; - display: block; - padding-left: @gridWidth/4; - margin-top: 0; - overflow: hidden; - height: @gridHeight * 2; - - &.ui-sortable-helper { - height: @gridHeight*2 !important; - opacity: 0.5; - } - &.ui-sortable-placeholder { - visibility: visible !important; - height: 0 !important; //@gridHeight*3 - border-bottom: 2px solid @colorLinkActive; - margin: 5px 0; - } - - &.selected, - &.slided-out { - height: 100%; - - - ul.sub-menu li { - height: floor(@gridHeight * 2); - opacity: 1; - } - } - - &.selected { - color: @colorLinkActive; - background: #FFF; - border-radius: 3px 0 0 3px; - box-shadow: @shadowStandard; - border-top: 0; +.sidePadding { + padding-left: 11px; + padding-right: 11px; +} - .slide-out, .slide-out.arrow-down { - transform: rotate(90deg) translate3d(-1px, -10px, 0); - } - - ul.sub-menu .see-all { - display: block; - } - - button.selected { - font-weight: 600; - } - } +// OS-specific styles +html.darwin aside.sidebar { + top: @gridHeight; + &::after { + top: @gridHeight; + } + nav { + margin-top: @gridHeight; + } +} - &.selected + li, - &:first-child { - border-top: 0; - margin-top: 1px; - } +aside.sidebar { + display: flex; + flex-flow: column nowrap; + justify-content: space-between; + + z-index: 4; + position: absolute; + top: @gridHeight / 2; + left: 0; + bottom: 0; + width: @widthSideBar; + + &::after { + .gradient-tip(); + top: @gridHeight / 2; + height: 8px; + background-image: linear-gradient(to top, rgba(241, 241, 241, 0) 0%, rgba(241, 241, 241, 1) 100%); + } + nav { + position: relative; + margin-top: @gridHeight / 2; + padding: 0 12px 0; + overflow: hidden; + min-height: 0; + &:hover { + overflow-y: auto; + } - &.browser { - .sub-menu { - li:first-child button { - color: @colorLinkFocus; + > ul { + margin: 6px 0; + padding: 0; + width: 55px; + + > li { + overflow: hidden; + margin-bottom: 15px; + transition-delay: 200ms; + + // draggable LI + &.ui-sortable-helper { + transform: scale(1.1); + .submenu-container { + display: none; } } - } - button.main { - position: relative; - padding: 0; - width: 85%; - height: @gridHeight*2; - color: @colorLinkBlur; - text-align: left; - text-transform: uppercase; - font-family: @sourceSansPro; - font-size: @fontSizeNormal; - font-weight: 500; - - - &:focus { - border: 0; - color: @colorLinkFocus; - } - - img, .icon-globe { - position: absolute; - top: 12px; - left: 2px; - height: @gridHeight; - width: @gridHeight; - border-radius: 10px; + &:hover { + .submenu-container { + opacity: 1; + visibility: visible; + } } - .icon-globe { - top: 13px; + &.selected, &:active, &:hover, &:focus { + button.main { + opacity: 1; + } } - - span, .badge { - .dapp-shorten-text; + button.main { + height: 54px; + width: 54px; display: block; - position: absolute; - width: 86%; - top: @gridHeight/2 + 2px; - left: 15px + @defaultPaddingVertical; - display: inline-block; - } + opacity: .6; + transition: 100ms opacity linear; - &.has-badge span { - position: relative; - top: -4px; - } + &:focus { + outline: 0; + border: none; + } - > .badge { - top: @gridHeight; - font-size: @fontSizeSmall; - font-weight: 200; - text-transform: none; - } - } + .icon-globe { + font-size: 32px; + text-align: center; + display: block; + background-color: #fff; + padding-top: 10px; + } - // Remove tab button - button.main { - img, .icon-globe { - transition: 0.25s ease-in-out transform; - transform: rotateY(0deg) translate3d(0, 0, 0); - backface-visibility: hidden; - color: @colorGray; + img, .icon-globe { + width: 54px; + height: 54px; + -webkit-mask-image: url('icons/mask-icon.svg'); + -webkit-mask-size: cover; + } } } + } - &:not(.browser):not(.wallet) header:hover { - button.main img, button.main .icon-globe { - transform: rotateY(-180deg) translate3d(0, 0, 0); - } - - button.remove-tab { - transform: rotateY(0deg) translate3d(0px, 0px, 0); - &:hover { - background: @colorLink; - color: @colorWhite; - } - } - } - - button.remove-tab { - box-sizing: border-box; + .submenu-container { + width: 185px; + position: fixed; + left: 90px; + top: 120px; + border-radius: 5px; + z-index: 1000; + visibility: hidden; + opacity: 0; + cursor: default; + + transition: 150ms linear all, 1ms linear top; + transition-delay: 200ms; + transform: translateY(-11px); + // backdrop-filter: blur(0); + + &::before { + @tipSize: 8px; + content: ''; + margin-left: -@tipSize; + margin-top: 19px + 11px; + display: block; position: absolute; - padding: 6px 8px 0px 2px; - transition: 0.25s ease-in-out transform; - left: 8px; - top: 11px; - color: @colorLink; - height: @gridHeight - 1px; - width: @gridHeight; - border-radius: 50%; - padding: 0; - z-index: 2; - backface-visibility: hidden; - transform: rotateY(180deg) translate3d(0px, 0px, 0); - &:focus { - border: none; - } + width: 0px; + height: @tipSize * 2.25; + border: 0px solid transparent; + border-width: @tipSize; + border-left: 0; + border-right-color: rgba(0,0,0,0.78); } - button.slide-out { - position: absolute; - right: @gridHeight/2; - top: @gridHeight/1.75; - width: 30px; - height: 20px; - background: url('icons/expand-icon.png') no-repeat center; - background-size: 15px 10px; - border: none; - transform: translate3d(10px, -1px, 0); - - &.arrow-down { - transform: rotate(180deg) translate3d(-10px, 1px, 0); + button { + &:active, &:focus { + transform: none; + border: none; } } - - // SUB MENU - ul.sub-menu { - padding-left: @gridWidth/2; - border-top: 0; - max-height: @gridHeight*12; - - li { - height: 0; - opacity: 0; - line-height: floor(@gridHeight * 2) - 1px; // -1px fixes an issue, where fonts shift up by 1px - - border-top: 1px solid rgba(0, 0, 0, 0.04); + section { + padding: 8px 0 0; + background-color: rgba(0, 0, 0, 0.78); + // backdrop-filter: blur(5px); + width: 100%; + border-radius: 5px; + color: #fff; + position: relative; - button:not(.slide-out) { - font-size: @fontSizeSmall; - font-weight: 400; - text-transform: none; - } + header { + .sidePadding(); + padding-bottom: 11px; } - - li:nth-child(6), li:nth-child(7) { - visibility: hidden; + span { + font-weight: 400; } - - button { - .dapp-shorten-text; - padding: floor(@gridHeight / 2) 0; - width: @gridWidth*3.6; - color: @colorLinkActive; - text-align: left; - border: 0; - - &:focus { - border: 0; - color: @colorLinkFocus; - } - - - &.selected { - color: @colorLinkBlur; - } - - + a, button { + color: #fff; } - .badge { - .dapp-shorten-text; + font-size: 11px; + } + .remove-tab { + color: #A6A6A6; position: absolute; - max-width: @gridWidth*1.5; - right: 0; - top: 0; - bottom: 0; - padding: 0 @gridWidth/6; - background-color: rgba(0, 0, 0, 0.05); - text-align: center; - line-height: floor(@gridHeight * 2); - font-size: @fontSizeSmall; - + right: 5px; + top: 4px; + width: 14px; &:hover { - max-width: @gridWidth*4; + color: #fff; } } - .see-all { - position: absolute; - margin-top: -4px; - top: @gridHeight * 10; - width: 100%; - + .accounts { + margin-top: 11px; button { - font-style: italic; + font-size: 12px; + font-weight: 300; + text-transform: uppercase; + width: 100%; + } + .connect { + background-color: #4C92E6; + border-radius: 4px; + padding: 3px 0; + } + .display { + @identiconHeight: 17px; + text-align: left; + line-height: @identiconHeight; + + .dapp-identicon-container { + float: right; + height: @identiconHeight; + } + + .dapp-identicon { + margin-right: 0px; + width: @identiconHeight; + height: @identiconHeight; + &:not(:last-child) { + margin-right: 4px; + } + } } } - } - } - - } - - &.full-tabs { - nav > ul > li:not(.selected) { - visibility: hidden; - height: 0; - transform: translate3d(-100%,0,0); - } - nav > ul > li.selected { - img { - opacity: 0; - } - button.slide-out { - transform: rotate(-90deg); - left: 1px; - right: auto; - } - - ul { - max-height: none; - } - - li:nth-child(6), li:nth-child(7) { - visibility: visible; } - .see-all { - display: none; + .sub-menu { + margin: 6px 0; + padding: 0; + border-top: 1px solid rgba(255, 255, 255, 0.2); + overflow-y: auto; + overflow-x: hidden; + padding-bottom: 0.1em; + margin-top: 2px; + margin-bottom: 0; + li { + opacity: 1; + font-weight: normal; + font-size: 14px; + margin-bottom: 0; + button { + padding: 8px 0 6px; + .sidePadding(); + box-sizing: border-box; + width: 100%; + text-align: left; + background-color: rgba(255, 255, 255, 0); + transition: 150ms linear background-color; + + margin: 5px 0; + display: -webkit-box; + -webkit-line-clamp: 2; // 2 lines max + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; + &:hover { + background-color: rgba(255, 255, 255, 0.2); + } + .badge { + display: block; + opacity: .8; + } + } + } } } - - & + .browser-bar + main .webview { - border-top-left-radius: 0; - } } .node-info { + position: relative; + cursor: default; + + &::after { + .gradient-tip(); + height: 20px; + top: -(20px - 1); + background-image: linear-gradient(to bottom, rgba(241, 241, 241, 0) 0%, rgba(241, 241, 241, 1) 100%); + } + display: flex; - top: initial; - bottom: @gridHeight; - left: @gridWidth/2; flex-flow: row wrap; - + flex-shrink: 0; + padding: 8px; + font-size: 0.9em; + color: @colorTextSecondary; + div, span { padding: @gridHeight/2 1px; flex: 1; text-align: center; } + i { + display: block; + margin-bottom: 2px; + } + progress { flex: 1 100%; } - .mining-indicator { + .mining-indicator { flex: 3; } .block-number { flex: 2; + white-space: nowrap; } .test-chain { flex: 1 100%; padding: 1px 5px 2px; - margin: @gridHeight/2 @gridWidth/4; } } } -// Remove tab button related code -aside.full-tabs button.remove-tab { - display: none; +.app-blur aside.newsidebar nav::after { + background-image: linear-gradient(to bottom, rgba(246, 246, 246, 0) 0%, rgba(246, 246, 246, 1) 100%); } diff --git a/interface/client/templates/elements/img.js b/interface/client/templates/elements/img.js index 6e7e335..060bef3 100644 --- a/interface/client/templates/elements/img.js +++ b/interface/client/templates/elements/img.js @@ -17,7 +17,7 @@ Template['elements_img'].helpers({ @method (preload) */ - 'preload': function(){ + 'preload': function () { var template = Template.instance(), data = this, img = new Image(); @@ -30,4 +30,4 @@ Template['elements_img'].helpers({ }; img.src = data.src; } -}); \ No newline at end of file +}); diff --git a/interface/client/templates/elements/networkIndicator.js b/interface/client/templates/elements/networkIndicator.js index 9b8a92b..f1f66fc 100644 --- a/interface/client/templates/elements/networkIndicator.js +++ b/interface/client/templates/elements/networkIndicator.js @@ -12,61 +12,59 @@ The networkIndicator template */ - /** Check network type. @method checkNetworkType */ -var checkNetworkType = function(template) { +var checkNetworkType = function (template) { console.trace('Check network type...'); try { - web3.eth.getBlock(0, function(e, res) { + web3.eth.getBlock(0, function (e, res) { console.trace('Get block 0', e, res); - + if (e) { console.error('Got error fetching block 0', e); } else { - TemplateVar.set(template, 'network', Helpers.detectNetwork(res.hash).type); + TemplateVar.set(template, 'network', Helpers.detectNetwork(res.hash).type); } - }); + }); } catch (err) { console.error('Unable to get block 0', err); } }; - -Template['elements_networkIndicator'].onRendered(function(){ +Template['elements_networkIndicator'].onRendered(function () { var template = this; TemplateVar.set(template, 'network', 'unknown'); checkNetworkType(template); - ipc.on('uiAction_nodeStatus', function(e, status) { + ipc.on('uiAction_nodeStatus', function (e, status) { console.trace('Node status', status); switch (status) { - case 'starting': - case 'stopping': - case 'connected': - console.debug('Node status changing, reset network type indicator'); + case 'starting': + case 'stopping': + case 'connected': + console.debug('Node status changing, reset network type indicator'); + + TemplateVar.set(template, 'network', 'unknown'); - TemplateVar.set(template, 'network', 'unknown'); - break; } }); - ipc.on('uiAction_nodeSyncStatus', function(e, status, data) { + ipc.on('uiAction_nodeSyncStatus', function (e, status, data) { console.trace('Node sync status', status); if ('inProgress' === status && TemplateVar.get(template, 'network') === 'unknown') { console.debug('Node syncing, re-check network type.'); - checkNetworkType(template); + checkNetworkType(template); } }); }); diff --git a/interface/client/templates/elements/nodeInfo.js b/interface/client/templates/elements/nodeInfo.js index df3fc8c..91e94df 100644 --- a/interface/client/templates/elements/nodeInfo.js +++ b/interface/client/templates/elements/nodeInfo.js @@ -49,8 +49,8 @@ Template['elements_nodeInfo'].onCreated(function(){ web3.eth.getBlock(0, function(e, res){ if(!e){ const network = Helpers.detectNetwork(res.hash); - TemplateVar.set(template, 'network', network.type); - TemplateVar.set(template, 'networkName', network.name); + TemplateVar.set(template, 'network', network.type); + TemplateVar.set(template, 'networkName', network.name); } }); @@ -63,12 +63,12 @@ Template['elements_nodeInfo'].onCreated(function(){ web3.reset(true); } else if(_.isObject(syncing)) { - + syncing.progress = Math.floor(((syncing.currentBlock - syncing.startingBlock) / (syncing.highestBlock - syncing.startingBlock)) * 100); syncing.blockDiff = numeral(syncing.highestBlock - syncing.currentBlock).format('0,0'); TemplateVar.set(template, 'syncing', syncing); - + } else { console.log('Restart app operation again'); @@ -138,13 +138,10 @@ Template['elements_nodeInfo'].helpers({ return timeSince.fromNow(true); } else if (diff<2) { Helpers.rerun["1s"].tick(); - return ' ' + TAPi18n.__('mist.nodeInfo.blockReceived') + '' + return ' ' + TAPi18n.__('mist.nodeInfo.blockReceivedShort') + '' } else { Helpers.rerun["1s"].tick(); return diff + "s"; } } }); - - - diff --git a/interface/client/templates/index.js b/interface/client/templates/index.js index 6982956..1c92d6c 100644 --- a/interface/client/templates/index.js +++ b/interface/client/templates/index.js @@ -17,17 +17,18 @@ Template.body.helpers({ @method renderApp */ - 'renderApp': function(){ - if(_.isEmpty(location.hash)) { + 'renderApp': function () { + if (_.isEmpty(location.hash)) { $('title').text('Mist'); return 'layout_main'; } else { var renderWindow = location.hash.match(/#([a-zA-Z]*)_?/); - if (renderWindow.length>0) + if (renderWindow.length > 0) { return 'popupWindows_' + renderWindow[1]; - else + } else { return false; + } } } }); @@ -42,4 +43,4 @@ Template.body.events({ 'dragover body > *, drop body > *': function(e){ e.preventDefault(); }, -});*/ \ No newline at end of file +});*/ diff --git a/interface/client/templates/layout/browserBar.js b/interface/client/templates/layout/browserBar.js index b584621..b4f1110 100644 --- a/interface/client/templates/layout/browserBar.js +++ b/interface/client/templates/layout/browserBar.js @@ -12,7 +12,7 @@ The browserBar template */ -Template['layout_browserBar'].onRendered(function(){ +Template['layout_browserBar'].onRendered(function () { var template = this; }); @@ -39,7 +39,7 @@ Template['layout_browserBar'].helpers({ @method (dapp) */ - 'dapp': function(){ + 'dapp': function () { return Tabs.findOne(LocalStore.get('selectedTab')); }, /** @@ -47,16 +47,17 @@ Template['layout_browserBar'].helpers({ @method (dappAccounts) */ - 'dappAccounts': function(){ - if(this.permissions) - return EthAccounts.find({address: {$in: this.permissions.accounts || []}}); + 'dappAccounts': function () { + if (this.permissions) { + return EthAccounts.find({ address: { $in: this.permissions.accounts || [] } }); + } }, /** Show the add button, when on a dapp and in doogle @method (isBrowser) */ - 'isBrowser': function(){ + 'isBrowser': function () { return (LocalStore.get('selectedTab') === 'browser'); }, /** @@ -64,8 +65,8 @@ Template['layout_browserBar'].helpers({ @method (currentWebView) */ - 'currentWebView': function(){ - return '.webview webview[data-id="'+ LocalStore.get('selectedTab') +'"]'; + 'currentWebView': function () { + return '.webview webview[data-id="' + LocalStore.get('selectedTab') + '"]'; } }); @@ -75,22 +76,24 @@ Template['layout_browserBar'].events({ @event click button.back */ - 'click button.back': function(){ + 'click button.back': function () { var webview = Helpers.getWebview(LocalStore.get('selectedTab')); - if(webview && webview.canGoBack()) + if (webview && webview.canGoBack()) { webview.goBack(); + } }, /* Reload the current webview @event click button.reload */ - 'click button.reload': function(){ + 'click button.reload': function () { var webview = Helpers.getWebview(LocalStore.get('selectedTab')); - if(webview) + if (webview) { webview.reload(); + } }, /* Remove the current selected tab @@ -99,7 +102,7 @@ Template['layout_browserBar'].events({ @event click button.remove-tab */ - 'click button.remove-tab': function(){ + 'click button.remove-tab': function () { var tabId = LocalStore.get('selectedTab'); Tabs.remove(tabId); @@ -110,17 +113,17 @@ Template['layout_browserBar'].events({ @event click .app-bar > button.accounts' */ - 'click .app-bar > button.accounts': function(e, template) { - mist.requestAccount(function(e, addresses){ + 'click .app-bar > button.accounts': function (e, template) { + LocalStore.set('chosenTab', LocalStore.get('selectedTab')); // needed by connectAccount + mist.requestAccount(function (e, addresses) { var tabId = LocalStore.get('selectedTab'); dbSync.syncDataFromBackend(LastVisitedPages); - dbSync.syncDataFromBackend(Tabs).then(function(){ - Tabs.update(tabId, {$set: { + dbSync.syncDataFromBackend(Tabs).then(function () { + Tabs.update(tabId, { $set: { 'permissions.accounts': addresses - }}); + } }); }); - }); }, /* @@ -128,7 +131,7 @@ Template['layout_browserBar'].events({ @event blur */ - 'blur .app-bar > form.url .url-input': function(e, template) { + 'blur .app-bar > form.url .url-input': function (e, template) { template.$('.app-bar').removeClass('show-bar'); }, /* @@ -136,7 +139,7 @@ Template['layout_browserBar'].events({ @event mouseenter .app-bar */ - 'mouseenter .app-bar': function(e, template){ + 'mouseenter .app-bar': function (e, template) { clearTimeout(TemplateVar.get('timeoutId')); }, /* @@ -144,7 +147,7 @@ Template['layout_browserBar'].events({ @event submit */ - 'submit': function(e, template){ + 'submit': function (e, template) { var url = Helpers.formatUrl(template.$('.url-input')[0].value); // remove focus from url input @@ -154,13 +157,13 @@ Template['layout_browserBar'].events({ var url = Helpers.sanitizeUrl(url); var tabId = Helpers.getTabIdByUrl(url); - console.log('Submitted new URL:'+ url); + console.log('Submitted new URL:' + url); // update current tab url - Tabs.update(tabId, {$set: { + Tabs.update(tabId, { $set: { url: url, redirect: url - }}); + } }); LocalStore.set('selectedTab', tabId); } }); diff --git a/interface/client/templates/layout/sidebar.html b/interface/client/templates/layout/sidebar.html index 677d865..71203a7 100644 --- a/interface/client/templates/layout/sidebar.html +++ b/interface/client/templates/layout/sidebar.html @@ -1,49 +1,71 @@ \ No newline at end of file + diff --git a/interface/client/templates/layout/sidebar.js b/interface/client/templates/layout/sidebar.js index 381909d..56b3e49 100644 --- a/interface/client/templates/layout/sidebar.js +++ b/interface/client/templates/layout/sidebar.js @@ -11,7 +11,7 @@ The sidebar template @constructor */ -Template['layout_sidebar'].onRendered(function(){ +Template['layout_sidebar'].onRendered(function () { var template = this, $ul = template.$('nav > ul'); @@ -21,23 +21,26 @@ Template['layout_sidebar'].onRendered(function(){ // tolerance: 'pointer', items: '> li:not(.browser)', handle: 'button.main', - cancel: '', + cancel: '.browser', cursor: 'move', delay: 150, revert: 200, - start: function(e){ + start: function (e) { $ul.sortable('refreshPositions'); }, - update: function(e){ - console.log('UPDATED'); + update: function (e) { // iterate over the lis and reposition the items - $ul.find('> li').each(function(index, test){ + $ul.find('> li').each(function (index, test) { var id = $(this).data('tab-id'); - if(id) - Tabs.update(id, {$set: {position: index+1}}); + if (id) { + Tabs.update(id, { $set: { position: index + 1 } }); + } }); } }); + + template.$('[data-tab-id]').on('mouseover', function () { + }); }); @@ -47,15 +50,15 @@ Template['layout_sidebar'].helpers({ @method (tabs) */ - 'tabs': function() { - return Tabs.find({}, {sort: {position: 1}}).fetch(); + 'tabs': function () { + return Tabs.find({}, { sort: { position: 1 } }).fetch(); }, /** Return the correct name @method (name) */ - 'name': function() { + 'name': function () { return (this._id === 'browser') ? TAPi18n.__('mist.sidebar.buttons.browser') : this.name; }, /** @@ -63,7 +66,7 @@ Template['layout_sidebar'].helpers({ @method (icon) */ - 'icon': function() { + 'icon': function () { return (this._id === 'browser') ? 'icons/browse-icon@2x.png' : this.icon; }, /** @@ -71,19 +74,23 @@ Template['layout_sidebar'].helpers({ @method (subMenu) */ - 'subMenu': function(){ + 'subMenu': function () { var template = Template.instance(); - if(this._id === 'browser') { - return LastVisitedPages.find({},{sort: {timestamp: -1}, limit: 25}); + if (this._id === 'browser') { + return LastVisitedPages.find({}, { sort: { timestamp: -1 }, limit: 25 }); - } else if(this.menu) { + } else if (this.menu) { var menu = _.toArray(this.menu); // sort by position - menu.sort(function(a, b){ - if(a.position < b.position) return -1; - if(a.position > b.position) return 1; + menu.sort(function (a, b) { + if (a.position < b.position) { + return -1; + } + if (a.position > b.position) { + return 1; + } return 0; }); @@ -91,26 +98,35 @@ Template['layout_sidebar'].helpers({ } }, /** + Returns connected accounts for dapp + + @method (dappAccounts) + */ + 'dappAccounts': function (limit) { + if (this.permissions) { + if (limit) { + return EthAccounts.find({ address: { $in: this.permissions.accounts || [] } }, + { limit: limit }); + } + return EthAccounts.find({ address: { $in: this.permissions.accounts || [] } }); + } + }, + /** Determines if the current tab is visible @method (isSelected) */ - 'isSelected': function(){ - var selected = (LocalStore.get('selectedTab') === (this._id || 'browser')) ? 'selected' : ''; - - if(this.menuVisible) - selected += ' slided-out'; - - return selected; + 'isSelected': function () { + return (LocalStore.get('selectedTab') === (this._id || 'browser')) ? 'selected' : ''; }, /** - Determines if the current tab is visible + It defines which tabs will have a remove button on the interface - @method (fullTabs) + @method (tabShouldBeRemovable) */ - 'fullTabs': function(){ - return (LocalStore.get('fullTabs')) ? 'full-tabs' : ''; - } + 'tabShouldBeRemovable': function () { + return !_.contains(['browser', 'wallet'], this._id); + }, }); @@ -120,7 +136,7 @@ Template['layout_sidebar'].events({ @event click button.main */ - 'click nav button.main': function(e, template){ + 'click nav button.main': function (e, template) { LocalStore.set('selectedTab', this._id || 'browser'); }, /** @@ -128,61 +144,77 @@ Template['layout_sidebar'].events({ @event click ul.sub-menu button */ - 'click nav ul.sub-menu button': function(e, template){ + 'click nav ul.sub-menu button': function (e, template) { var tabId = $(e.currentTarget).parent().parents('li').data('tab-id'); - var webview = $('webview[data-id="'+ tabId +'"]')[0]; + var webview = $('webview[data-id="' + tabId + '"]')[0]; // browser - if(tabId === 'browser') { - webviewLoadStart.call(webview, tabId, {newURL: this.url, type: 'side-bar-click', preventDefault: function(){}}); + if (tabId === 'browser') { + webviewLoadStart.call(webview, tabId, { newURL: this.url, type: 'side-bar-click', preventDefault: function () {} }); // dapp tab - } else if(webview) { + } else if (webview) { webview.send('mistAPI_callMenuFunction', this.id); LocalStore.set('selectedTab', tabId); } }, /** - Slide out + Remove the current selected tab - @event button.slide-out + // TODO show popup before to confirm + + @event click button.remove-tab */ - 'click button.slide-out': function(e, template){ - var isSelected = (LocalStore.get('selectedTab') === (this._id || 'browser')); - - if (isSelected && LocalStore.get('fullTabs')) { - LocalStore.set('fullTabs', false); - } else if (isSelected) { - LocalStore.set('fullTabs', true); - } else { - Tabs.update(this._id, {$set: {menuVisible: !this.menuVisible}}); + 'click button.remove-tab': function () { + if (LocalStore.get('selectedTab') === this._id) { + LocalStore.set('selectedTab', 'browser'); } + + Tabs.remove(this._id); }, /** - See all + Show connect account popup - @event .see-all button + @event click .accounts button' */ - 'click li.see-all > button': function(e, template){ - var isSelected = (LocalStore.get('selectedTab') === (this._id || 'browser')); - - if (isSelected && LocalStore.get('fullTabs')) { - LocalStore.set('fullTabs', false); - } else if (isSelected) { - LocalStore.set('fullTabs', true); - } + 'click .accounts button': function (e, template) { + var initialTabCount = Tabs.find().fetch().length; + LocalStore.set('selectedTab', this._id); + var initialTabId = this._id; + + mist.requestAccount(function (ev, addresses) { + dbSync.syncDataFromBackend(LastVisitedPages); + dbSync.syncDataFromBackend(Tabs).then(function () { + var tabCount = Tabs.find().fetch().length; + var tabId; + if (tabCount > initialTabCount) { // browse tab was pinned + tabId = Tabs.findOne({}, { sort: { position: -1 }, limit: 1 }); + } else { + tabId = initialTabId; + } + Tabs.update(tabId, { + $set: { + 'permissions.accounts': addresses + } + }); + }); + }); }, - /** - Remove the current selected tab - // TODO show popup before to confirm + /** + Shows dapp submenu - @event click button.remove-tab + @event mouseenter .sidebar-menu > li */ - 'click button.remove-tab': function(){ - if (LocalStore.get('selectedTab') === this._id) - LocalStore.set('selectedTab', 'browser'); - - Tabs.remove(this._id); + 'mouseenter .sidebar-menu > li': function (e, template) { + var $this = $(e.currentTarget); + var tabTopOffset = $this.offset().top; + var $submenuContainer = $this.find('.submenu-container'); + var $submenu = $this.find('.sub-menu'); + var submenuHeaderHeight = $this.find('header').outerHeight(); + var windowHeight = $(window).outerHeight(); + + $submenuContainer.css('top', tabTopOffset + 'px'); + $submenu.css('max-height', (windowHeight - tabTopOffset - submenuHeaderHeight - 30) + 'px'); }, }); diff --git a/interface/client/templates/layout/webviews.js b/interface/client/templates/layout/webviews.js index 2ed22b6..baefbfa 100644 --- a/interface/client/templates/layout/webviews.js +++ b/interface/client/templates/layout/webviews.js @@ -17,7 +17,7 @@ Template['layout_webviews'].helpers({ @method (tabs) */ - 'tabs': function() { - return Tabs.find({}, {field: {position: 1}}); + 'tabs': function () { + return Tabs.find({}, { field: { position: 1 } }); } -}); \ No newline at end of file +}); diff --git a/interface/client/templates/popupWindows/about.js b/interface/client/templates/popupWindows/about.js index 18035af..3be06a5 100644 --- a/interface/client/templates/popupWindows/about.js +++ b/interface/client/templates/popupWindows/about.js @@ -11,7 +11,7 @@ The about template @class [template] popupWindows_about @constructor */ -Template['popupWindows_about'].onCreated(function(){ +Template['popupWindows_about'].onCreated(function () { }); diff --git a/interface/client/templates/popupWindows/clientUpdateAvailable.js b/interface/client/templates/popupWindows/clientUpdateAvailable.js index 8f55f2d..34c48f3 100644 --- a/interface/client/templates/popupWindows/clientUpdateAvailable.js +++ b/interface/client/templates/popupWindows/clientUpdateAvailable.js @@ -6,10 +6,10 @@ Template Controllers Template['popupWindows_clientUpdateAvailable'].events({ - 'click .ok': function(e){ + 'click .ok': function (e) { ipc.send('backendAction_windowCallback', 'update'); }, - 'click .cancel': function(e){ + 'click .cancel': function (e) { ipc.send('backendAction_windowCallback', 'skip'); }, }); diff --git a/interface/client/templates/popupWindows/connectAccount.js b/interface/client/templates/popupWindows/connectAccount.js index 4926030..a720747 100644 --- a/interface/client/templates/popupWindows/connectAccount.js +++ b/interface/client/templates/popupWindows/connectAccount.js @@ -1,26 +1,22 @@ -var pinToSidebar = function() { - var selectedTab = Tabs.findOne(LocalStore.get('selectedTab')); +var pinToSidebar = function () { + var selectedTab = TemplateVar.get('tab'); - if(selectedTab) { + if (selectedTab) { var existingUserTab = Helpers.getTabIdByUrl(selectedTab.url); - console.log(existingUserTab); - console.log(selectedTab); - - if(existingUserTab === 'browser') { + if (existingUserTab === 'browser') { var newTabId = Tabs.insert({ url: selectedTab.url, redirect: selectedTab.url, name: selectedTab.name, menu: {}, - menuVisible: false, position: Tabs.find().count() + 1 }); LocalStore.set('selectedTab', newTabId); - } else if(existingUserTab) { + } else if (existingUserTab) { LocalStore.set('selectedTab', existingUserTab); } @@ -28,7 +24,7 @@ var pinToSidebar = function() { var sameLastPage; // move the current browser tab to the last visited page - var lastPageItems = LastVisitedPages.find({}, {limit: 2, sort: {timestamp: -1}}).fetch(); + var lastPageItems = LastVisitedPages.find({}, { limit: 2, sort: { timestamp: -1 } }).fetch(); var lastPage = lastPageItems.pop(); var lastPageURL = lastPage ? lastPage.url : 'http://about:blank'; Tabs.update('browser', { @@ -37,23 +33,26 @@ var pinToSidebar = function() { }); // remove last page form last pages - if(sameLastPage = LastVisitedPages.findOne({url: selectedTab.url})) + if (sameLastPage = LastVisitedPages.findOne({ url: selectedTab.url })) { LastVisitedPages.remove(sameLastPage._id); + } } } }; -var updateSelectedTabAccounts = function(accounts){ - var tabId = LocalStore.get('selectedTab'); - Tabs.update(tabId, {$set: { +var updateSelectedTabAccounts = function (accounts) { + var tabId = TemplateVar.get('selectedTab')._id; + Tabs.update(tabId, { $set: { 'permissions.accounts': accounts - }}); + } }); }; -Template['popupWindows_connectAccount'].onCreated(function() { - this.autorun(function(){ - var tab = Tabs.findOne(LocalStore.get('selectedTab'), {fields: {'permissions.accounts': 1}}); - var accounts = (tab && tab.permissions && tab.permissions.accounts) ? tab.permissions.accounts : []; +Template['popupWindows_connectAccount'].onCreated(function () { + this.autorun(function () { + TemplateVar.set('tab', Tabs.findOne(LocalStore.get('selectedTab'))); + + var tab = TemplateVar.get('tab'); + var accounts = (tab && tab.permissions && tab.permissions.accounts) ? tab.permissions.accounts : []; TemplateVar.set('accounts', accounts); }); }); @@ -65,17 +64,17 @@ Template['popupWindows_connectAccount'].helpers({ @method (dapp) */ - dapp: function(){ - return Tabs.findOne(LocalStore.get('selectedTab')); + dapp: function () { + return TemplateVar.get('tab'); }, /** Returns a cleaner version of URL @method (dappFriendlyURL) */ - dappFriendlyURL: function(){ - var currentTab = Tabs.findOne(LocalStore.get('selectedTab')) - if (currentTab && currentTab.url){ + dappFriendlyURL: function () { + var currentTab = TemplateVar.get('tab'); + if (currentTab && currentTab.url) { return currentTab.url.replace(/^https?:\/\/(www\.)?/, '').replace(/\/$/, ''); } }, @@ -85,7 +84,7 @@ Template['popupWindows_connectAccount'].helpers({ @method accountNumber @return {Number} */ - 'accountNumber': function(){ + 'accountNumber': function () { var accounts = _.pluck(EthAccounts.find().fetch(), 'address'); return _.intersection(accounts, TemplateVar.get('accounts')).length; @@ -96,7 +95,7 @@ Template['popupWindows_connectAccount'].helpers({ @method selectedAccounts @return {Array} */ - 'selectedAccounts': function() { + 'selectedAccounts': function () { var accounts = _.pluck(EthAccounts.find().fetch(), 'address'); return _.intersection(accounts, TemplateVar.get('accounts')); }, @@ -106,7 +105,7 @@ Template['popupWindows_connectAccount'].helpers({ @method selected @return {String} "selected" */ - 'selected': function(){ + 'selected': function () { return (_.contains(TemplateVar.get('accounts'), this.address)) ? 'selected' : ''; } }); @@ -117,45 +116,46 @@ Template['popupWindows_connectAccount'].events({ @event click .dapp-account-list button */ - 'click .dapp-account-list button': function(e, template) { + 'click .dapp-account-list button': function (e, template) { e.preventDefault(); var accounts = TemplateVar.get('accounts'); - if(!_.contains(accounts, this.address)) + if (!_.contains(accounts, this.address)) { accounts.push(this.address); - else + } else { accounts = _.without(accounts, this.address); + } TemplateVar.set(template, 'accounts', accounts); }, - /** + /** Closes the popup @event click .cancel */ - 'click .cancel': function(e) { - ipc.send('backendAction_closePopupWindow'); - }, + 'click .cancel': function (e) { + ipc.send('backendAction_closePopupWindow'); + }, /** - Confirm or cancel the accounts available for this dapp and reload the dapp. @event click button.confirm, click button.cancel */ - 'click .ok, click .stay-anonymous': function(e) { + 'click .ok, click .stay-anonymous': function (e) { e.preventDefault(); var accounts = TemplateVar.get('accounts'); - + // Pin to sidebar, if needed if ($('#pin-to-sidebar')[0].checked) { pinToSidebar(); } - accounts = _.unique(_.flatten(accounts)); + accounts = _.unique(_.flatten(accounts)); // reload the webview ipc.send('backendAction_windowMessageToOwner', null, accounts); - setTimeout(function(){ + setTimeout(function () { ipc.send('backendAction_closePopupWindow'); }, 600); }, @@ -164,7 +164,7 @@ Template['popupWindows_connectAccount'].events({ @event click button.create-account */ - 'click button.create-account': function(e, template){ + 'click button.create-account': function (e, template) { ipc.send('mistAPI_createAccount'); } }); diff --git a/interface/client/templates/popupWindows/onboardingScreen.js b/interface/client/templates/popupWindows/onboardingScreen.js index 35ba0b7..38775ab 100644 --- a/interface/client/templates/popupWindows/onboardingScreen.js +++ b/interface/client/templates/popupWindows/onboardingScreen.js @@ -12,37 +12,36 @@ The onboardingScreen template */ - /** Update the peercount @method getPeerCount */ -var getPeerCount = function(template) { - web3.net.getPeerCount(function(e, res) { - if(!e) +var getPeerCount = function (template) { + web3.net.getPeerCount(function (e, res) { + if (!e) { TemplateVar.set(template, 'peerCount', res); + } }); }; - -Template['popupWindows_onboardingScreen'].onCreated(function(){ +Template['popupWindows_onboardingScreen'].onCreated(function () { var template = this; TemplateVar.set('readyToLaunch', false); TemplateVar.set('newAccount', false); // check for block status - this.syncFilter = web3.eth.isSyncing(function(error, syncing) { - if(!error) { + this.syncFilter = web3.eth.isSyncing(function (error, syncing) { + if (!error) { - if(syncing === true) { + 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); @@ -58,7 +57,7 @@ Template['popupWindows_onboardingScreen'].onCreated(function(){ getPeerCount(template); Meteor.clearInterval(this.peerCountIntervalId); - this.peerCountIntervalId = setInterval(function() { + this.peerCountIntervalId = setInterval(function () { getPeerCount(template); }, 1000); @@ -66,14 +65,14 @@ Template['popupWindows_onboardingScreen'].onCreated(function(){ TemplateVar.set('currentActive', 'start'); // store the last class - this.autorun(function(){ + this.autorun(function () { TemplateVar.set('lastActive', TemplateVar.get('currentActive')); }); -}) +}); Template['popupWindows_onboardingScreen'].helpers({ - 'newAccountLowerCase': function(){ + 'newAccountLowerCase': function () { var account = TemplateVar.get('newAccount'); return (account) ? account.toLowerCase() : ''; }, @@ -82,7 +81,7 @@ Template['popupWindows_onboardingScreen'].helpers({ @method syncStatus */ - 'syncStatus' : function() { + 'syncStatus': function () { // This functions loops trhough numbers while waiting for the node to respond var template = Template.instance(); @@ -90,7 +89,7 @@ Template['popupWindows_onboardingScreen'].helpers({ Meteor.clearInterval(template._intervalId); // Create an interval to quickly iterate trough the numbers - template._intervalId = Meteor.setInterval(function(){ + template._intervalId = Meteor.setInterval(function () { // load the sync information var syncing = TemplateVar.get(template, 'syncing'); @@ -99,9 +98,9 @@ Template['popupWindows_onboardingScreen'].helpers({ TemplateVar.set(template, 'readyToLaunch', false); // 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._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); + syncing._displayStatesDownload = Number(syncing._displayStatesDownload + (syncing.pulledStates / (1 + syncing.knownStates) - syncing._displayStatesDownload) / 100) || Number(syncing.pulledStates) / Number(syncing.knownStates + 1); // Calculates progress syncing.progress = 100 * (syncing._displayBlock - syncing.startingBlock) / (1 + Number(syncing.highestBlock) - syncing.startingBlock); @@ -110,7 +109,7 @@ Template['popupWindows_onboardingScreen'].helpers({ 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'); + syncing.statesPercent = numeral(Math.round(syncing._displayStatesDownload * 10000) / 100).format('0.00'); // Saves the data back to the object TemplateVar.set(template, 'syncing', syncing); @@ -118,17 +117,17 @@ Template['popupWindows_onboardingScreen'].helpers({ // If it's close enough, show the synced button - if (Number(syncing.highestBlock) - syncing.currentBlock < 100 ) { + 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", ''); + 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)); + TemplateVar.set(template, 'syncStatusMessageLive', TAPi18n.__('mist.popupWindows.onboarding.syncMessage', syncing)); } } @@ -139,63 +138,66 @@ Template['popupWindows_onboardingScreen'].helpers({ @method syncStatusMessage */ - 'syncStatusMessage' : function() { - return TemplateVar.get("syncStatusMessageLive"); + 'syncStatusMessage': function () { + return TemplateVar.get('syncStatusMessageLive'); } }); Template['popupWindows_onboardingScreen'].events({ - 'click .goto-start': function(e){ - TemplateVar.set('currentActive','start'); + 'click .goto-start': function (e) { + TemplateVar.set('currentActive', 'start'); }, - 'click .goto-import-account': function(){ - TemplateVar.set('currentActive','import-account'); + 'click .goto-import-account': function () { + TemplateVar.set('currentActive', 'import-account'); // if testnet, make sure to switch to the mainnet - if(TemplateVar.get('testnet')) { + 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')) { + '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'); + TemplateVar.set('currentActive', 'testnet'); template.$('.onboarding-testnet input.password').focus(); }, - 'click .goto-password': function(e, template){ - TemplateVar.set('currentActive','password'); + 'click .goto-password': function (e, template) { + TemplateVar.set('currentActive', 'password'); template.$('.onboarding-password input.password').focus(); }, - 'click .goto-account': function(){ - TemplateVar.set('currentActive','account'); + 'click .goto-account': function () { + TemplateVar.set('currentActive', 'account'); }, - 'click .goto-tutorial-1': function(){ - TemplateVar.set('currentActive','tutorial-1'); - if (!TemplateVar.get('syncing')) + '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'); - if (!TemplateVar.get('syncing')) + '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'); - if (!TemplateVar.get('syncing')) + 'click .goto-tutorial-3': function () { + TemplateVar.set('currentActive', 'tutorial-3'); + if (!TemplateVar.get('syncing')) { TemplateVar.set('readyToLaunch', true); + } }, /** Start the application @event click .launch-app */ - 'click .launch-app': function(){ + 'click .launch-app': function () { ipc.send('onBoarding_launchApp'); }, /** @@ -203,7 +205,7 @@ Template['popupWindows_onboardingScreen'].events({ @event dragover .onboarding-screen, drop .onboarding-screen */ - 'dragover .onboarding-screen, drop .onboarding-screen': function(e){ + 'dragover .onboarding-screen, drop .onboarding-screen': function (e) { e.preventDefault(); } }); @@ -222,10 +224,10 @@ Template['popupWindows_onboardingScreen_importAccount'].helpers({ @method showPassword */ - 'showPassword': function() { - return TemplateVar.get('showPassword')? 'text' : 'password' ; + 'showPassword': function () { + return TemplateVar.get('showPassword') ? 'text' : 'password'; } -}) +}); Template['popupWindows_onboardingScreen_importAccount'].events({ @@ -234,7 +236,7 @@ Template['popupWindows_onboardingScreen_importAccount'].events({ @event dragenter .dropable */ - 'dragenter .dropable': function(e){ + 'dragenter .dropable': function (e) { $(e.currentTarget).addClass('active'); }, /** @@ -242,7 +244,7 @@ Template['popupWindows_onboardingScreen_importAccount'].events({ @event dragleave .dropable */ - 'dragleave .dropable': function(e){ + 'dragleave .dropable': function (e) { $(e.currentTarget).removeClass('active'); }, /** @@ -250,26 +252,30 @@ Template['popupWindows_onboardingScreen_importAccount'].events({ @event drop .dropable */ - 'drop .dropable': function(e, template) { + 'drop .dropable': function (e, template) { e.preventDefault(); - if (e.originalEvent.dataTransfer) files = e.originalEvent.dataTransfer.files; + if (e.originalEvent.dataTransfer) { + files = e.originalEvent.dataTransfer.files; + } if (files.length) { ipc.send('backendAction_checkWalletFile', files[0].path); - ipc.on('uiAction_checkedWalletFile', function(e, error, type) { + ipc.on('uiAction_checkedWalletFile', function (e, error, type) { switch (type) { case 'presale': + console.log(`Imported ${type} account`); TemplateVar.set(template, 'filePath', files[0].path); - Tracker.afterFlush(function() { + Tracker.afterFlush(function () { template.$('.password').focus(); }); break; case 'web3': + console.log(`Imported ${type} account`); TemplateVar.set(template, 'filePath', files[0].path); TemplateVar.set(template, 'importing', true); - setTimeout(function() { + setTimeout(function () { ipc.send('backendAction_closePopupWindow'); }, 750); break; @@ -289,7 +295,7 @@ Template['popupWindows_onboardingScreen_importAccount'].events({ @event dragover .dropable */ - 'dragover .dropable': function(e){ + 'dragover .dropable': function (e) { e.preventDefault(); }, /** @@ -297,31 +303,31 @@ Template['popupWindows_onboardingScreen_importAccount'].events({ @event click .show-password */ - 'click .show-password': function(e){ - TemplateVar.set('showPassword', e.currentTarget.checked) + 'click .show-password': function (e) { + TemplateVar.set('showPassword', e.currentTarget.checked); }, /** Checks the password match sends the file path and password to the mist backend to import @event submit form */ - 'submit form': function(e, template){ + 'submit form': function (e, template) { var pw = template.find('input.password').value; ipc.send('backendAction_importWalletFile', TemplateVar.get('filePath'), pw); TemplateVar.set('importing', true); - ipc.on('uiAction_importedWalletFile', function(e, error, address){ + ipc.on('uiAction_importedWalletFile', function (e, error, address) { TemplateVar.set(template, 'importing', false); TemplateVar.set(template, 'filePath', false); - if(address) { + if (address) { ipc.removeAllListeners('uiAction_importedWalletFile'); console.log('Imported account: ', address); // move to add account screen, when in the onboarding window - if($('.onboarding-start')[0]) { + if ($('.onboarding-start')[0]) { TemplateVar.setTo('.onboarding-account', 'newAccount', web3.toChecksumAddress(address)); TemplateVar.setTo('.onboarding-screen', 'currentActive', 'account'); @@ -334,14 +340,14 @@ Template['popupWindows_onboardingScreen_importAccount'].events({ } else { console.log('Import failed', error); - if(error === 'Decryption Failed') { + if (error === 'Decryption Failed') { GlobalNotification.warning({ content: TAPi18n.__('mist.popupWindows.onboarding.errors.wrongPassword'), duration: 4 }); } else { GlobalNotification.warning({ - content: TAPi18n.__('mist.popupWindows.onboarding.errors.importFailed', {error: error}), + content: TAPi18n.__('mist.popupWindows.onboarding.errors.importFailed', { error: error }), duration: 4 }); } @@ -355,7 +361,6 @@ Template['popupWindows_onboardingScreen_importAccount'].events({ }); - /** The onboardingScreen password template @@ -369,10 +374,10 @@ Template['popupWindows_onboardingScreen_password'].helpers({ @method showPassword */ - 'passwordInputType': function() { - return TemplateVar.get('passwordInputType')? 'text' : 'password' ; + 'passwordInputType': function () { + return TemplateVar.get('passwordInputType') ? 'text' : 'password'; } -}) +}); Template['popupWindows_onboardingScreen_password'].events({ @@ -381,7 +386,7 @@ Template['popupWindows_onboardingScreen_password'].events({ @event click button[type="button"] */ - 'click button[type="button"]': function(e, template){ + 'click button[type="button"]': function (e, template) { template.find('input.password').value = ''; template.find('input.password-repeat').value = ''; }, @@ -390,15 +395,15 @@ Template['popupWindows_onboardingScreen_password'].events({ @event click .show-password */ - 'click .show-password': function(e){ - TemplateVar.set('passwordInputType', e.currentTarget.checked) + 'click .show-password': function (e) { + TemplateVar.set('passwordInputType', e.currentTarget.checked); }, /** Password checks @event click button[type="button"] */ - 'input input, change input': function(e, template){ + 'input input, change input': function (e, template) { var pw = template.find('input.password').value, pwRepeat = template.find('input.password-repeat').value; @@ -411,25 +416,28 @@ Template['popupWindows_onboardingScreen_password'].events({ @event submit form */ - 'submit form': function(e, template){ + 'submit form': function (e, template) { var pw = template.find('input.password').value, pwRepeat = template.find('input.password-repeat').value; - if( pw !== pwRepeat) { + if (pw !== pwRepeat) { GlobalNotification.warning({ content: TAPi18n.__('mist.popupWindows.requestAccount.errors.passwordMismatch'), duration: 3 }); - } else if (pw && pw.length > 1 && pw.length < 9) { + } else if (pw && pw.length < 8) { GlobalNotification.warning({ content: TAPi18n.__('mist.popupWindows.requestAccount.errors.passwordTooShort'), duration: 3 }); - } else if (pw && pw.length >= 9) { + } else if (pw && pw.length >= 8) { TemplateVar.set('creatingPassword', true); - web3.personal.newAccount(pw, function(e, res){ + web3.personal.newAccount(pw, function (e, res) { TemplateVar.set(template, 'creatingPassword', false); + // notifiy about backing up! + alert(TAPi18n.__('mist.popupWindows.requestAccount.backupHint')); + if(!e) { TemplateVar.setTo('.onboarding-account', 'newAccount', web3.toChecksumAddress(res)); TemplateVar.setTo('.onboarding-screen', 'currentActive', 'account'); diff --git a/interface/client/templates/popupWindows/requestAccount.js b/interface/client/templates/popupWindows/requestAccount.js index 294d2ed..a0e7982 100644 --- a/interface/client/templates/popupWindows/requestAccount.js +++ b/interface/client/templates/popupWindows/requestAccount.js @@ -11,63 +11,72 @@ The request account popup window template @constructor */ -Template['popupWindows_requestAccount'].onRendered(function(){ +Template['popupWindows_requestAccount'].onRendered(function () { this.$('input.password').focus(); TemplateVar.set('showPassword', false); }); Template['popupWindows_requestAccount'].helpers({ - 'passwordInputType': function() { - return TemplateVar.get('showPassword')? 'text' : 'password'; + 'passwordInputType': function () { + return TemplateVar.get('showPassword') ? 'text' : 'password'; } }); Template['popupWindows_requestAccount'].events({ - 'click .cancel': function(){ + 'click .cancel': function () { ipc.send('backendAction_closePopupWindow'); - }, - 'click .show-password': function(e){ - TemplateVar.set('showPassword', e.currentTarget.checked) }, - 'submit form': function(e, template){ + 'click .show-password': function (e) { + TemplateVar.set('showPassword', e.currentTarget.checked); + }, + 'submit form': function (e, template) { e.preventDefault(); var pw = template.find('input.password').value; - var pwRepeat = template.find('input.password-repeat').value; + var pwRepeat = template.find('input.password-repeat').value; // ask for password repeat - if(!pwRepeat) { + if (!pwRepeat) { TemplateVar.set('password-repeat', true); template.$('input.password-repeat').focus(); // stop here so we dont set the password repeat to false return; + } // check passwords - } else if(pwRepeat === pw) { + if ( pw !== pwRepeat) { + GlobalNotification.warning({ + content: TAPi18n.__('mist.popupWindows.requestAccount.errors.passwordMismatch'), + duration: 3 + }); + } else if (pw && pw.length < 8) { + GlobalNotification.warning({ + content: TAPi18n.__('mist.popupWindows.requestAccount.errors.passwordTooShort'), + duration: 3 + }); + } else if (pw && pw.length >= 8) { TemplateVar.set('creating', true); - web3.personal.newAccount(pwRepeat, function(e, res){ - if(!e) + web3.personal.newAccount(pwRepeat, function (e, res) { + if (!e) { ipc.send('backendAction_windowMessageToOwner', null, res); - else + } else { ipc.send('backendAction_windowMessageToOwner', e); + } TemplateVar.set(template, 'creating', false); + + // notifiy about backing up! + alert(TAPi18n.__('mist.popupWindows.requestAccount.backupHint')); + ipc.send('backendAction_closePopupWindow'); }); - } else { - template.$('.password').focus(); - - GlobalNotification.warning({ - content: TAPi18n.__('mist.popupWindows.requestAccount.errors.passwordMismatch'), - duration: 3 - }); } TemplateVar.set('password-repeat', false); template.find('input.password-repeat').value = ''; template.find('input.password').value = ''; pw = pwRepeat = null; - } + } }); diff --git a/interface/client/templates/popupWindows/sendTransactionConfirmation.html b/interface/client/templates/popupWindows/sendTransactionConfirmation.html index 4cc9c5f..fc9e25a 100644 --- a/interface/client/templates/popupWindows/sendTransactionConfirmation.html +++ b/interface/client/templates/popupWindows/sendTransactionConfirmation.html @@ -13,7 +13,8 @@

{{i18n "mist.popupWindows.sendTransactionConfirmation.title.createContract"} {{/if}}
-
+
+
{{#if TemplateVar.get "fromIsContract"}} @@ -54,15 +55,35 @@

{{i18n "mist.popupWindows.sendTransactionConfirmation.title.createContract"}

{{#if transactionInvalid}} -

{{{i18n "mist.popupWindows.sendTransactionConfirmation.estimatedGasError"}}}

+

+ {{i18n "mist.popupWindows.sendTransactionConfirmation.estimatedGasError"}} +

{{else}} - {{#if $and data (TemplateVar.get "toIsContract")}} -

{{{i18n "mist.popupWindows.sendTransactionConfirmation.contractExecutionInfo"}}}

- {{/if}} + {{#unless $eq (TemplateVar.get "gasError") "notEnoughGas"}} + {{#if $eq (TemplateVar.get "gasError") "overBlockGasLimit"}} +
+ {{i18n "mist.popupWindows.sendTransactionConfirmation.overBlockGasLimit"}} +
+ {{else}} + {{#if $and data (TemplateVar.get "toIsContract")}} +

+ {{i18n "mist.popupWindows.sendTransactionConfirmation.contractExecutionInfo"}} +

+ {{/if}} + + {{#unless to}} +

+ {{i18n "mist.popupWindows.sendTransactionConfirmation.contractCreationInfo"}} +

+ {{/unless}} - {{#unless to}} -

{{{i18n "mist.popupWindows.sendTransactionConfirmation.contractCreationInfo"}}}

+ {{/if}} + {{else}} +
+ {{{i18n "mist.popupWindows.sendTransactionConfirmation.notEnoughGas"}}} +
{{/unless}} + {{/if}}
@@ -107,8 +128,8 @@

{{i18n "mist.popupWindows.sendTransactionConfirmation.title.createContract"} {{#if data}} {{#if showFormattedParams}}
-

{{{i18n "mist.popupWindows.sendTransactionConfirmation.parameters"}}} - {{{i18n "mist.popupWindows.sendTransactionConfirmation.showRawBytecode"}}} +

{{i18n "mist.popupWindows.sendTransactionConfirmation.parameters"}} + {{i18n "mist.popupWindows.sendTransactionConfirmation.showRawBytecode"}}

    {{# each param in params}} @@ -121,7 +142,7 @@

    {{{i18n "mist.popupWindows.sendTransactionConfirmation.parameters"}}}

    {{i18n "mist.popupWindows.sendTransactionConfirmation.data"}} {{# if params}} - {{{i18n "mist.popupWindows.sendTransactionConfirmation.showDecodedParameters"}}} + {{i18n "mist.popupWindows.sendTransactionConfirmation.showDecodedParameters"}} {{else}} {{#if to}} {{#unless (TemplateVar.get "lookingUpFunctionSignature")}} @@ -137,6 +158,7 @@

    {{i18n "mist.popupWindows.sendTransactionConfirmation.data"}} {{/if}} {{/if}} +