diff --git a/.gitignore b/.gitignore index 7f26c1ac0..3ada4312c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,11 @@ src/js/version.js cordova/project/* cordova/*.keystore +# node-webkit +cache +webkitbuilds/* +!webkitbuilds/README.md + # chrome extensions browser-extensions/chrome/copay-chrome-extension browser-extensions/chrome/copay-chrome-extension.zip diff --git a/Gruntfile.js b/Gruntfile.js index ff1368ead..3555d86cb 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -184,6 +184,16 @@ module.exports = function(grunt) { force: true, recursive: false } + }, + nodewebkit: { + options: { + platforms: ['win','osx','linux'], + buildDir: './webkitbuilds', + version: '0.12.2', + macIcns: './public/img/icons/icon.icns', + exeIco: './public/img/icons/icon.ico' + }, + src: ['./package.json', './public/**/*'] } }); @@ -197,7 +207,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-karma'); grunt.loadNpmTasks('grunt-karma-coveralls'); - + grunt.loadNpmTasks('grunt-node-webkit-builder'); grunt.registerTask('default', [ 'nggettext_compile', 'exec:version', 'concat', 'copy' @@ -208,4 +218,5 @@ module.exports = function(grunt) { grunt.registerTask('translate', ['nggettext_extract']); grunt.registerTask('test', ['karma:unit']); grunt.registerTask('test-coveralls', ['karma:prod', 'coveralls']); + grunt.registerTask('desktop', ['prod', 'nodewebkit']); }; diff --git a/atom-shell.md b/atom-shell.md deleted file mode 100644 index 0807918fb..000000000 --- a/atom-shell.md +++ /dev/null @@ -1,67 +0,0 @@ - -# Warning - -This NEEDS to be updated. - -## Running in the Native Shell - -Copay can be executed from within a "native" application shell, providing some -additional features such as native menus, notifications, tray integration, etc. -This is accomplished using [Atom Shell](https://github.com/atom/atom-shell). - -To run and test Copay from within this context, first download the atom-shell -package to `shell/bin/{platform}` (ignored by git), by running: - -``` -npm run setup-shell -``` - -Once this script has completed, you can launch the shell-based Copay by running: - -``` -npm run shell -``` - -## Building Native Shell Binaries/Installers (OSX) - -``` -npm run dist -``` - -This script will download atom shell binaries and combine them with Copay sources -to build a DMG for osx-x64, an installer EXE for win32, and a .tar.gz for linux-x64. -It was developed to be run on OSX. The outputs are copied to the dist directory. - -DMG is created with hdiutil -EXE is created with makensis (brew install makensis) - - -# Development - -## Native Shell - -To add features that enhance the native experience of Copay, first follow the -directions above under "Running in the Native Shell". It's important to ensure -that functionality within this context should either hook into existing features -or supplement the experience of those features. Copay should continue to operate -full-featured from within a modern web browser. - -Shell functionality works by sending and receiving messages between the Copay -application and the shell wrapper. Native functionality should be handled mostly -from within `shell/lib/message-handler.js`, which receives messages conditionally -from the front-end Angular controllers. - -Look at `js/shell.js` to see how we determine if Copay is running from within the -native shell context. If we are running within the shell, Copay has access to the -global variable `window.cshell`, which provides access to the messenger. For -instance, to Copay might want to use a native dialog alert in favor of a regular -one if running in this context. You would do this like so: - -```js -if (window.cshell) { - window.cshell.send('alert', 'info', 'Please select a wallet.'); -} -else { - window.alert('Please select a wallet.'); -} -``` diff --git a/bower.json b/bower.json index a741c8997..abfa19ab7 100644 --- a/bower.json +++ b/bower.json @@ -15,7 +15,8 @@ "foundation": "zurb/bower-foundation#~5.5.1", "foundation-icon-fonts": "*", "ng-lodash": "~0.2.0", - "angular-moment": "~0.9.0", + "angular-moment": "~0.10.1", + "moment": "~2.10.3", "angular-bitcore-wallet-client": "^0.0.23", "angular-ui-router": "~0.2.13", "qrcode-decoder-js": "*", diff --git a/package.json b/package.json index 17d491125..b65fbcd07 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,23 @@ "bitcoin", "bitcore" ], - "main": "app.js", + "main": "public/index.html", + "window": { + "toolbar": false, + "show": true, + "visible": true, + "resizable": false, + "frame": true, + "width": 400, + "height": 600, + "position": "center", + "fullscreen": false + }, + "webkit": { + "page-cache": false, + "java": false, + "plugin": false + }, "id": "jid1-x7bV5evAaI1P9Q", "homepage": "https://github.com/bitpay/copay", "license": "MIT", @@ -45,6 +61,7 @@ "angular-mocks": "^1.3.14", "grunt-karma": "^0.10.1", "grunt-karma-coveralls": "^2.5.3", + "grunt-node-webkit-builder": "^1.0.2", "karma": "^0.12.31", "karma-cli": "0.0.4", "karma-coverage": "^0.2.7", diff --git a/public/img/icons/icon.icns b/public/img/icons/icon.icns new file mode 100644 index 000000000..8fc084681 Binary files /dev/null and b/public/img/icons/icon.icns differ diff --git a/public/img/icons/icon.ico b/public/img/icons/icon.ico new file mode 100644 index 000000000..e40c22dcf Binary files /dev/null and b/public/img/icons/icon.ico differ diff --git a/src/js/controllers/walletHome.js b/src/js/controllers/walletHome.js index 33302d135..789db0e6e 100644 --- a/src/js/controllers/walletHome.js +++ b/src/js/controllers/walletHome.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $timeout, $filter, $modal, $log, notification, txStatus, isCordova, profileService, lodash, configService, rateService, storageService, bitcore, isChromeApp, gettext, gettextCatalog) { +angular.module('copayApp.controllers').controller('walletHomeController', function($scope, $rootScope, $timeout, $filter, $modal, $log, notification, txStatus, isCordova, profileService, lodash, configService, rateService, storageService, bitcore, isChromeApp, gettext, gettextCatalog, nodeWebkit) { var self = this; $rootScope.hideMenuBar = false; @@ -381,6 +381,8 @@ angular.module('copayApp.controllers').controller('walletHomeController', functi if (isCordova) { window.cordova.plugins.clipboard.copy('bitcoin:' + addr); window.plugins.toast.showShortCenter('Copied to clipboard'); + } else if (nodeWebkit.isDefined()) { + nodeWebkit.writeToClipboard(addr); } }; diff --git a/src/js/routes.js b/src/js/routes.js index ef052f64f..288ad5585 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -374,7 +374,7 @@ angular needProfile: false }); }) - .run(function($rootScope, $state, $log, gettextCatalog, uriHandler, isCordova, amMoment, profileService, $timeout) { + .run(function($rootScope, $state, $log, gettextCatalog, uriHandler, isCordova, amMoment, profileService, $timeout, nodeWebkit) { FastClick.attach(document.body); // Auto-detect browser language @@ -396,6 +396,18 @@ angular uriHandler.register(); } + if (nodeWebkit.isDefined()) { + var gui = require('nw.gui'); + var win = gui.Window.get(); + var nativeMenuBar = new gui.Menu({ type: "menubar" }); + try { + nativeMenuBar.createMacBuiltin("Copay"); + } catch(e) { + $log.debug('This is not OSX'); + } + win.menu = nativeMenuBar; + } + var pageWeight = { walletHome: 0, copayers: -1, diff --git a/src/js/services/go.js b/src/js/services/go.js index d9fd0a4b3..3e55e2939 100644 --- a/src/js/services/go.js +++ b/src/js/services/go.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('copayApp.services').factory('go', function($window, $rootScope, $location, $state, profileService) { +angular.module('copayApp.services').factory('go', function($window, $rootScope, $location, $state, profileService, nodeWebkit) { var root = {}; var hideSidebars = function() { @@ -30,7 +30,12 @@ angular.module('copayApp.services').factory('go', function($window, $rootScope, }; root.openExternalLink = function(url) { - var ref = window.open(url, '_blank', 'location=no'); + if (nodeWebkit.isDefined()) { + nodeWebkit.openExternalLink(url); + } + else { + var ref = window.open(url, '_blank', 'location=no'); + } }; root.path = function(path, cb) { diff --git a/src/js/services/localStorage.js b/src/js/services/localStorage.js index 1d8c55b11..853677159 100644 --- a/src/js/services/localStorage.js +++ b/src/js/services/localStorage.js @@ -1,11 +1,11 @@ 'use strict'; angular.module('copayApp.services') - .factory('localStorageService', function(isChromeApp, $timeout) { + .factory('localStorageService', function(isChromeApp, nodeWebkit, $timeout) { var root = {}; var ls = ((typeof window.localStorage !== "undefined") ? window.localStorage : null); - if (isChromeApp && !ls) { + if (isChromeApp && !nodeWebkit.isDefined() && !ls) { ls = localStorage = chrome.storage.local; window.localStorage = chrome.storage.local; } @@ -14,7 +14,7 @@ angular.module('copayApp.services') throw new Error('localstorage not available'); root.get = function(k, cb) { - if (isChromeApp) { + if (isChromeApp && !nodeWebkit.isDefined()) { chrome.storage.local.get(k, function(data) { //TODO check for errors @@ -40,7 +40,7 @@ angular.module('copayApp.services') }; root.set = function(k, v, cb) { - if (isChromeApp) { + if (isChromeApp && !nodeWebkit.isDefined()) { var obj = {}; obj[k] = v; @@ -53,7 +53,7 @@ angular.module('copayApp.services') }; root.remove = function(k, cb) { - if (isChromeApp) { + if (isChromeApp && !nodeWebkit.isDefined()) { chrome.storage.local.remove(k, cb); } else { ls.removeItem(k); diff --git a/src/js/services/logHeader.js b/src/js/services/logHeader.js index 698c442a2..cec37bf5e 100644 --- a/src/js/services/logHeader.js +++ b/src/js/services/logHeader.js @@ -1,8 +1,8 @@ 'use strict'; angular.module('copayApp.services') - .factory('logHeader', function($log, isChromeApp, isCordova) { + .factory('logHeader', function($log, isChromeApp, isCordova, nodeWebkit) { $log.info('Starting Copay v' + window.version + ' #' + window.commitHash); - $log.info('Client: isCordova:', isCordova, 'isChromeApp:', isChromeApp); + $log.info('Client: isCordova:', isCordova, 'isChromeApp:', isChromeApp, 'isNodeWebkit:', nodeWebkit.isDefined()); $log.info('Navigator:', navigator.userAgent); return {}; }); diff --git a/src/js/services/nodeWebkit.js b/src/js/services/nodeWebkit.js new file mode 100644 index 000000000..580d502eb --- /dev/null +++ b/src/js/services/nodeWebkit.js @@ -0,0 +1,42 @@ +'use strict'; + +angular.module('copayApp.services').factory('nodeWebkit', function nodeWebkitFactory() { + var root = {}; + + var isNodeWebkit = function() { + var isNode = (typeof process !== "undefined" && typeof require !== "undefined"); + if(isNode) { + try { + return (typeof require('nw.gui') !== "undefined"); + } catch(e) { + return false; + } + } + }; + + root.isDefined = function() { + return isNodeWebkit(); + }; + + root.readFromClipboard = function() { + if (!isNodeWebkit()) return; + var gui = require('nw.gui'); + var clipboard = gui.Clipboard.get(); + return clipboard.get(); + }; + + root.writeToClipboard = function(text) { + if (!isNodeWebkit()) return; + var gui = require('nw.gui'); + var clipboard = gui.Clipboard.get(); + return clipboard.set(text); + }; + + root.openExternalLink = function(url) { + if (!isNodeWebkit()) return; + var gui = require('nw.gui'); + return gui.Shell.openExternal(url); + }; + + return root; +}); diff --git a/webkitbuilds/README.md b/webkitbuilds/README.md new file mode 100644 index 000000000..efb169b03 --- /dev/null +++ b/webkitbuilds/README.md @@ -0,0 +1,14 @@ +# Copay NW.js + +NW.js (also know as node-webkit) is an app runtime based on `Chromium` and `node.js`. To build desktop native application, Copay uses NW.js. + +## Building Copay for OSX, Linux and Windows versions + +**Requirements** + + - Install NW.js in your system from [nw.js](http://nwjs.io/) + +**Builder** + + - Try `grunt desktop` (*) +