diff --git a/src/js/controllers/preferencesLogs.js b/src/js/controllers/preferencesLogs.js index 7d356e615..5ee106bf0 100644 --- a/src/js/controllers/preferencesLogs.js +++ b/src/js/controllers/preferencesLogs.js @@ -1,39 +1,73 @@ 'use strict'; angular.module('copayApp.controllers').controller('preferencesLogs', - function($scope, historicLog, platformInfo) { + function($scope, historicLog, lodash, configService, gettextCatalog) { + + var config = configService.getSync(); + var logLevels = historicLog.getLevels(); + var selectedLevel; + + $scope.logOptions = lodash.indexBy(logLevels, 'level'); + + var filterLogs = function(weight) { + $scope.filteredLogs = historicLog.get(weight); + }; + + $scope.setOptionSelected = function(level) { + var weight = $scope.logOptions[level].weight; + $scope.fillClass = 'fill-bar-' + level; + filterLogs(weight); + lodash.each($scope.logOptions, function(opt) { + opt.selected = opt.weight <= weight ? true : false; + opt.head = opt.weight == weight; + }); + + // Save the setting. + var opts = { + log: { + filter: level + } + }; + configService.set(opts, function(err) { + if (err) $log.debug(err); + }); + }; + + $scope.prepareLogs = function() { + var log = 'Copay Session Logs\n Be careful, this could contain sensitive private data\n\n'; + log += '\n\n'; + log += historicLog.get().map(function(v) { + return '[' + v.timestamp + '][' + v.level + ']' + v.msg; + }).join('\n'); + + return log; + }; + + $scope.sendLogs = function() { + var body = $scope.prepareLogs(); + + window.plugins.socialsharing.shareViaEmail( + body, + 'Copay Logs', + null, // TO: must be null or an array + null, // CC: must be null or an array + null, // BCC: must be null or an array + null, // FILES: can be null, a string, or an array + function() {}, + function() {} + ); + }; + + $scope.showOptionsMenu = function() { + $scope.showOptions = true; + }; $scope.$on("$ionicView.beforeEnter", function(event, data) { - $scope.isCordova = platformInfo.isCordova; + selectedLevel = lodash.has(config, 'log.filter') ? historicLog.getLevel(config.log.filter) : historicLog.getDefaultLevel(); + $scope.setOptionSelected(selectedLevel.level); }); $scope.$on("$ionicView.enter", function(event, data) { - $scope.logs = historicLog.get(); - - $scope.prepare = function() { - var log = 'Copay Session Logs\n Be careful, this could contain sensitive private data\n\n'; - log += '\n\n'; - log += $scope.logs.map(function(v) { - return v.msg; - }).join('\n'); - - return log; - }; - - $scope.sendLogs = function() { - var body = $scope.prepare(); - - window.plugins.socialsharing.shareViaEmail( - body, - 'Copay Logs', - null, // TO: must be null or an array - null, // CC: must be null or an array - null, // BCC: must be null or an array - null, // FILES: can be null, a string, or an array - function() {}, - function() {} - ); - }; - + filterLogs(selectedLevel.weight); }); }); diff --git a/src/js/directives/logOptions.js b/src/js/directives/logOptions.js new file mode 100644 index 000000000..6b00cdc0c --- /dev/null +++ b/src/js/directives/logOptions.js @@ -0,0 +1,29 @@ +'use strict'; + +angular.module('copayApp.directives') + .directive('logOptions', function($timeout, platformInfo) { + return { + restrict: 'E', + templateUrl: 'views/includes/logOptions.html', + transclude: true, + scope: { + show: '=logOptionsShow', + options: '=logOptions', + fillClass: '=logOptionsFillClass', + onSelect: '=logOptionsOnSelect', + onCopy: '=logOptionsOnCopy', + onSend: '=logOptionsOnSend' + }, + link: function(scope, element, attrs) { + scope.isCordova = platformInfo.isCordova; + + scope.hide = function() { + scope.show = false; + }; + + scope.getFillClass = function(index) { + scope.onSelect(index); + }; + } + }; + }); diff --git a/src/js/routes.js b/src/js/routes.js index b557a6282..4904be9f6 100644 --- a/src/js/routes.js +++ b/src/js/routes.js @@ -44,7 +44,8 @@ angular.module('copayApp').config(function(historicLogProvider, $provide, $logPr function($delegate, platformInfo) { var historicLog = historicLogProvider.$get(); - ['debug', 'info', 'warn', 'error', 'log'].forEach(function(level) { + historicLog.getLevels().forEach(function(levelDesc) { + var level = levelDesc.level; if (platformInfo.isDevel && level == 'error') return; var orig = $delegate[level]; diff --git a/src/js/services/configService.js b/src/js/services/configService.js index c128208c4..a17fcf628 100644 --- a/src/js/services/configService.js +++ b/src/js/services/configService.js @@ -87,6 +87,10 @@ angular.module('copayApp.services').factory('configService', function(storageSer emailNotifications: { enabled: false, }, + + log: { + filter: 'debug', + }, }; var configCache = null; diff --git a/src/js/services/historicLog.js b/src/js/services/historicLog.js index f0cd8e5a5..88446ae54 100644 --- a/src/js/services/historicLog.js +++ b/src/js/services/historicLog.js @@ -1,18 +1,54 @@ 'use strict'; var logs = []; angular.module('copayApp.services') - .factory('historicLog', function historicLog() { + .factory('historicLog', function historicLog(lodash) { var root = {}; + var levels = [ + { level: 'error', weight: 0, label: 'Error'}, + { level: 'warn', weight: 1, label: 'Warning'}, + { level: 'info', weight: 2, label: 'Info', default: true}, + { level: 'debug', weight: 3, label: 'Debug'} + ]; + + // Create an array of level weights for performant filtering. + var weight = {}; + for (var i = 0; i < levels.length; i++) { + weight[levels[i].level] = levels[i].weight; + } + + root.getLevels = function() { + return levels; + }; + + root.getLevel = function(level) { + return lodash.find(levels, function(l) { + return l.level == level; + }); + }; + + root.getDefaultLevel = function() { + return lodash.find(levels, function(l) { + return l.default; + }); + }; + root.add = function(level, msg) { logs.push({ + timestamp: new Date().toISOString(), level: level, msg: msg, }); }; - root.get = function() { - return logs; + root.get = function(filterWeight) { + var filteredLogs = logs; + if (filterWeight != undefined) { + filteredLogs = lodash.filter(logs, function(l) { + return weight[l.level] <= filterWeight; + }); + } + return filteredLogs; }; return root; diff --git a/src/js/services/profileService.js b/src/js/services/profileService.js index ae44c7b26..d5598a5b8 100644 --- a/src/js/services/profileService.js +++ b/src/js/services/profileService.js @@ -134,7 +134,7 @@ angular.module('copayApp.services') wallet.setNotificationsInterval(UPDATE_PERIOD); wallet.openWallet(function(err) { if (wallet.status !== true) - $log.log('Wallet + ' + walletId + ' status:' + wallet.status) + $log.debug('Wallet + ' + walletId + ' status:' + wallet.status) }); }); diff --git a/src/sass/directives/directives.scss b/src/sass/directives/directives.scss index 79e3edbd3..9159d3f23 100644 --- a/src/sass/directives/directives.scss +++ b/src/sass/directives/directives.scss @@ -1 +1 @@ -@import "gravatar" +@import "gravatar"; diff --git a/src/sass/views/includes/checkBar.scss b/src/sass/views/includes/checkBar.scss new file mode 100644 index 000000000..337635505 --- /dev/null +++ b/src/sass/views/includes/checkBar.scss @@ -0,0 +1,62 @@ +#check-bar { + $bar-widths: ( + // defined by user, example: + // error: 10%, + // warn: 37%, + // info: 65%, + // debug: 90% + ); + .item { + padding: 40px; + } + .checkbox-icon { + width: 22px; + height: 22px; + } + .checkbox-icon:after { + border: none; + } + .check-bar { + position: relative; + .initial-bar-default { + border: 2px solid $v-success-color; + width: 0%; + top: 40px; + z-index: 99; + border-radius: 10px; + position: absolute; + } + .initial-bar { + } + @each $name, $bar-width in $bar-widths { + .fill-bar-#{$name} { + width: $bar-width !important; + transition: width .2s; + } + } + .base-bar { + position: absolute; + width: 100%; + border-bottom: 2px solid #f2f2f2; + top: 41px; + z-index: 98; + left: 0px; + } + &.list { + margin-top: 20px; + display: flex; + justify-content: space-between; + .custom-checkbox { + text-align: -webkit-center; + .item { + border-style: none; + z-index: 999; + background-color: inherit; + } + label { + padding-top: 5px; + } + } + } + } +} diff --git a/src/sass/views/includes/logOptions.scss b/src/sass/views/includes/logOptions.scss new file mode 100644 index 000000000..89dfef121 --- /dev/null +++ b/src/sass/views/includes/logOptions.scss @@ -0,0 +1,88 @@ +log-options { + + $border-color: #EFEFEF; + + .bp-action-sheet__sheet { + padding-left: 2rem; + padding-right: .75rem; + } + + .entry { + border: 0; + padding-right: 0; + padding-top: 0; + padding-bottom: 0; + margin-bottom: 1px; + overflow: visible; + + > i { + color: $v-accent-color; + padding: 0 0 5px 0; + margin-left: -5px; + + > img { + height: 39px; + width: 39px; + padding: 4px; + } + } + } + + .entry-inner { + display: flex; + position: relative; + padding-top: 16px; + padding-bottom: 16px; + + &::after { + display: block; + position: absolute; + width: 100%; + height: 1px; + background: $border-color; + bottom: 0; + right: 0; + content: ''; + } + } + + .entry-details { + flex-grow: 1; + + .entry-name { + padding-bottom: 5px; + } + } + + #check-bar { + $bar-widths: ( + // Order must match weight, see services/historicLog.js + error: 10%, + warn: 35%, + info: 65%, + debug: 90% + ); + .check-bar { + .initial-bar { + border: 2px solid $v-accent-color; + } + @each $name, $bar-width in $bar-widths { + .fill-bar-#{$name} { + width: $bar-width !important; + } + } + } + + .head .checkbox-icon { + width: 22px; + height: 22px; + top: 0; + } + + .checkbox-icon { + width: 10px; + height: 10px; + top: 6px; + } + } +} diff --git a/src/sass/views/tab-settings.scss b/src/sass/views/tab-settings.scss index 67283b585..6e17b4d37 100644 --- a/src/sass/views/tab-settings.scss +++ b/src/sass/views/tab-settings.scss @@ -123,6 +123,20 @@ color: #00901B; } } + .log-bg { + background: white; + } + .log-entry { + font-size: 12px; + line-height: 18px; + border: none; + } + .log-timestamp { + font-weight: bold; + } + .log-level { + font-weight: bold; + } } #tab-settings { diff --git a/src/sass/views/views.scss b/src/sass/views/views.scss index cbae9b19f..722c97e1d 100644 --- a/src/sass/views/views.scss +++ b/src/sass/views/views.scss @@ -48,3 +48,5 @@ @import "integrations/integrations"; @import "custom-amount"; @import "includes/pin"; +@import "includes/logOptions"; +@import "includes/checkBar"; diff --git a/www/views/includes/checkBar.html b/www/views/includes/checkBar.html new file mode 100644 index 000000000..38b3637ea --- /dev/null +++ b/www/views/includes/checkBar.html @@ -0,0 +1,10 @@ +
+
+
+
+
+ + +
+
+
diff --git a/www/views/includes/logOptions.html b/www/views/includes/logOptions.html new file mode 100644 index 000000000..3729bba38 --- /dev/null +++ b/www/views/includes/logOptions.html @@ -0,0 +1,21 @@ + + +
Log options
+
+ + +
+
+
Copy to clipboard
+
+
+
+ + +
+
+
Send by email
+
+
+
+
diff --git a/www/views/preferencesLogs.html b/www/views/preferencesLogs.html index 91e3e3261..84d0528ac 100644 --- a/www/views/preferencesLogs.html +++ b/www/views/preferencesLogs.html @@ -1,23 +1,23 @@ - + {{'Session Log' | translate}} + + + - -
- - -
+
-
-
    -
  • - +
    +
    No entries for this log level filter setting.
    +
      +
    • + + [{{l.timestamp}}] + [{{l.level}}] {{l.msg}}
    • @@ -25,4 +25,12 @@
+ +