diff --git a/browser-extensions/chrome/initial.js b/browser-extensions/chrome/initial.js new file mode 100644 index 000000000..fc12301b5 --- /dev/null +++ b/browser-extensions/chrome/initial.js @@ -0,0 +1,8 @@ +chrome.app.runtime.onLaunched.addListener(function() { + chrome.app.window.create('index.html', { + 'bounds': { + 'width': 1200, + 'height': 800 + } + }); +}); diff --git a/browser-extensions/chrome/manifest.json b/browser-extensions/chrome/manifest.json index 7b8e6517f..6c9388801 100644 --- a/browser-extensions/chrome/manifest.json +++ b/browser-extensions/chrome/manifest.json @@ -3,17 +3,16 @@ "name": "Copay", "description": "A multisignature bitcoin wallet", "version": "APP_VERSION", - "homepage_url": "http://bitpay.github.io/copay", - "browser_action": { - "default_title": "Copay", - "default_icon": "img/icons/icon.png", - "default_popup": "popup.html" - }, - "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", "permissions": [ - "tabs", "storage", "notifications" + "storage", + "notifications", + "videoCapture" ], - "options_page": "index.html#/settings", + "app": { + "background": { + "scripts": ["initial.js"] + } + }, "icons": { "128": "img/icons/icon.png" } diff --git a/browser-extensions/include b/browser-extensions/include index 346645ba0..5a43929ed 100644 --- a/browser-extensions/include +++ b/browser-extensions/include @@ -6,9 +6,11 @@ sound views config.js init.js +initial.js version.js index.html popup.html lib/angular/angular-csp.css lib/angular/angular.min.js.map lib/angular-route/angular-route.min.js.map +lib/angular-touch/angular-touch.min.js.map diff --git a/css/src/main.css b/css/src/main.css index 4a4b32666..e83fc982d 100644 --- a/css/src/main.css +++ b/css/src/main.css @@ -22,6 +22,7 @@ color: #B7C2CD; } + #qr-canvas { display: none; } #qrcode-scanner-video { display: block; @@ -60,6 +61,7 @@ body, html{ height:100%; width:100%; color: #2C3E50; + -webkit-user-select: text; } header { diff --git a/index.html b/index.html index 5aac51371..b349ec383 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,7 @@ +
Loading...
diff --git a/init.js b/init.js index c4cb7298a..c56005eb9 100644 --- a/init.js +++ b/init.js @@ -1,7 +1,12 @@ -var ld = (document.all); +var isChromeApp = window.chrome && chrome.runtime && chrome.runtime.id; +var ld; +if (!isChromeApp) { + ld = (document.all); +} + var ns4 = document.layers; -var ns6 = document.getElementById && !document.all; -var ie4 = document.all; +var ns6 = !isChromeApp && document.getElementById && !document.all; +var ie4 = !isChromeApp && document.all; if (ns4) { ld = document.loading; } else if (ns6) { @@ -15,6 +20,10 @@ function init() { ld.visibility = "hidden"; } else if (ns6 || ie4) { ld.display = "none"; + } else { + ld = document.getElementById("loading").style; + ld.visibility = "hidden"; + ld.display = "none"; } } init(); diff --git a/initial.js b/initial.js new file mode 100644 index 000000000..fc12301b5 --- /dev/null +++ b/initial.js @@ -0,0 +1,8 @@ +chrome.app.runtime.onLaunched.addListener(function() { + chrome.app.window.create('index.html', { + 'bounds': { + 'width': 1200, + 'height': 800 + } + }); +}); diff --git a/js/app.js b/js/app.js index 4ff3e5ba9..e2ef9e359 100644 --- a/js/app.js +++ b/js/app.js @@ -3,18 +3,25 @@ var copay = require('copay'); var _ = require('lodash'); var config = defaultConfig; -var localConfig = JSON.parse(localStorage.getItem('config')); +var LS = require('../js/plugins/LocalStorage'); +var ls = new LS(); + +var localConfig; var defaults = JSON.parse(JSON.stringify(defaultConfig)); -if (localConfig) { - var cmv = copay.version.split('.')[1]; - var lmv = localConfig.version ? localConfig.version.split('.')[1] : '-1'; - if (cmv === lmv) { - _.each(localConfig, function(value, key) { - config[key] = value; - }); + +ls.getItem('config', function(err, data) { + localConfig = JSON.parse(data); + if (localConfig) { + var cmv = copay.version.split('.')[1]; + var lmv = localConfig.version ? localConfig.version.split('.')[1] : '-1'; + if (cmv === lmv) { + _.each(localConfig, function(value, key) { + config[key] = value; + }); + } } -} +}); var modules = [ 'ngRoute', @@ -47,9 +54,10 @@ copayApp.config(function($sceDelegateProvider) { }); angular.module('ui.gravatar').config([ - 'gravatarServiceProvider', function(gravatarServiceProvider) { + 'gravatarServiceProvider', + function(gravatarServiceProvider) { gravatarServiceProvider.defaults = { - size : 35 + size: 35 }; // Use https endpoint gravatarServiceProvider.secure = true; diff --git a/js/controllers/head.js b/js/controllers/head.js index 58ff90dbc..4d32b7331 100644 --- a/js/controllers/head.js +++ b/js/controllers/head.js @@ -4,6 +4,8 @@ angular.module('copayApp.controllers').controller('HeadController', function($sc $scope.username = $rootScope.iden.getName(); $scope.hoverMenu = false; + var isChromeApp = typeof window !== "undefined" && window.chrome && chrome.runtime && chrome.runtime.id; + $scope.hoverIn = function() { this.hoverMenu = true; }; @@ -30,12 +32,14 @@ angular.module('copayApp.controllers').controller('HeadController', function($sc } }; - // Ensures a graceful disconnect + + //Ensures a graceful disconnect window.onbeforeunload = function() { $scope.signout(); }; $scope.$on('$destroy', function() { + if (isChromeApp) return; window.onbeforeunload = undefined; }); diff --git a/js/controllers/settings.js b/js/controllers/settings.js index 012605b0a..05da4110c 100644 --- a/js/controllers/settings.js +++ b/js/controllers/settings.js @@ -11,7 +11,6 @@ angular.module('copayApp.controllers').controller('SettingsController', function $scope.availableLogLevels = []; - for (var key in logLevels) { $scope.availableLogLevels.push({ 'name': key @@ -63,7 +62,6 @@ angular.module('copayApp.controllers').controller('SettingsController', function $scope.insightLivenet = copay.Insight.setCompleteUrl($scope.insightLivenet); $scope.insightTestnet = copay.Insight.setCompleteUrl($scope.insightTestnet); - var insightSettings = { livenet: { url: $scope.insightLivenet, @@ -95,6 +93,7 @@ angular.module('copayApp.controllers').controller('SettingsController', function }), function() { applicationService.restart(); }); + }; diff --git a/js/plugins/LocalStorage.js b/js/plugins/LocalStorage.js index 8e4ee75e3..10b84293b 100644 --- a/js/plugins/LocalStorage.js +++ b/js/plugins/LocalStorage.js @@ -1,58 +1,109 @@ 'use strict'; var _ = require('lodash'); var preconditions = require('preconditions').singleton(); +var isChromeApp = typeof window !== "undefined" && window.chrome && chrome.runtime && chrome.runtime.id; + function LocalStorage(opts) { this.type = 'DB'; opts = opts || {}; - - this.ls = opts.ls - || ( (typeof localStorage !== 'undefined') ? localStorage : null ); + + + + this.ls = opts.ls || + ((typeof localStorage !== "undefined") ? localStorage : null); + + if (isChromeApp && !this.ls) { + this.ls = localStorage = chrome.storage.local; + window.localStorage = chrome.storage.local; + } preconditions.checkState(this.ls, 'localstorage not available, cannot run plugin'); }; -LocalStorage.prototype.init = function() { -}; +LocalStorage.prototype.init = function() {}; LocalStorage.prototype.setCredentials = function(email, password, opts) { // NOP }; -LocalStorage.prototype.getItem = function(k,cb) { - preconditions.checkArgument(_.isFunction(cb)); - return cb(null, this.ls.getItem(k)); +LocalStorage.prototype.getItem = function(k, cb) { + if (isChromeApp) { + chrome.storage.local.get(k, + function(data) { + //TODO check for errors + return cb(null, data[k]); + }); + } else { + return cb(null, this.ls.getItem(k)); + } }; /** * Same as setItem, but fails if an item already exists */ LocalStorage.prototype.createItem = function(name, value, callback) { - preconditions.checkArgument(_.isFunction(callback)); - if (this.ls.getItem(name)) { - return callback('EEXISTS'); + var self = this; + self.getItem(name, + function(err, data) { + if (data) { + return callback('EEXISTS'); + } else { + return self.setItem(name, value, callback); + } + }); +}; + +LocalStorage.prototype.setItem = function(k, v, cb) { + if (isChromeApp) { + var obj = {}; + obj[k] = v; + + chrome.storage.local.set(obj, cb); + } else { + this.ls.setItem(k, v); + return cb(); } - return this.setItem(name, value, callback); + }; -LocalStorage.prototype.setItem = function(k,v,cb) { - preconditions.checkArgument(_.isFunction(cb)); - this.ls.setItem(k,v); +LocalStorage.prototype.removeItem = function(k, cb) { + if (isChromeApp) { + chrome.storage.remove(k, cb); + } else { + this.ls.removeItem(k); + return cb(); + } + +}; + +LocalStorage.prototype.clear = function(cb) { + if (isChromeApp) { + chrome.storage.clear(); + } else { + this.ls.clear(); + } return cb(); }; -LocalStorage.prototype.removeItem = function(k,cb) { - preconditions.checkArgument(_.isFunction(cb)); - this.ls.removeItem(k); - return cb(); +LocalStorage.prototype.allKeys = function(cb) { + if (isChromeApp) { + chrome.storage.local.get(null, function(items) { + return cb(null, _.keys(items)); + }); + } else { + var ret = []; + var l = this.ls.length; + + for (var i = 0; i < l; i++) + ret.push(this.ls.key(i)); + + return cb(null, ret); + } }; -LocalStorage.prototype.clear = function(cb) { - preconditions.checkArgument(_.isFunction(cb)); - this.ls.clear(); - return cb(); -}; + module.exports = LocalStorage; diff --git a/js/routes.js b/js/routes.js index 12d8b93f2..39a207902 100644 --- a/js/routes.js +++ b/js/routes.js @@ -1,5 +1,8 @@ 'use strict'; +var LS = require('../js/plugins/LocalStorage'); +var ls = new LS(); + //Setting up route angular .module('copayApp') @@ -118,7 +121,9 @@ angular uriHandler.register(); } $rootScope.$on('$routeChangeStart', function(event, next, current) { - if (!localStorage || localStorage.length < 1) { + + + if (!ls || ls.length < 1) { $location.path('unsupported'); } else { if (!$rootScope.iden && next.logged) { diff --git a/js/services/applicationService.js b/js/services/applicationService.js index 3612bf0d1..4c3bade19 100644 --- a/js/services/applicationService.js +++ b/js/services/applicationService.js @@ -1,17 +1,23 @@ 'use strict'; angular.module('copayApp.services') .factory('applicationService', function() { - var root = {}; + var root = {}; + var isChromeApp = window.chrome && chrome.runtime && chrome.runtime.id; - root.restart = function() { - // Go home reloading the application - var hashIndex = window.location.href.indexOf('#!/'); + root.restart = function() { + + // Go home reloading the application + var hashIndex = window.location.href.indexOf('#!/'); + if (isChromeApp) { + chrome.runtime.reload(); + } else { window.location = window.location.href.substr(0, hashIndex); - }; + } + }; - root.reload = function() { - window.location.reload(); - }; + root.reload = function() { + window.location.reload(); + }; - return root; + return root; }); diff --git a/js/services/notifications.js b/js/services/notifications.js index 3f5b324ea..7e602c4f9 100644 --- a/js/services/notifications.js +++ b/js/services/notifications.js @@ -1,12 +1,21 @@ 'use strict'; +var LS = require('../js/plugins/LocalStorage'); +var ls = new LS(); + angular.module('copayApp.services'). factory('notification', ['$timeout', function($timeout) { - var notifications = JSON.parse(localStorage.getItem('notifications')) || [], - queue = []; + var notifications = []; + ls.getItem('notifications', function(err, data) { + if (data) { + notifications = JSON.parse(data); + } + }); + + var queue = []; var settings = { info: { duration: 6000, diff --git a/js/util/log.js b/js/util/log.js index 220fc2a02..31a5a3ba5 100644 --- a/js/util/log.js +++ b/js/util/log.js @@ -5,7 +5,7 @@ var ls; try { var LS = require('../js/plugins/LocalStorage'); ls = new LS(); -} catch(e) {}; +} catch (e) {}; /** * @desc @@ -126,6 +126,8 @@ var error = new Error(); var logLevel = config.logLevel || 'info'; + + if (ls && ls.getItem) { ls.getItem("config", function(err, value) { if (err) return; diff --git a/views/settings.html b/views/settings.html index 9c2d20fdd..5f962a066 100644 --- a/views/settings.html +++ b/views/settings.html @@ -64,7 +64,7 @@
- Log level shows information on the console. This is usefull to find bugs and help users. 'debug' is the most verbose level while 'fatal' only shows unexcpected errors + Log level shows information on the console. This is useful to find bugs and to help users. 'debug' is the most verbose level while 'fatal' only shows unexpected errors