Skip to content
Snippets Groups Projects
settings-controllers.js 15.51 KiB

angular.module('cesium.settings.controllers', ['cesium.services', 'cesium.currency.controllers'])
  .config(function($stateProvider) {
    'ngInject';

    $stateProvider

      .state('app.settings', {
        url: "/settings",
        views: {
          'menuContent': {
            templateUrl: "templates/settings/settings.html",
            controller: 'SettingsCtrl'
          }
        }
      })
    ;
  })

  .controller('SettingsCtrl', SettingsController)
;

function SettingsController($scope, $q, $window, $ionicHistory, $ionicPopup, $timeout, $translate, $ionicPopover,
                            UIUtils, Modals, BMA, csHttp, csConfig, csCurrency, csSettings, csPlatform) {
  'ngInject';

  $scope.formData = angular.copy(csSettings.data);
  $scope.popupData = {}; // need for the node popup
  $scope.loading = true;
  $scope.nodePopup = {};
  $scope.bma = BMA;
  $scope.listeners = [];
  $scope.platform = {
    loading: !csPlatform.isStarted(),
    loadingMessage: 'COMMON.LOADING'
  };
  $scope.timeouts = csSettings.timeouts;
  $scope.keepAuthIdleLabels = {
    /*0: {
      labelKey: 'SETTINGS.KEEP_AUTH_OPTION.NEVER'
    },*/
    10: {
      labelKey: 'SETTINGS.KEEP_AUTH_OPTION.SECONDS',
      labelParams: {value: 10}
    },
    30: {
      labelKey: 'SETTINGS.KEEP_AUTH_OPTION.SECONDS',
      labelParams: {value: 30}
    },
    60: {
      labelKey: 'SETTINGS.KEEP_AUTH_OPTION.MINUTE',
      labelParams: {value: 1}
    },
    600: {
      labelKey: 'SETTINGS.KEEP_AUTH_OPTION.MINUTES',
      labelParams: {value: 10}
    },
    3600: {
      labelKey: 'SETTINGS.KEEP_AUTH_OPTION.HOUR',
      labelParams: {value: 1}
    },
    9999: {
      labelKey: 'SETTINGS.KEEP_AUTH_OPTION.ALWAYS'
    }
  };
  $scope.keepAuthIdles = _.keys($scope.keepAuthIdleLabels);

  $scope.blockValidityWindowLabels = {
    0: {
      labelKey: 'SETTINGS.BLOCK_VALIDITY_OPTION.NONE'
    },
    6: {
      labelKey: 'SETTINGS.BLOCK_VALIDITY_OPTION.N',
      labelParams: {count: 6, time: undefined /*defined in enter*/}
    },
    12: {
      labelKey: 'SETTINGS.BLOCK_VALIDITY_OPTION.N',
      labelParams: {count: 12, time: undefined /*defined in enter*/}
    },
    24: {
      labelKey: 'SETTINGS.BLOCK_VALIDITY_OPTION.N',
      labelParams: {count: 24, time: undefined /*defined in enter*/}
    }
  };
  $scope.blockValidityWindows = _.keys($scope.blockValidityWindowLabels);

  $scope.enter = function() {
    $scope.addListeners();

    $q.all([
      csSettings.ready(),
      csCurrency.parameters()
        .catch(function(err) {
          // Continue (will use default value)
          // Make sure to continue even if node is down - Fix #788
        })
        .then(function(parameters) {
          var avgGenTime = parameters && parameters.avgGenTime;
          if (!avgGenTime || avgGenTime < 0) {
            console.warn('[settings] Could not not currency parameters. Using default G1 \'avgGenTime\' (300s)');
            avgGenTime = 300; /* = G1 value = 5min */
          }
          _.each($scope.blockValidityWindows, function(blockCount) {
            if (blockCount > 0) {
              $scope.blockValidityWindowLabels[blockCount].labelParams.time = avgGenTime * blockCount;
            }
          });
        })
    ])
    .then($scope.load);
  };

  $scope.setPopupForm = function(popupForm) {
    $scope.popupForm = popupForm;
  };

  $scope.load = function() {
    $scope.loading = true; // to avoid the call of csWallet.store()

    $scope.platform.loading = !csPlatform.isStarted();

    // Fill locales
    $scope.locales = angular.copy(csSettings.locales);



    // Apply settings
    angular.merge($scope.formData, csSettings.data);

    // Make sure to use full locale object (id+name)
    $scope.formData.locale = (csSettings.data.locale && csSettings.data.locale.id && _.findWhere($scope.locales, {id: csSettings.data.locale.id})) ||
      _.findWhere($scope.locales, {id: csSettings.defaultSettings.locale.id});


    return $timeout(function() {
      $scope.loading = false;
      // Set Ink
      UIUtils.ink({selector: '.item'});
      $scope.showHelpTip();
    }, 100);
  };


  $scope.addListeners = function() {
    $scope.listeners = [
      // Listen platform start message
      csPlatform.api.start.on.message($scope, function(message) {
        $scope.platform.loading = !csPlatform.isStarted();
        $scope.platform.loadingMessage = message;
      })
    ];
  };

  $scope.leave = function() {
    console.debug('[settings] Leaving page');
    $scope.removeListeners();
  };

  $scope.reset = function() {
    if ($scope.actionsPopover) {
      $scope.actionsPopover.hide();
    }
    $scope.pendingSaving = true;
    csSettings.reset()
      .then(csPlatform.restart)
      .then(function() {
        // reload
        $scope.load();
        $scope.pendingSaving = false;
      });
  };

  $scope.changeLanguage = function(langKey) {
    $translate.use(langKey);
  };

  $scope.changeExpertMode = function(expertMode) {
    // Restart platform, to auto select node
    if (!expertMode) {
      csPlatform.restart();
    }
  };

  // Change node
  $scope.changeNode = function(node, confirm) {

    // If platform not stared yet: wait then loop
    if (!csPlatform.isStarted()) {
      UIUtils.loading.update({template: this.loadingMessage});
      return csPlatform.ready()
        .then(function() {
          return $scope.changeNode(node, confirm); // Loop
        });
    }

    // Ask user to confirm, before allow to change the node
    if (!confirm && !$scope.formData.expertMode) {
      return UIUtils.alert.confirm('CONFIRM.ENABLE_EXPERT_MODE_TO_CHANGE_NODE', 'CONFIRM.POPUP_WARNING_TITLE', {
        cssClass: 'warning',
        cancelText: 'COMMON.BTN_NO',
        okText: 'COMMON.BTN_YES_CONTINUE',
        okType: 'button-assertive'
      })
      .then(function(confirm) {
        if (!confirm) return;
        $scope.changeNode(node, true);
      });
    }

    // If not given, get node from settings data
    if (!node || !node.host) {
      var host = $scope.formData.node.host;
      if (!host) return; // Should never occur

      var useSsl = angular.isDefined($scope.formData.node.useSsl) ?
        $scope.formData.node.useSsl :
        ($scope.formData.node.port == 443);
      var port = !!$scope.formData.node.port && $scope.formData.node.port != 80 && $scope.formData.node.port != 443 ? $scope.formData.node.port : undefined;
      var path = $scope.formData.node.path || (host.indexOf('/') !== -1 ? host.substring(host.indexOf('/')) : '');
      if (path.endsWith('/')) path = path.substring(0, path.length - 1); // Remove trailing slash
      host = host.indexOf('/') !== -1 ? host.substring(0, host.indexOf('/')) : host; // Remove path from host
      node = {
        host: host,
        port: port,
        path: path,
        useSsl: useSsl
      };
    }

    $scope.showNodePopup(node)
      .then(function(newNode) {
        if (newNode.host === $scope.formData.node.host &&
          newNode.port == $scope.formData.node.port &&
          newNode.path === $scope.formData.node.path &&
          newNode.useSsl === $scope.formData.node.useSsl &&
          !$scope.formData.node.temporary) {
          return; // same node = nothing to do
        }

        // Change to expert mode
        $scope.formData.expertMode = true;

        UIUtils.loading.show();

        BMA.isAlive(newNode)
          .then(function(alive) {
            if (!alive) {
              UIUtils.loading.hide();
              return UIUtils.alert.error('ERROR.INVALID_NODE_SUMMARY')
                .then(function(){
                  $scope.changeNode(newNode, true); // loop
                });
            }
            UIUtils.loading.hide();
            angular.merge($scope.formData.node, newNode);
            delete $scope.formData.node.temporary;
            BMA.stop();
            BMA.copy(newNode);
            $scope.bma = BMA;

            // Restart platform (or start if not already started)
            csPlatform.restart();

            // Reset history cache
            return $ionicHistory.clearCache();
          });
      });
  };

  $scope.showNodeList = function() {
    // Check if need a filter on SSL node
    var forceUseSsl = (csConfig.httpsMode === 'true' || csConfig.httpsMode === true || csConfig.httpsMode === 'force') ||
    ($window.location && $window.location.protocol === 'https:') ? true : false;

    $ionicPopup._popupStack[0].responseDeferred.promise.close();
    return Modals.showNetworkLookup({
      enableFilter: true, // enable filter button
      bma: true, // only BMA node
      ssl: forceUseSsl ? true : undefined
    })
      .then(function (peer) {
        if (peer) {
          var bma = peer.getBMA();
          var host = (bma.dns ? bma.dns :
            (peer.hasValid4(bma) ? bma.ipv4 : bma.ipv6));
          var useSsl = bma.useSsl || bma.port == 443;
          var port = bma.port || (useSsl ? 443 : 80);
          return {
            host: host,
            port: port,
            path: bma.path || '',
            useSsl: useSsl
          };
        }
      })
      .then(function(newNode) {
        $scope.changeNode(newNode, true);
      });
  };

  // Show node popup
  $scope.showNodePopup = function(node) {
    return $q(function(resolve, reject) {
      var useSsl = node.useSsl || node.port == 443;
      var host = (node.port && node.port != 80 && node.port != 443) ? [node.host, node.port].join(':') : node.host;
      if (node.path && node.path.length && node.path !== '/') host += node.path;
      $scope.popupData.newNode = host;
      $scope.popupData.useSsl = useSsl;
      if (!!$scope.popupForm) {
        $scope.popupForm.$setPristine();
      }
      $translate(['SETTINGS.POPUP_PEER.TITLE', 'COMMON.BTN_OK', 'COMMON.BTN_CANCEL'])
        .then(function (translations) {
          // Choose UID popup
          $ionicPopup.show({
            templateUrl: 'templates/settings/popup_node.html',
            title: translations['SETTINGS.POPUP_PEER.TITLE'],
            scope: $scope,
            buttons: [
              { text: translations['COMMON.BTN_CANCEL'] },
              {
                text: translations['COMMON.BTN_OK'],
                type: 'button-positive',
                onTap: function(e) {
                  $scope.popupForm.$submitted=true;
                  if(!$scope.popupForm.$valid || !$scope.popupForm.newNode) {
                    //don't allow the user to close unless he enters a node
                    e.preventDefault();
                  } else {
                    return {
                      host: $scope.popupData.newNode,
                      useSsl: $scope.popupData.useSsl
                    };
                  }
                }
              }
            ]
          })
          .then(function(res) {
            if (!res || !res.host) { // user cancel
              UIUtils.loading.hide();
              reject('CANCELLED');
              return;
            }
            var host = res.host;
            var path = host.indexOf('/') !== -1 ? host.substring(host.indexOf('/')) : '';
            host = host.indexOf('/') !== -1 ? host.substring(0, host.indexOf('/')) : host;
            var parts = host.split(':', 2);
            host = parts[0];
            var port = parts[1] ? parts[1] : (res.useSsl ? 443 : 80);
            var useSsl = res.useSsl || port == 443;
            resolve({
              host: host,
              port: port,
              path: path,
              useSsl: useSsl
            });
          });
        });
      });
    };

  $scope.save = function() {
    if ($scope.loading || $scope.pendingSaving) return $q.when();
    if ($scope.saving) {
      $scope.pendingSaving = true;
      // Retry later
      return $timeout(function() {
        $scope.pendingSaving = false;
        return $scope.save();
      }, 500);
    }
    $scope.saving = true;

    var now = Date.now();

    // Async - to avoid UI lock
    return $timeout(function() {
      console.debug('[settings] Saving...');

      // Make sure to format helptip
      $scope.cleanupHelpTip();

      // Applying
      csSettings.apply($scope.formData);

      // Applying UI effect
      UIUtils.setEffects($scope.formData.uiEffects);

      // Store
      return csSettings.store();

    }, 100)
    .then(function() {
      //return $timeout(function() {
      $scope.saving = false;
      console.debug('[settings] Saving [OK] in {0}ms'.format(Date.now() - now));
      //}, 100);
    });
  };

  $scope.onDataChanged = function(oldValue, newValue, scope) {
    if ($scope.loading || $scope.pendingSaving) return $q.when();
    if ($scope.saving) {
      $scope.pendingSaving = true;
      // Retry later
      return $timeout(function() {
        $scope.pendingSaving = false;
        return $scope.onDataChanged(oldValue, newValue, scope);
      }, 500);
    }

    // Changes from the current scope: save changes
    if ((scope === $scope) && !angular.equals(oldValue, newValue)) {
      $scope.save();
    }
  };
  $scope.$watch('formData', $scope.onDataChanged, true);

  // Detected changes from outside (e.g. enabling encryption on wallet can be rollback if user cancel auth)
  csSettings.api.data.on.changed($scope, function(data) {
    if ($scope.loading || $scope.saving || $scope.pendingSaving) return;

    var updated = !angular.equals(data.useLocalStorageEncryption, $scope.formData.useLocalStorageEncryption);
    if (updated) {
      console.debug('[settings] Settings changed (outside the settings page). Reloading...');
      $scope.load();
    }
  });

  $scope.getServer = function() {
    if (!$scope.formData.node || !$scope.formData.node.host) return '';
    return csHttp.getServer($scope.formData.node.host, $scope.formData.node.port);
  };

  $scope.cleanupHelpTip = function() {
    var helptipChanged = $scope.formData.helptip.enable !== csSettings.data.helptip.enable;
    if (helptipChanged) {
      var enable = $scope.formData.helptip.enable;
      // Apply default values
      $scope.formData.helptip = angular.merge({}, csSettings.defaultSettings.helptip);
      // Then restore the enable flag
      $scope.formData.helptip.enable = enable;
    }
  };

  /* -- modals & popover -- */

  $scope.showActionsPopover = function(event) {
    UIUtils.popover.show(event, {
      templateUrl: 'templates/settings/popover_actions.html',
      scope: $scope,
      autoremove: true,
      afterShow: function(popover) {
        $scope.actionsPopover = popover;
      }
    });
  };

  $scope.hideActionsPopover = function() {
    if ($scope.actionsPopover) {
      $scope.actionsPopover.hide();
      $scope.actionsPopover = null;
    }
  };

  $scope.startSettingsTour = function() {
    $scope.hideActionsPopover();

    return $scope.showHelpTip(0, true);
  };

  // Show help tip (show only not already shown)
  $scope.showHelpTip = function(index, tour) {
    if (!$scope.isLogin() && !tour) return;
    index = angular.isDefined(index) ? index : csSettings.data.helptip.settings;
    if (index < 0) return;
    if (index === 0) index = 1; // skip first step

    // Create a new scope for the tour controller
    var helptipScope = $scope.createHelptipScope(tour);
    if (!helptipScope) return; // could be undefined, if a global tour already is already started

    return helptipScope.startSettingsTour(index, false)
      .then(function(endIndex) {
        helptipScope.$destroy();
        csSettings.data.helptip.settings = endIndex;
        csSettings.store();
      });
  };

  $scope.removeListeners = function() {
    if ($scope.listeners.length) {
      console.debug('[settings] Closing listeners');
      _.forEach($scope.listeners, function(remove){
        remove();
      });
      $scope.listeners = [];
    }
  };

  $scope.$on('$ionicView.enter', $scope.enter);
  $scope.$on('$ionicView.beforeLeave', $scope.leave);

}