mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-25 16:59:17 +01:00
[TypeScript] Settings module (#398)
* Convert settings to TypeScript. * Simplify loadSettings using await. * Re-write save feature settings. * Minor fixes.
This commit is contained in:
parent
7c525d3f3a
commit
78c4ea7ecb
@ -27,6 +27,7 @@ import ServicesModule from './services/services.module';
|
|||||||
import LockModule from './lock/lock.module';
|
import LockModule from './lock/lock.module';
|
||||||
import CurrentModule from './current/current.module';
|
import CurrentModule from './current/current.module';
|
||||||
import GlobalModule from './global/global.module';
|
import GlobalModule from './global/global.module';
|
||||||
|
import SettingsModule from './settings/settings.module';
|
||||||
|
|
||||||
// Model imports
|
// Model imports
|
||||||
import { Attachment } from '../../models/domain/attachment';
|
import { Attachment } from '../../models/domain/attachment';
|
||||||
@ -88,7 +89,7 @@ angular
|
|||||||
'bit.accounts',
|
'bit.accounts',
|
||||||
CurrentModule,
|
CurrentModule,
|
||||||
'bit.vault',
|
'bit.vault',
|
||||||
'bit.settings',
|
SettingsModule,
|
||||||
ToolsModule,
|
ToolsModule,
|
||||||
LockModule
|
LockModule
|
||||||
]);
|
]);
|
||||||
@ -107,18 +108,6 @@ require('./vault/vaultAddCipherController.js');
|
|||||||
require('./vault/vaultEditCipherController.js');
|
require('./vault/vaultEditCipherController.js');
|
||||||
require('./vault/vaultViewCipherController.js');
|
require('./vault/vaultViewCipherController.js');
|
||||||
require('./vault/vaultAttachmentsController.js');
|
require('./vault/vaultAttachmentsController.js');
|
||||||
require('./settings/settingsModule.js');
|
|
||||||
require('./settings/settingsController.js');
|
|
||||||
require('./settings/settingsHelpController.js');
|
|
||||||
require('./settings/settingsAboutController.js');
|
|
||||||
require('./settings/settingsCreditsController.js');
|
|
||||||
require('./settings/settingsFeaturesController.js');
|
|
||||||
require('./settings/settingsSyncController.js');
|
|
||||||
require('./settings/settingsFoldersController.js');
|
|
||||||
require('./settings/settingsAddFolderController.js');
|
|
||||||
require('./settings/settingsEditFolderController.js');
|
|
||||||
require('./settings/settingsPremiumController.js');
|
|
||||||
require('./settings/settingsEnvironmentController.js');
|
|
||||||
require('./tools/toolsPasswordGeneratorHistoryController.js');
|
require('./tools/toolsPasswordGeneratorHistoryController.js');
|
||||||
|
|
||||||
// $$ngIsClass fix issue with "class constructors must be invoked with |new|" on Firefox ESR
|
// $$ngIsClass fix issue with "class constructors must be invoked with |new|" on Firefox ESR
|
||||||
|
@ -120,8 +120,7 @@ angular
|
|||||||
})
|
})
|
||||||
.state('tabs.settings', {
|
.state('tabs.settings', {
|
||||||
url: '/settings',
|
url: '/settings',
|
||||||
template: require('./settings/views/settings.html'),
|
component: 'settings',
|
||||||
controller: 'settingsController'
|
|
||||||
})
|
})
|
||||||
.state('tabs.tools', {
|
.state('tabs.tools', {
|
||||||
url: '/tools',
|
url: '/tools',
|
||||||
@ -186,72 +185,62 @@ angular
|
|||||||
|
|
||||||
.state('about', {
|
.state('about', {
|
||||||
url: '/about',
|
url: '/about',
|
||||||
template: require('./settings/views/settingsAbout.html'),
|
component: 'about',
|
||||||
controller: 'settingsAboutController',
|
|
||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
.state('credits', {
|
.state('credits', {
|
||||||
url: '/credits',
|
url: '/credits',
|
||||||
template: require('./settings/views/settingsCredits.html'),
|
component: 'credits',
|
||||||
controller: 'settingsCreditsController',
|
|
||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
.state('features', {
|
.state('features', {
|
||||||
url: '/features',
|
url: '/features',
|
||||||
template: require('./settings/views/settingsFeatures.html'),
|
component: 'features',
|
||||||
controller: 'settingsFeaturesController',
|
|
||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
.state('help', {
|
.state('help', {
|
||||||
url: '/help',
|
url: '/help',
|
||||||
template: require('./settings/views/settingsHelp.html'),
|
component: 'help',
|
||||||
controller: 'settingsHelpController',
|
|
||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
.state('sync', {
|
.state('sync', {
|
||||||
url: '/sync',
|
url: '/sync',
|
||||||
template: require('./settings/views/settingsSync.html'),
|
component: 'sync',
|
||||||
controller: 'settingsSyncController',
|
|
||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
.state('premium', {
|
.state('premium', {
|
||||||
url: '/premium',
|
url: '/premium',
|
||||||
template: require('./settings/views/settingsPremium.html'),
|
component: 'premium',
|
||||||
controller: 'settingsPremiumController',
|
|
||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
|
|
||||||
.state('folders', {
|
.state('folders', {
|
||||||
url: '/folders',
|
url: '/folders',
|
||||||
template: require('./settings/views/settingsFolders.html'),
|
abstract: true,
|
||||||
controller: 'settingsFoldersController',
|
|
||||||
data: { authorize: true },
|
data: { authorize: true },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
.state('addFolder', {
|
.state('folders.list', {
|
||||||
url: '/addFolder',
|
url: '',
|
||||||
template: require('./settings/views/settingsAddFolder.html'),
|
component: 'folders',
|
||||||
controller: 'settingsAddFolderController',
|
|
||||||
data: { authorize: true },
|
|
||||||
params: { animation: null }
|
|
||||||
})
|
})
|
||||||
.state('editFolder', {
|
.state('folders.add', {
|
||||||
url: '/editFolder?folderId',
|
url: '/add',
|
||||||
template: require('./settings/views/settingsEditFolder.html'),
|
component: 'addFolder',
|
||||||
controller: 'settingsEditFolderController',
|
})
|
||||||
data: { authorize: true },
|
.state('folders.edit', {
|
||||||
params: { animation: null }
|
url: '/{folderId}/edit',
|
||||||
|
component: 'editFolder',
|
||||||
})
|
})
|
||||||
.state('environment', {
|
.state('environment', {
|
||||||
url: '/environment',
|
url: '/environment',
|
||||||
template: require('./settings/views/settingsEnvironment.html'),
|
component: 'environment',
|
||||||
controller: 'settingsEnvironmentController',
|
|
||||||
data: { authorize: false },
|
data: { authorize: false },
|
||||||
params: { animation: null }
|
params: { animation: null }
|
||||||
})
|
})
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
|
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.about}}</div>
|
<div class="title">{{$ctrl.i18n.about}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="about-page">
|
<div class="about-page">
|
||||||
<img src="../../../../images/logo@3x.png" alt="bitwarden" />
|
<img src="../../../images/logo@3x.png" alt="bitwarden" />
|
||||||
{{i18n.version}} {{version}}<br />
|
{{$ctrl.i18n.version}} {{$ctrl.version}}<br />
|
||||||
© 8bit Solutions LLC 2015-{{year}}
|
© 8bit Solutions LLC 2015-{{$ctrl.year}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item" ui-sref="credits({animation: 'in-slide-left'})">
|
<a class="list-section-item" ui-sref="credits({animation: 'in-slide-left'})">
|
||||||
{{i18n.credits}}
|
{{$ctrl.i18n.credits}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
19
src/popup/app/settings/about.component.ts
Normal file
19
src/popup/app/settings/about.component.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as template from './about.component.html';
|
||||||
|
|
||||||
|
class AboutController {
|
||||||
|
version: string;
|
||||||
|
year: number;
|
||||||
|
i18n: any;
|
||||||
|
|
||||||
|
constructor(i18nService: any) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
this.year = (new Date()).getFullYear();
|
||||||
|
this.version = chrome.runtime.getManifest().version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AboutComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: AboutController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,14 +1,14 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="about({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
|
<a ui-sref="about({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.thankYou}}</div>
|
<div class="title">{{$ctrl.i18n.thankYou}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-header">
|
<div class="list-section-header">
|
||||||
{{i18n.translations}}
|
{{$ctrl.i18n.translations}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.contribute}} <a href="" ng-click="learnMore()">{{i18n.learnMore}}</a>
|
{{$ctrl.i18n.contribute}} <a href="" ng-click="learnMore()">{{$ctrl.i18n.learnMore}}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
23
src/popup/app/settings/credits.component.ts
Normal file
23
src/popup/app/settings/credits.component.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import * as template from './credits.component.html';
|
||||||
|
|
||||||
|
class CreditsController {
|
||||||
|
i18n: any;
|
||||||
|
|
||||||
|
constructor(i18nService: any, private $analytics: any) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
}
|
||||||
|
|
||||||
|
learnMore() {
|
||||||
|
this.$analytics.eventTrack('Contribute Learn More');
|
||||||
|
|
||||||
|
chrome.tabs.create({
|
||||||
|
url: 'https://github.com/bitwarden/browser/blob/master/CONTRIBUTING.md',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CreditsComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: CreditsController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,54 +1,54 @@
|
|||||||
<form name="theForm" ng-submit="save()">
|
<form name="theForm" ng-submit="$ctrl.save()">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="home({animation: 'out-slide-down'})">{{i18n.close}}</a>
|
<a ui-sref="home({animation: 'out-slide-down'})">{{$ctrl.i18n.close}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button type="submit" class="btn btn-link">{{i18n.save}}</button>
|
<button type="submit" class="btn btn-link">{{$ctrl.i18n.save}}</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.settings}}</div>
|
<div class="title">{{$ctrl.i18n.settings}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-header">
|
<div class="list-section-header">
|
||||||
{{i18n.selfHostedEnvironment}}
|
{{$ctrl.i18n.selfHostedEnvironment}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="baseUrl" class="item-label">{{i18n.baseUrl}}</label>
|
<label for="baseUrl" class="item-label">{{$ctrl.i18n.baseUrl}}</label>
|
||||||
<input id="baseUrl" type="text" name="BaseUrl" ng-model="baseUrl"
|
<input id="baseUrl" type="text" name="BaseUrl" ng-model="$ctrl.baseUrl"
|
||||||
placeholder="ex. https://bitwarden.company.com">
|
placeholder="ex. https://bitwarden.company.com">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.selfHostedEnvironmentFooter}}
|
{{$ctrl.i18n.selfHostedEnvironmentFooter}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-header">
|
<div class="list-section-header">
|
||||||
{{i18n.customEnvironment}}
|
{{$ctrl.i18n.customEnvironment}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="webVaultUrl" class="item-label">{{i18n.webVaultUrl}}</label>
|
<label for="webVaultUrl" class="item-label">{{$ctrl.i18n.webVaultUrl}}</label>
|
||||||
<input id="webVaultUrl" type="text" name="WebVaultUrl" ng-model="webVaultUrl">
|
<input id="webVaultUrl" type="text" name="WebVaultUrl" ng-model="$ctrl.webVaultUrl">
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="apiUrl" class="item-label">{{i18n.apiUrl}}</label>
|
<label for="apiUrl" class="item-label">{{$ctrl.i18n.apiUrl}}</label>
|
||||||
<input id="apiUrl" type="text" name="ApiUrl" ng-model="apiUrl">
|
<input id="apiUrl" type="text" name="ApiUrl" ng-model="$ctrl.apiUrl">
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="identityUrl" class="item-label">{{i18n.identityUrl}}</label>
|
<label for="identityUrl" class="item-label">{{$ctrl.i18n.identityUrl}}</label>
|
||||||
<input id="identityUrl" type="text" name="IdentityUrl" ng-model="identityUrl">
|
<input id="identityUrl" type="text" name="IdentityUrl" ng-model="$ctrl.identityUrl">
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="iconsUrl" class="item-label">{{i18n.iconsUrl}}</label>
|
<label for="iconsUrl" class="item-label">{{$ctrl.i18n.iconsUrl}}</label>
|
||||||
<input id="iconsUrl" type="text" name="IconsUrl" ng-model="iconsUrl">
|
<input id="iconsUrl" type="text" name="IconsUrl" ng-model="$ctrl.iconsUrl">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.customEnvironmentFooter}}
|
{{$ctrl.i18n.customEnvironmentFooter}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
57
src/popup/app/settings/environment.component.ts
Normal file
57
src/popup/app/settings/environment.component.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import * as angular from 'angular';
|
||||||
|
import UtilsService from '../../../services/utils.service';
|
||||||
|
import * as template from './environment.component.html';
|
||||||
|
|
||||||
|
class EnvironmentController {
|
||||||
|
iconsUrl: string;
|
||||||
|
identityUrl: string;
|
||||||
|
apiUrl: string;
|
||||||
|
webVaultUrl: string;
|
||||||
|
baseUrl: string;
|
||||||
|
i18n: any;
|
||||||
|
|
||||||
|
constructor(private i18nService: any, private $analytics: any, utilsService: UtilsService,
|
||||||
|
private environmentService: any, private toastr: any, private $timeout: ng.ITimeoutService) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
|
||||||
|
$timeout(() => {
|
||||||
|
utilsService.initListSectionItemListeners(document, angular);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
this.baseUrl = environmentService.baseUrl || '';
|
||||||
|
this.webVaultUrl = environmentService.webVaultUrl || '';
|
||||||
|
this.apiUrl = environmentService.apiUrl || '';
|
||||||
|
this.identityUrl = environmentService.identityUrl || '';
|
||||||
|
this.iconsUrl = environmentService.iconsUrl || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.environmentService
|
||||||
|
.setUrls({
|
||||||
|
base: this.baseUrl,
|
||||||
|
api: this.apiUrl,
|
||||||
|
identity: this.identityUrl,
|
||||||
|
webVault: this.webVaultUrl,
|
||||||
|
icons: this.iconsUrl,
|
||||||
|
})
|
||||||
|
.then((resUrls: any) => {
|
||||||
|
this.$timeout(() => {
|
||||||
|
// re-set urls since service can change them, ex: prefixing https://
|
||||||
|
this.baseUrl = resUrls.base;
|
||||||
|
this.apiUrl = resUrls.api;
|
||||||
|
this.identityUrl = resUrls.identity;
|
||||||
|
this.webVaultUrl = resUrls.webVault;
|
||||||
|
this.iconsUrl = resUrls.icons;
|
||||||
|
|
||||||
|
this.$analytics.eventTrack('Set Environment URLs');
|
||||||
|
this.toastr.success(this.i18nService.environmentSaved);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EnvironmentComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: EnvironmentController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,80 +1,81 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
|
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.features}}</div>
|
<div class="title">{{$ctrl.i18n.features}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="totp-copy">{{i18n.enableAutoFillOnPageLoad}}</label>
|
<label for="totp-copy">{{$ctrl.i18n.enableAutoFillOnPageLoad}}</label>
|
||||||
<input id="totp-copy" type="checkbox" ng-model="enableAutoFillOnPageLoad"
|
<input id="totp-copy" type="checkbox" ng-model="$ctrl.enableAutoFillOnPageLoad"
|
||||||
ng-change="updateAutoFillOnPageLoad()">
|
ng-change="$ctrl.updateAutoFillOnPageLoad()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.enableAutoFillOnPageLoadDesc}}
|
{{$ctrl.i18n.enableAutoFillOnPageLoadDesc}}
|
||||||
<b>{{i18n.warning}}</b>: {{i18n.experimentalFeature}}
|
<b>{{$ctrl.i18n.warning}}</b>: {{$ctrl.i18n.experimentalFeature}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="totp-copy">{{i18n.disableAutoTotpCopy}}</label>
|
<label for="totp-copy">{{$ctrl.i18n.disableAutoTotpCopy}}</label>
|
||||||
<input id="totp-copy" type="checkbox" ng-model="disableAutoTotpCopy" ng-change="updateAutoTotpCopy()">
|
<input id="totp-copy" type="checkbox" ng-model="$ctrl.disableAutoTotpCopy"
|
||||||
|
ng-change="$ctrl.updateAutoTotpCopy()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.disableAutoTotpCopyDesc}}
|
{{$ctrl.i18n.disableAutoTotpCopyDesc}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="ga">{{i18n.disableGa}}</label>
|
<label for="ga">{{$ctrl.i18n.disableGa}}</label>
|
||||||
<input id="ga" type="checkbox" ng-model="disableGa" ng-change="updateGa()">
|
<input id="ga" type="checkbox" ng-model="$ctrl.disableGa" ng-change="$ctrl.updateGa()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.gaDesc}}
|
{{$ctrl.i18n.gaDesc}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="notification-bar">{{i18n.disableAddLoginNotification}}</label>
|
<label for="notification-bar">{{$ctrl.i18n.disableAddLoginNotification}}</label>
|
||||||
<input id="notification-bar" type="checkbox" ng-model="disableAddLoginNotification"
|
<input id="notification-bar" type="checkbox" ng-model="$ctrl.disableAddLoginNotification"
|
||||||
ng-change="updateAddLoginNotification()">
|
ng-change="$ctrl.updateAddLoginNotification()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.addLoginNotificationDesc}}
|
{{$ctrl.i18n.addLoginNotificationDesc}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="context-menu">{{i18n.disableContextMenuItem}}</label>
|
<label for="context-menu">{{$ctrl.i18n.disableContextMenuItem}}</label>
|
||||||
<input id="context-menu" type="checkbox" ng-model="disableContextMenuItem"
|
<input id="context-menu" type="checkbox" ng-model="$ctrl.disableContextMenuItem"
|
||||||
ng-change="updateDisableContextMenuItem()">
|
ng-change="$ctrl.updateDisableContextMenuItem()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.disableContextMenuItemDesc}}
|
{{$ctrl.i18n.disableContextMenuItemDesc}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item list-section-item-checkbox">
|
<div class="list-section-item list-section-item-checkbox">
|
||||||
<label for="context-menu">{{i18n.disableFavicon}}</label>
|
<label for="context-menu">{{$ctrl.i18n.disableFavicon}}</label>
|
||||||
<input id="context-menu" type="checkbox" ng-model="disableFavicon"
|
<input id="context-menu" type="checkbox" ng-model="$ctrl.disableFavicon"
|
||||||
ng-change="updateDisableFavicon()">
|
ng-change="$ctrl.updateDisableFavicon()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.disableFaviconDesc}}
|
{{$ctrl.i18n.disableFaviconDesc}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
111
src/popup/app/settings/features.component.ts
Normal file
111
src/popup/app/settings/features.component.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import * as angular from 'angular';
|
||||||
|
import { UtilsService } from '../../../services/abstractions/utils.service';
|
||||||
|
import StateService from '../services/state.service';
|
||||||
|
import * as template from './features.component.html';
|
||||||
|
|
||||||
|
class FeaturesController {
|
||||||
|
disableFavicon = false;
|
||||||
|
enableAutoFillOnPageLoad = false;
|
||||||
|
disableAutoTotpCopy = false;
|
||||||
|
disableContextMenuItem = false;
|
||||||
|
disableAddLoginNotification = false;
|
||||||
|
disableGa = false;
|
||||||
|
i18n: any;
|
||||||
|
|
||||||
|
constructor(private i18nService: any, private $analytics: any, private constantsService: any,
|
||||||
|
private utilsService: UtilsService, private totpService: any, private stateService: StateService,
|
||||||
|
private $timeout: ng.ITimeoutService) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
|
||||||
|
$timeout(() => {
|
||||||
|
utilsService.initListSectionItemListeners(document, angular);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
this.loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadSettings() {
|
||||||
|
this.enableAutoFillOnPageLoad = await this.utilsService
|
||||||
|
.getObjFromStorage<boolean>(this.constantsService.enableAutoFillOnPageLoadKey);
|
||||||
|
|
||||||
|
const disableGa = await this.utilsService.getObjFromStorage<boolean>(this.constantsService.disableGaKey);
|
||||||
|
this.disableGa = disableGa || (this.utilsService.isFirefox() && disableGa === undefined);
|
||||||
|
|
||||||
|
this.disableAddLoginNotification = await this.utilsService
|
||||||
|
.getObjFromStorage<boolean>(this.constantsService.disableAddLoginNotificationKey);
|
||||||
|
|
||||||
|
this.disableContextMenuItem = await this.utilsService
|
||||||
|
.getObjFromStorage<boolean>(this.constantsService.disableContextMenuItemKey);
|
||||||
|
|
||||||
|
this.disableAutoTotpCopy = !await this.totpService.isAutoCopyEnabled();
|
||||||
|
|
||||||
|
this.disableFavicon = await this.utilsService
|
||||||
|
.getObjFromStorage<boolean>(this.constantsService.disableFaviconKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
callAnalytics(name: string, enabled: boolean) {
|
||||||
|
const status = enabled ? 'Enabled' : 'Disabled';
|
||||||
|
this.$analytics.eventTrack(`${status} ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGa() {
|
||||||
|
this.utilsService.saveObjToStorage(
|
||||||
|
this.constantsService.disableGaKey,
|
||||||
|
this.disableGa,
|
||||||
|
);
|
||||||
|
this.callAnalytics('Analytics', !this.disableGa);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAddLoginNotification() {
|
||||||
|
this.utilsService.saveObjToStorage(
|
||||||
|
this.constantsService.disableAddLoginNotificationKey,
|
||||||
|
this.disableAddLoginNotification,
|
||||||
|
);
|
||||||
|
this.callAnalytics('Add Login Notification', !this.disableAddLoginNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDisableContextMenuItem() {
|
||||||
|
this.utilsService
|
||||||
|
.saveObjToStorage(
|
||||||
|
this.constantsService.disableContextMenuItemKey,
|
||||||
|
this.disableContextMenuItem,
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
chrome.runtime.sendMessage({
|
||||||
|
command: 'bgUpdateContextMenu',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.callAnalytics('Context Menu Item', !this.disableContextMenuItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAutoTotpCopy() {
|
||||||
|
this.utilsService.saveObjToStorage(
|
||||||
|
this.constantsService.disableAutoTotpCopyKey,
|
||||||
|
this.disableAutoTotpCopy,
|
||||||
|
);
|
||||||
|
this.callAnalytics('Auto Copy TOTP', !this.disableAutoTotpCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAutoFillOnPageLoad() {
|
||||||
|
this.utilsService.saveObjToStorage(
|
||||||
|
this.constantsService.enableAutoFillOnPageLoadKey,
|
||||||
|
this.enableAutoFillOnPageLoad,
|
||||||
|
);
|
||||||
|
this.callAnalytics('Auto-fill Page Load', this.enableAutoFillOnPageLoad);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateDisableFavicon() {
|
||||||
|
this.utilsService.saveObjToStorage(
|
||||||
|
this.constantsService.disableFaviconKey,
|
||||||
|
this.disableFavicon,
|
||||||
|
);
|
||||||
|
this.stateService.saveState('faviconEnabled', !this.disableFavicon);
|
||||||
|
this.callAnalytics('Favicon', !this.disableFavicon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FeaturesComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: FeaturesController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,21 +1,21 @@
|
|||||||
<form name="theForm" ng-submit="save(folder)" bit-form="savePromise" autocomplete="off">
|
<form name="theForm" ng-submit="$ctrl.save($ctrl.folder)" bit-form="$ctrl.savePromise" autocomplete="off">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="folders({animation: 'out-slide-down'})">{{i18n.cancel}}</a>
|
<a ui-sref="^.list({animation: 'out-slide-down'})">{{$ctrl.i18n.cancel}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.save}}</button>
|
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{$ctrl.i18n.save}}</button>
|
||||||
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
|
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.addFolder}}</div>
|
<div class="title">{{$ctrl.i18n.addFolder}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="name" class="item-label">{{i18n.name}}</label>
|
<label for="name" class="item-label">{{$ctrl.i18n.name}}</label>
|
||||||
<input id="name" type="text" name="Name" ng-model="folder.name">
|
<input id="name" type="text" name="Name" ng-model="$ctrl.folder.name">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
47
src/popup/app/settings/folders/add-folder.component.ts
Normal file
47
src/popup/app/settings/folders/add-folder.component.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import * as angular from 'angular';
|
||||||
|
import { Folder } from '../../../../models/domain/folder';
|
||||||
|
import { UtilsService } from '../../../../services/abstractions/utils.service';
|
||||||
|
import * as template from './add-folder.component.html';
|
||||||
|
|
||||||
|
class AddFolderController {
|
||||||
|
savePromise: any;
|
||||||
|
folder: {};
|
||||||
|
i18n: any;
|
||||||
|
|
||||||
|
constructor(private folderService: any, private $state: any, private toastr: any, utilsService: UtilsService,
|
||||||
|
private $analytics: any, private i18nService: any, $timeout: any) {
|
||||||
|
$timeout(() => {
|
||||||
|
utilsService.initListSectionItemListeners(document, angular);
|
||||||
|
document.getElementById('name').focus();
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
this.i18n = i18nService;
|
||||||
|
this.folder = {};
|
||||||
|
this.savePromise = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
save(model: any) {
|
||||||
|
if (!model.name) {
|
||||||
|
this.toastr.error(this.i18nService.nameRequired, this.i18nService.errorsOccurred);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.savePromise = this.folderService
|
||||||
|
.encrypt(model)
|
||||||
|
.then((folderModel: any) => {
|
||||||
|
const folder = new Folder(folderModel, true);
|
||||||
|
return this.folderService.saveWithServer(folder);
|
||||||
|
})
|
||||||
|
.then((folder: any) => {
|
||||||
|
this.$analytics.eventTrack('Added Folder');
|
||||||
|
this.toastr.success(this.i18nService.addedFolder);
|
||||||
|
this.$state.go('^.list', { animation: 'out-slide-down' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AddFolderComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: AddFolderController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,28 +1,28 @@
|
|||||||
<form name="theForm" ng-submit="save(folder)" bit-form="savePromise" autocomplete="off">
|
<form name="theForm" ng-submit="$ctrl.save($ctrl.folder)" bit-form="savePromise" autocomplete="off">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="folders({animation: 'out-slide-down'})">{{i18n.cancel}}</a>
|
<a ui-sref="^.list({animation: 'out-slide-down'})">{{$ctrl.i18n.cancel}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.save}}</button>
|
<button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{$ctrl.i18n.save}}</button>
|
||||||
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
|
<i class="fa fa-spinner fa-lg" ng-show="theForm.$loading" ng-class="{'fa-spin' : theForm.$loading}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.editFolder}}</div>
|
<div class="title">{{$ctrl.i18n.editFolder}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="name" class="item-label">{{i18n.name}}</label>
|
<label for="name" class="item-label">{{$ctrl.i18n.name}}</label>
|
||||||
<input id="name" type="text" name="Name" ng-model="folder.name">
|
<input id="name" type="text" name="Name" ng-model="$ctrl.folder.name">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a href="" ng-click="delete()" class="list-section-item text-danger">
|
<a href="" ng-click="$ctrl.delete()" class="list-section-item text-danger">
|
||||||
<i class="fa fa-trash fa-fw fa-lg"></i>{{i18n.deleteFolder}}
|
<i class="fa fa-trash fa-fw fa-lg"></i>{{$ctrl.i18n.deleteFolder}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
86
src/popup/app/settings/folders/edit-folder.component.ts
Normal file
86
src/popup/app/settings/folders/edit-folder.component.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import * as angular from 'angular';
|
||||||
|
import { Folder } from '../../../../models/domain/folder';
|
||||||
|
import UtilsService from '../../../../services/utils.service';
|
||||||
|
import * as template from './edit-folder.component.html';
|
||||||
|
|
||||||
|
class EditFolderController {
|
||||||
|
$transition$: any;
|
||||||
|
folderId: any;
|
||||||
|
savePromise: any = null;
|
||||||
|
i18n: any;
|
||||||
|
folder: Folder;
|
||||||
|
|
||||||
|
constructor($scope: any, $stateParams: any, private folderService: any, private toastr: any, private $state: any,
|
||||||
|
private SweetAlert: any, utilsService: UtilsService, private $analytics: any, private i18nService: any,
|
||||||
|
$timeout: any) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
|
||||||
|
$timeout(() => {
|
||||||
|
utilsService.initListSectionItemListeners(document, angular);
|
||||||
|
document.getElementById('name').focus();
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
$scope.folder = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
$onInit() {
|
||||||
|
this.folderId = this.$transition$.params('to').folderId;
|
||||||
|
|
||||||
|
this.folderService
|
||||||
|
.get(this.folderId)
|
||||||
|
.then((folder: any) => {
|
||||||
|
return folder.decrypt();
|
||||||
|
}).then((model: Folder) => {
|
||||||
|
this.folder = model;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
save(model: any) {
|
||||||
|
if (!model.name) {
|
||||||
|
this.toastr.error(this.i18nService.nameRequired, this.i18nService.errorsOccurred);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.savePromise = this.folderService
|
||||||
|
.encrypt(model)
|
||||||
|
.then((folderModel: any) => {
|
||||||
|
const folder = new Folder(folderModel, true);
|
||||||
|
return this.folderService.saveWithServer(folder);
|
||||||
|
})
|
||||||
|
.then((folder: any) => {
|
||||||
|
this.$analytics.eventTrack('Edited Folder');
|
||||||
|
this.toastr.success(this.i18nService.editedFolder);
|
||||||
|
this.$state.go('^.list', { animation: 'out-slide-down' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
delete() {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.deleteFolder,
|
||||||
|
text: this.i18nService.deleteFolderConfirmation,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.no,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.folderService
|
||||||
|
.deleteWithServer(this.folderId)
|
||||||
|
.then(() => {
|
||||||
|
this.$analytics.eventTrack('Deleted Folder');
|
||||||
|
this.toastr.success(this.i18nService.deletedFolder);
|
||||||
|
this.$state.go('^.list', {
|
||||||
|
animation: 'out-slide-down',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EditFolderComponent = {
|
||||||
|
bindings: {
|
||||||
|
$transition$: '<',
|
||||||
|
},
|
||||||
|
controller: EditFolderController,
|
||||||
|
template,
|
||||||
|
};
|
31
src/popup/app/settings/folders/folders.component.html
Normal file
31
src/popup/app/settings/folders/folders.component.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<div class="header">
|
||||||
|
<div class="left">
|
||||||
|
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<a ui-sref="^.add({animation: 'in-slide-up'})" title="{{::$ctrl.i18n.addFolder}}"><i class="fa fa-plus fa-lg"></i></a>
|
||||||
|
</div>
|
||||||
|
<div class="title">{{$ctrl.i18n.folders}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<div ng-if="$ctrl.folders.length">
|
||||||
|
<div class="list">
|
||||||
|
<div class="list-grouped">
|
||||||
|
<a href="" ng-click="$ctrl.editFolder(folder)" class="list-grouped-item" title="{{::$ctrl.i18n.edit}}"
|
||||||
|
ng-repeat="folder in theFolders = ($ctrl.folders | orderBy: ['name']) track by $index">
|
||||||
|
<span class="text">{{folder.name}}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="centered-message" ng-if="$ctrl.loaded && !$ctrl.folders.length">
|
||||||
|
<p>
|
||||||
|
{{$ctrl.i18n.noFolders}}
|
||||||
|
<a ui-sref="^.add({animation: 'in-slide-up'})" style="margin-top: 20px;"
|
||||||
|
class="btn btn-link btn-block">{{$ctrl.i18n.addFolder}}</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="page-loading" ng-if="!$ctrl.loaded">
|
||||||
|
<i class="fa fa-lg fa-spinner fa-spin"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
42
src/popup/app/settings/folders/folders.component.ts
Normal file
42
src/popup/app/settings/folders/folders.component.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { Folder } from '../../../../models/domain/folder';
|
||||||
|
import * as template from './folders.component.html';
|
||||||
|
|
||||||
|
class FoldersController {
|
||||||
|
folders: Folder[] = [];
|
||||||
|
i18n: any;
|
||||||
|
loaded = false;
|
||||||
|
|
||||||
|
constructor(private folderService: any, private $state: any, i18nService: any) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
|
||||||
|
this.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
load() {
|
||||||
|
this.folderService
|
||||||
|
.getAllDecrypted()
|
||||||
|
.then((folders: any) => {
|
||||||
|
if (folders.length > 0 && folders[0].id === null) {
|
||||||
|
// remove the "none" folder
|
||||||
|
this.folders = folders.slice(1);
|
||||||
|
} else {
|
||||||
|
this.folders = folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
editFolder(folder: any) {
|
||||||
|
this.$state.go('^.edit', {
|
||||||
|
folderId: folder.id,
|
||||||
|
animation: 'in-slide-up',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const FoldersComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: FoldersController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,53 +1,53 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
|
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.helpFeedback}}</div>
|
<div class="title">{{$ctrl.i18n.helpFeedback}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item" href="" ng-click="email()">
|
<a class="list-section-item" href="" ng-click="$ctrl.email()">
|
||||||
{{i18n.emailUs}}
|
{{$ctrl.i18n.emailUs}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.emailUsDirectly}}
|
{{$ctrl.i18n.emailUsDirectly}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item" href="" ng-click="website()">
|
<a class="list-section-item" href="" ng-click="$ctrl.website()">
|
||||||
{{i18n.visitOurWebsite}}
|
{{$ctrl.i18n.visitOurWebsite}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.visitOurWebsiteDirectly}}
|
{{$ctrl.i18n.visitOurWebsiteDirectly}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item" href="" ng-click="tutorial()">
|
<a class="list-section-item" href="" ng-click="$ctrl.tutorial()">
|
||||||
{{i18n.gettingStartedTutorial}}
|
{{$ctrl.i18n.gettingStartedTutorial}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.gettingStartedTutorialVideo}}
|
{{$ctrl.i18n.gettingStartedTutorialVideo}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item" href="" ng-click="bug()">
|
<a class="list-section-item" href="" ng-click="$ctrl.bug()">
|
||||||
{{i18n.fileBugReport}}
|
{{$ctrl.i18n.fileBugReport}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.gitHubIssue}}
|
{{$ctrl.i18n.gitHubIssue}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
35
src/popup/app/settings/help.component.ts
Normal file
35
src/popup/app/settings/help.component.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import * as template from './help.component.html';
|
||||||
|
|
||||||
|
class HelpController {
|
||||||
|
i18n: any;
|
||||||
|
|
||||||
|
constructor(i18nService: any, private $analytics: any) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
}
|
||||||
|
|
||||||
|
email() {
|
||||||
|
this.$analytics.eventTrack('Selected Help Email');
|
||||||
|
chrome.tabs.create({ url: 'mailto:hello@bitwarden.com' });
|
||||||
|
}
|
||||||
|
|
||||||
|
website() {
|
||||||
|
this.$analytics.eventTrack('Selected Help Website');
|
||||||
|
chrome.tabs.create({ url: 'https://bitwarden.com/contact/' });
|
||||||
|
}
|
||||||
|
|
||||||
|
tutorial() {
|
||||||
|
this.$analytics.eventTrack('Selected Help Tutorial');
|
||||||
|
chrome.tabs.create({ url: 'https://bitwarden.com/browser-start/' });
|
||||||
|
}
|
||||||
|
|
||||||
|
bug() {
|
||||||
|
this.$analytics.eventTrack('Selected Help Bug Report');
|
||||||
|
chrome.tabs.create({ url: 'https://github.com/bitwarden/browser' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HelpComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: HelpController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,52 +1,52 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
|
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.premiumMembership}}</div>
|
<div class="title">{{$ctrl.i18n.premiumMembership}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="premium-page">
|
<div class="premium-page">
|
||||||
<div ng-if="!isPremium">
|
<div ng-if="!$ctrl.isPremium">
|
||||||
<p class="text-center lead">{{i18n.premiumNotCurrentMember}}</p>
|
<p class="text-center lead">{{$ctrl.i18n.premiumNotCurrentMember}}</p>
|
||||||
<p>{{i18n.premiumSignUpAndGet}}</p>
|
<p>{{$ctrl.i18n.premiumSignUpAndGet}}</p>
|
||||||
<ul class="fa-ul">
|
<ul class="fa-ul">
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success"></i>
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
{{i18n.ppremiumSignUpStorage}}
|
{{$ctrl.i18n.ppremiumSignUpStorage}}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success"></i>
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
{{i18n.ppremiumSignUpTwoStep}}
|
{{$ctrl.i18n.ppremiumSignUpTwoStep}}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success"></i>
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
{{i18n.ppremiumSignUpTotp}}
|
{{$ctrl.i18n.ppremiumSignUpTotp}}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success"></i>
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
{{i18n.ppremiumSignUpSupport}}
|
{{$ctrl.i18n.ppremiumSignUpSupport}}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<i class="fa-li fa fa-check text-success"></i>
|
<i class="fa-li fa fa-check text-success"></i>
|
||||||
{{i18n.ppremiumSignUpFuture}}
|
{{$ctrl.i18n.ppremiumSignUpFuture}}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="text-center lead">{{i18n.premiumPrice.replace('%price%', price)}}</p>
|
<p class="text-center lead">{{$ctrl.i18n.premiumPrice.replace('%price%', $ctrl.price)}}</p>
|
||||||
<div class="bottom-buttons">
|
<div class="bottom-buttons">
|
||||||
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="purchase()">
|
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="$ctrl.purchase()">
|
||||||
<b>{{i18n.premiumPurchase}}</b>
|
<b>{{$ctrl.i18n.premiumPurchase}}</b>
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-lg btn-link btn-block" href="#" stop-click ng-click="refresh()">
|
<a class="btn btn-lg btn-link btn-block" href="#" stop-click ng-click="$ctrl.refresh()">
|
||||||
{{i18n.premiumRefresh}}
|
{{$ctrl.i18n.premiumRefresh}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div ng-if="isPremium">
|
<div ng-if="isPremium">
|
||||||
<p class="text-center lead">{{i18n.premiumCurrentMember}}</p>
|
<p class="text-center lead">{{$ctrl.i18n.premiumCurrentMember}}</p>
|
||||||
<p class="text-center">{{i18n.premiumCurrentMemberThanks}}</p>
|
<p class="text-center">{{$ctrl.i18n.premiumCurrentMemberThanks}}</p>
|
||||||
<div class="bottom-buttons">
|
<div class="bottom-buttons">
|
||||||
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="manage()">
|
<a class="btn btn-lg btn-primary btn-block" href="#" stop-click ng-click="$ctrl.manage()">
|
||||||
<b>{{i18n.premiumManage}}</b>
|
<b>{{$ctrl.i18n.premiumManage}}</b>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
62
src/popup/app/settings/premium.component.ts
Normal file
62
src/popup/app/settings/premium.component.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import * as template from './premium.component.html';
|
||||||
|
|
||||||
|
class PremiumController {
|
||||||
|
isPremium: boolean;
|
||||||
|
i18n: any;
|
||||||
|
price = '$10';
|
||||||
|
|
||||||
|
constructor(private i18nService: any, private tokenService: any, private apiService: any, private toastr: any,
|
||||||
|
private SweetAlert: any, private $analytics: any, private $timeout: any) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
this.isPremium = tokenService.getPremium();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.apiService
|
||||||
|
.refreshIdentityToken()
|
||||||
|
.then(() => {
|
||||||
|
this.toastr.success(this.i18nService.refreshComplete);
|
||||||
|
this.$timeout(() => {
|
||||||
|
this.isPremium = this.tokenService.getPremium();
|
||||||
|
});
|
||||||
|
}, (err: any) => {
|
||||||
|
this.toastr.error(this.i18nService.errorsOccurred);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
purchase() {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.premiumPurchase,
|
||||||
|
text: this.i18nService.premiumPurchaseAlert,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.cancel,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
this.$analytics.eventTrack('Clicked Purchase Premium');
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=purchase' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
manage() {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.premiumManage,
|
||||||
|
text: this.i18nService.premiumManageAlert,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.cancel,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
this.$analytics.eventTrack('Clicked Manage Membership');
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=manage' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PremiumComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: PremiumController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,100 +1,100 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<pop-out class="left"></pop-out>
|
<pop-out class="left"></pop-out>
|
||||||
<div class="title">{{i18n.settings}}</div>
|
<div class="title">{{$ctrl.i18n.settings}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content content-tabs">
|
<div class="content content-tabs">
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-header">
|
<div class="list-section-header">
|
||||||
{{i18n.security}}
|
{{$ctrl.i18n.security}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<div class="list-section-item">
|
<div class="list-section-item">
|
||||||
<label for="lock-option" class="item-label">{{i18n.lockOptions}}</label>
|
<label for="lock-option" class="item-label">{{$ctrl.i18n.lockOptions}}</label>
|
||||||
<select id="lock-option" name="LockOption" ng-model="lockOption" ng-change="changeLockOption()">
|
<select id="lock-option" name="LockOption" ng-model="$ctrl.lockOption" ng-change="$ctrl.changeLockOption()">
|
||||||
<option value="0">{{i18n.immediately}}</option>
|
<option value="0">{{$ctrl.i18n.immediately}}</option>
|
||||||
<option value="1">{{i18n.oneMinute}}</option>
|
<option value="1">{{$ctrl.i18n.oneMinute}}</option>
|
||||||
<option value="5">{{i18n.fiveMinutes}}</option>
|
<option value="5">{{$ctrl.i18n.fiveMinutes}}</option>
|
||||||
<option value="15">{{i18n.fifteenMinutes}}</option>
|
<option value="15">{{$ctrl.i18n.fifteenMinutes}}</option>
|
||||||
<option value="30">{{i18n.thirtyMinutes}}</option>
|
<option value="30">{{$ctrl.i18n.thirtyMinutes}}</option>
|
||||||
<option value="60">{{i18n.oneHour}}</option>
|
<option value="60">{{$ctrl.i18n.oneHour}}</option>
|
||||||
<option value="240">{{i18n.fourHours}}</option>
|
<option value="240">{{$ctrl.i18n.fourHours}}</option>
|
||||||
<option value="-2" ng-if="showOnLocked">{{i18n.onLocked}}</option>
|
<option value="-2" ng-if="$ctrl.showOnLocked">{{$ctrl.i18n.onLocked}}</option>
|
||||||
<option value="-1">{{i18n.onRestart}}</option>
|
<option value="-1">{{$ctrl.i18n.onRestart}}</option>
|
||||||
<option value="">{{i18n.never}}</option>
|
<option value="">{{$ctrl.i18n.never}}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<a class="list-section-item" href="" ng-click="lock()">
|
<a class="list-section-item" href="" ng-click="$ctrl.lock()">
|
||||||
{{i18n.lockNow}}
|
{{$ctrl.i18n.lockNow}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" href="" ng-click="twoStep()">
|
<a class="list-section-item" href="" ng-click="$ctrl.twoStep()">
|
||||||
{{i18n.twoStepLogin}}
|
{{$ctrl.i18n.twoStepLogin}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-header">
|
<div class="list-section-header">
|
||||||
{{i18n.account}}
|
{{$ctrl.i18n.account}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item text-primary" ui-sref="premium({animation: 'in-slide-left'})">
|
<a class="list-section-item text-primary" ui-sref="premium({animation: 'in-slide-left'})">
|
||||||
<i class="fa fa-star fa-fw"></i> <b>{{i18n.premiumMembership}}</b>
|
<i class="fa fa-star fa-fw"></i> <b>{{$ctrl.i18n.premiumMembership}}</b>
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" href="" ng-click="changePassword()">
|
<a class="list-section-item" href="" ng-click="$ctrl.changePassword()">
|
||||||
{{i18n.changeMasterPassword}}
|
{{$ctrl.i18n.changeMasterPassword}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" href="" ng-click="changeEmail()">
|
<a class="list-section-item" href="" ng-click="$ctrl.changeEmail()">
|
||||||
{{i18n.changeEmail}}
|
{{$ctrl.i18n.changeEmail}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" href="" ng-click="logOut()">
|
<a class="list-section-item" href="" ng-click="$ctrl.logOut()">
|
||||||
{{i18n.logOut}}
|
{{$ctrl.i18n.logOut}}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-header">
|
<div class="list-section-header">
|
||||||
{{i18n.manage}}
|
{{$ctrl.i18n.manage}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item" ui-sref="folders({animation: 'in-slide-left'})">
|
<a class="list-section-item" ui-sref="folders.list({animation: 'in-slide-left'})">
|
||||||
{{i18n.folders}}
|
{{$ctrl.i18n.folders}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" ui-sref="sync({animation: 'in-slide-left'})">
|
<a class="list-section-item" ui-sref="sync({animation: 'in-slide-left'})">
|
||||||
{{i18n.sync}}
|
{{$ctrl.i18n.sync}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section">
|
<div class="list-section">
|
||||||
<div class="list-section-header">
|
<div class="list-section-header">
|
||||||
{{i18n.other}}
|
{{$ctrl.i18n.other}}
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-items">
|
<div class="list-section-items">
|
||||||
<a class="list-section-item" ui-sref="features({animation: 'in-slide-left'})">
|
<a class="list-section-item" ui-sref="features({animation: 'in-slide-left'})">
|
||||||
{{i18n.features}}
|
{{$ctrl.i18n.features}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" ui-sref="about({animation: 'in-slide-left'})">
|
<a class="list-section-item" ui-sref="about({animation: 'in-slide-left'})">
|
||||||
{{i18n.about}}
|
{{$ctrl.i18n.about}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" ui-sref="help({animation: 'in-slide-left'})">
|
<a class="list-section-item" ui-sref="help({animation: 'in-slide-left'})">
|
||||||
{{i18n.helpFeedback}}
|
{{$ctrl.i18n.helpFeedback}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="list-section-item" href="" ng-click="rate()">
|
<a class="list-section-item" href="" ng-click="$ctrl.rate()">
|
||||||
{{i18n.rateExtension}}
|
{{$ctrl.i18n.rateExtension}}
|
||||||
<i class="fa fa-chevron-right fa-lg"></i>
|
<i class="fa fa-chevron-right fa-lg"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-section-footer">
|
<div class="list-section-footer">
|
||||||
{{i18n.rateExtensionDesc}}
|
{{$ctrl.i18n.rateExtensionDesc}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
161
src/popup/app/settings/settings.component.ts
Normal file
161
src/popup/app/settings/settings.component.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
import * as angular from 'angular';
|
||||||
|
import { BrowserType } from '../../../enums/browserType.enum';
|
||||||
|
import { CryptoService } from '../../../services/abstractions/crypto.service';
|
||||||
|
import { UtilsService } from '../../../services/abstractions/utils.service';
|
||||||
|
import ConstantsService from '../../../services/constants.service';
|
||||||
|
|
||||||
|
import * as template from './settings.component.html';
|
||||||
|
|
||||||
|
const RateUrls = {
|
||||||
|
[BrowserType.Chrome]:
|
||||||
|
'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb/reviews',
|
||||||
|
[BrowserType.Firefox]:
|
||||||
|
'https://addons.mozilla.org/en-US/firefox/addon/bitwarden-password-manager/#reviews',
|
||||||
|
[BrowserType.Opera]:
|
||||||
|
'https://addons.opera.com/en/extensions/details/bitwarden-free-password-manager/#feedback-container',
|
||||||
|
[BrowserType.Edge]:
|
||||||
|
'https://www.microsoft.com/store/p/bitwarden-free-password-manager/9p6kxl0svnnl',
|
||||||
|
};
|
||||||
|
|
||||||
|
class SettingsController {
|
||||||
|
lockOption = '';
|
||||||
|
i18n: any;
|
||||||
|
showOnLocked: boolean;
|
||||||
|
|
||||||
|
constructor(private $state: any, private SweetAlert: any, private utilsService: UtilsService,
|
||||||
|
private $analytics: any, private i18nService: any, private constantsService: ConstantsService,
|
||||||
|
private cryptoService: CryptoService, private lockService: any, private $timeout: ng.ITimeoutService) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
|
||||||
|
$timeout(() => {
|
||||||
|
utilsService.initListSectionItemListeners(document, angular);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
this.showOnLocked = !utilsService.isFirefox() && !utilsService.isEdge();
|
||||||
|
|
||||||
|
chrome.storage.local.get(constantsService.lockOptionKey, (obj: any) => {
|
||||||
|
if (obj && (obj[constantsService.lockOptionKey] || obj[constantsService.lockOptionKey] === 0)) {
|
||||||
|
let option = obj[constantsService.lockOptionKey].toString();
|
||||||
|
if (option === '-2' && !this.showOnLocked) {
|
||||||
|
option = '-1';
|
||||||
|
}
|
||||||
|
this.lockOption = option;
|
||||||
|
} else {
|
||||||
|
this.lockOption = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changeLockOption() {
|
||||||
|
const obj: any = {};
|
||||||
|
obj[this.constantsService.lockOptionKey] = null;
|
||||||
|
if (this.lockOption && this.lockOption !== '') {
|
||||||
|
obj[this.constantsService.lockOptionKey] = parseInt(this.lockOption, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.storage.local.set(obj, () => {
|
||||||
|
this.cryptoService.getKeyHash().then((keyHash) => {
|
||||||
|
if (keyHash) {
|
||||||
|
this.cryptoService.toggleKey();
|
||||||
|
} else {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.loggingOut,
|
||||||
|
text: this.i18nService.loggingOutConfirmation,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.cancel,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
if (confirmed) {
|
||||||
|
this.cryptoService.toggleKey();
|
||||||
|
chrome.runtime.sendMessage({ command: 'logout' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
lock() {
|
||||||
|
this.$analytics.eventTrack('Lock Now');
|
||||||
|
this.lockService
|
||||||
|
.lock()
|
||||||
|
.then(() => {
|
||||||
|
return this.$state.go('lock', {
|
||||||
|
animation: 'in-slide-down',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logOut() {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.logOut,
|
||||||
|
text: this.i18nService.logOutConfirmation,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.cancel,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.runtime.sendMessage({ command: 'logout' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changePassword() {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.changeMasterPassword,
|
||||||
|
text: this.i18nService.changeMasterPasswordConfirmation,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.cancel,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
this.$analytics.eventTrack('Clicked Change Password');
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.tabs.create({ url: 'https://help.bitwarden.com/article/change-your-master-password/' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
changeEmail() {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.changeEmail,
|
||||||
|
text: this.i18nService.changeEmailConfirmation,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.cancel,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
this.$analytics.eventTrack('Clicked Change Email');
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.tabs.create({ url: 'https://help.bitwarden.com/article/change-your-email/' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
twoStep() {
|
||||||
|
this.SweetAlert.swal({
|
||||||
|
title: this.i18nService.twoStepLogin,
|
||||||
|
text: this.i18nService.twoStepLoginConfirmation,
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: this.i18nService.yes,
|
||||||
|
cancelButtonText: this.i18nService.cancel,
|
||||||
|
}, (confirmed: boolean) => {
|
||||||
|
this.$analytics.eventTrack('Clicked Two-step Login');
|
||||||
|
if (confirmed) {
|
||||||
|
chrome.tabs.create({ url: 'https://help.bitwarden.com/article/setup-two-step-login/' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rate() {
|
||||||
|
this.$analytics.eventTrack('Rate Extension');
|
||||||
|
|
||||||
|
chrome.tabs.create({
|
||||||
|
url: RateUrls[this.utilsService.getBrowser()],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SettingsComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: SettingsController,
|
||||||
|
template,
|
||||||
|
};
|
29
src/popup/app/settings/settings.module.ts
Normal file
29
src/popup/app/settings/settings.module.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import * as angular from 'angular';
|
||||||
|
import { AboutComponent } from './about.component';
|
||||||
|
import { CreditsComponent } from './credits.component';
|
||||||
|
import { EnvironmentComponent } from './environment.component';
|
||||||
|
import { FeaturesComponent } from './features.component';
|
||||||
|
import { AddFolderComponent } from './folders/add-folder.component';
|
||||||
|
import { EditFolderComponent } from './folders/edit-folder.component';
|
||||||
|
import { FoldersComponent } from './folders/folders.component';
|
||||||
|
import { HelpComponent } from './help.component';
|
||||||
|
import { PremiumComponent } from './premium.component';
|
||||||
|
import { SettingsComponent } from './settings.component';
|
||||||
|
import { SyncComponent } from './sync.component';
|
||||||
|
|
||||||
|
export default angular
|
||||||
|
.module('bit.settings', ['oitozero.ngSweetAlert', 'toastr'])
|
||||||
|
|
||||||
|
.component('settings', SettingsComponent)
|
||||||
|
.component('environment', EnvironmentComponent)
|
||||||
|
.component('features', FeaturesComponent)
|
||||||
|
.component('about', AboutComponent)
|
||||||
|
.component('credits', CreditsComponent)
|
||||||
|
.component('help', HelpComponent)
|
||||||
|
.component('folders', FoldersComponent)
|
||||||
|
.component('addFolder', AddFolderComponent)
|
||||||
|
.component('editFolder', EditFolderComponent)
|
||||||
|
.component('premium', PremiumComponent)
|
||||||
|
.component('sync', SyncComponent)
|
||||||
|
|
||||||
|
.name;
|
@ -1,8 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsAboutController', function ($scope, i18nService) {
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.year = (new Date()).getFullYear();
|
|
||||||
$scope.version = chrome.runtime.getManifest().version;
|
|
||||||
});
|
|
@ -1,29 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsAddFolderController', function ($scope, $q, folderService, $state, toastr, utilsService,
|
|
||||||
$analytics, i18nService, $timeout) {
|
|
||||||
$timeout(function () {
|
|
||||||
utilsService.initListSectionItemListeners(document, angular);
|
|
||||||
document.getElementById('name').focus();
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.folder = {};
|
|
||||||
$scope.savePromise = null;
|
|
||||||
$scope.save = function (model) {
|
|
||||||
if (!model.name) {
|
|
||||||
toastr.error(i18nService.nameRequired, i18nService.errorsOccurred);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.savePromise = $q.when(folderService.encrypt(model)).then(function (folderModel) {
|
|
||||||
var folder = new Folder(folderModel, true);
|
|
||||||
return $q.when(folderService.saveWithServer(folder)).then(function (folder) {
|
|
||||||
$analytics.eventTrack('Added Folder');
|
|
||||||
toastr.success(i18nService.addedFolder);
|
|
||||||
$state.go('folders', { animation: 'out-slide-down' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,158 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsController', function ($scope, $state, SweetAlert, utilsService, $analytics,
|
|
||||||
i18nService, constantsService, cryptoService, lockService, $timeout) {
|
|
||||||
$timeout(function () {
|
|
||||||
utilsService.initListSectionItemListeners(document, angular);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
$scope.showOnLocked = !utilsService.isFirefox() && !utilsService.isEdge();
|
|
||||||
$scope.lockOption = '';
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
|
|
||||||
chrome.storage.local.get(constantsService.lockOptionKey, function (obj) {
|
|
||||||
if (obj && (obj[constantsService.lockOptionKey] || obj[constantsService.lockOptionKey] === 0)) {
|
|
||||||
var option = obj[constantsService.lockOptionKey].toString();
|
|
||||||
if (option === '-2' && !$scope.showOnLocked) {
|
|
||||||
option = '-1';
|
|
||||||
}
|
|
||||||
$scope.lockOption = option;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.lockOption = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.$apply();
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.changeLockOption = function () {
|
|
||||||
var obj = {};
|
|
||||||
obj[constantsService.lockOptionKey] = null;
|
|
||||||
if ($scope.lockOption && $scope.lockOption !== '') {
|
|
||||||
obj[constantsService.lockOptionKey] = parseInt($scope.lockOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
cryptoService.getKeyHash().then(function (keyHash) {
|
|
||||||
if (keyHash) {
|
|
||||||
cryptoService.toggleKey();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.loggingOut,
|
|
||||||
text: i18nService.loggingOutConfirmation,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.cancel
|
|
||||||
}, function (confirmed) {
|
|
||||||
if (confirmed) {
|
|
||||||
cryptoService.toggleKey();
|
|
||||||
chrome.runtime.sendMessage({ command: 'logout' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.lock = function () {
|
|
||||||
$analytics.eventTrack('Lock Now');
|
|
||||||
lockService.lock().then(function () {
|
|
||||||
return $state.go('lock', {
|
|
||||||
animation: 'in-slide-down'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.logOut = function () {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.logOut,
|
|
||||||
text: i18nService.logOutConfirmation,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.cancel
|
|
||||||
}, function (confirmed) {
|
|
||||||
if (confirmed) {
|
|
||||||
chrome.runtime.sendMessage({ command: 'logout' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.changePassword = function () {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.changeMasterPassword,
|
|
||||||
text: i18nService.changeMasterPasswordConfirmation,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.cancel
|
|
||||||
}, function (confirmed) {
|
|
||||||
$analytics.eventTrack('Clicked Change Password');
|
|
||||||
if (confirmed) {
|
|
||||||
chrome.tabs.create({ url: 'https://help.bitwarden.com/article/change-your-master-password/' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.changeEmail = function () {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.changeEmail,
|
|
||||||
text: i18nService.changeEmailConfirmation,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.cancel
|
|
||||||
}, function (confirmed) {
|
|
||||||
$analytics.eventTrack('Clicked Change Email');
|
|
||||||
if (confirmed) {
|
|
||||||
chrome.tabs.create({ url: 'https://help.bitwarden.com/article/change-your-email/' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.twoStep = function () {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.twoStepLogin,
|
|
||||||
text: i18nService.twoStepLoginConfirmation,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.cancel
|
|
||||||
}, function (confirmed) {
|
|
||||||
$analytics.eventTrack('Clicked Two-step Login');
|
|
||||||
if (confirmed) {
|
|
||||||
chrome.tabs.create({ url: 'https://help.bitwarden.com/article/setup-two-step-login/' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.rate = function () {
|
|
||||||
$analytics.eventTrack('Rate Extension');
|
|
||||||
|
|
||||||
switch (utilsService.getBrowserString()) {
|
|
||||||
case 'chrome':
|
|
||||||
chrome.tabs.create({
|
|
||||||
url: 'https://chrome.google.com/webstore/detail/bitwarden-free-password-m/' +
|
|
||||||
'nngceckbapebfimnlniiiahkandclblb/reviews'
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'firefox':
|
|
||||||
chrome.tabs.create({
|
|
||||||
url: 'https://addons.mozilla.org/en-US/firefox/addon/' +
|
|
||||||
'bitwarden-password-manager/#reviews'
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'edge':
|
|
||||||
chrome.tabs.create({
|
|
||||||
url: 'https://www.microsoft.com/store/p/bitwarden-free-password-manager/9p6kxl0svnnl'
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case 'opera':
|
|
||||||
chrome.tabs.create({
|
|
||||||
url: 'https://addons.opera.com/en/extensions/details/' +
|
|
||||||
'bitwarden-free-password-manager/#feedback-container'
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,14 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsCreditsController', function ($scope, i18nService, $analytics) {
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
|
|
||||||
$scope.learnMore = function () {
|
|
||||||
$analytics.eventTrack('Contribute Learn More');
|
|
||||||
|
|
||||||
chrome.tabs.create({
|
|
||||||
url: 'https://github.com/bitwarden/browser/blob/master/CONTRIBUTING.md'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,57 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsEditFolderController', function ($scope, $stateParams, folderService, toastr, $state, SweetAlert,
|
|
||||||
utilsService, $analytics, i18nService, $timeout) {
|
|
||||||
$timeout(function () {
|
|
||||||
utilsService.initListSectionItemListeners(document, angular);
|
|
||||||
document.getElementById('name').focus();
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.folder = {};
|
|
||||||
var folderId = $stateParams.folderId;
|
|
||||||
|
|
||||||
folderService.get(folderId).then(function (folder) {
|
|
||||||
return folder.decrypt();
|
|
||||||
}).then(function (model) {
|
|
||||||
$scope.folder = model;
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.savePromise = null;
|
|
||||||
$scope.save = function (model) {
|
|
||||||
if (!model.name) {
|
|
||||||
toastr.error(i18nService.nameRequired, i18nService.errorsOccurred);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.savePromise = folderService.encrypt(model).then(function (folderModel) {
|
|
||||||
var folder = new Folder(folderModel, true);
|
|
||||||
return folderService.saveWithServer(folder).then(function (folder) {
|
|
||||||
$analytics.eventTrack('Edited Folder');
|
|
||||||
toastr.success(i18nService.editedFolder);
|
|
||||||
$state.go('folders', { animation: 'out-slide-down' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.delete = function () {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.deleteFolder,
|
|
||||||
text: i18nService.deleteFolderConfirmation,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.no
|
|
||||||
}, function (confirmed) {
|
|
||||||
if (confirmed) {
|
|
||||||
folderService.deleteWithServer(folderId).then(function () {
|
|
||||||
$analytics.eventTrack('Deleted Folder');
|
|
||||||
toastr.success(i18nService.deletedFolder);
|
|
||||||
$state.go('folders', {
|
|
||||||
animation: 'out-slide-down'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,38 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsEnvironmentController', function ($scope, i18nService, $analytics, utilsService,
|
|
||||||
environmentService, toastr, $timeout) {
|
|
||||||
$timeout(function () {
|
|
||||||
utilsService.initListSectionItemListeners(document, angular);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.baseUrl = environmentService.baseUrl || '';
|
|
||||||
$scope.webVaultUrl = environmentService.webVaultUrl || '';
|
|
||||||
$scope.apiUrl = environmentService.apiUrl || '';
|
|
||||||
$scope.identityUrl = environmentService.identityUrl || '';
|
|
||||||
$scope.iconsUrl = environmentService.iconsUrl || '';
|
|
||||||
|
|
||||||
$scope.save = function () {
|
|
||||||
environmentService.setUrls({
|
|
||||||
base: $scope.baseUrl,
|
|
||||||
api: $scope.apiUrl,
|
|
||||||
identity: $scope.identityUrl,
|
|
||||||
webVault: $scope.webVaultUrl,
|
|
||||||
icons: $scope.iconsUrl
|
|
||||||
}).then(function (resUrls) {
|
|
||||||
$timeout(function () {
|
|
||||||
// re-set urls since service can change them, ex: prefixing https://
|
|
||||||
$scope.baseUrl = resUrls.base;
|
|
||||||
$scope.apiUrl = resUrls.api;
|
|
||||||
$scope.identityUrl = resUrls.identity;
|
|
||||||
$scope.webVaultUrl = resUrls.webVault;
|
|
||||||
$scope.iconsUrl = resUrls.icons;
|
|
||||||
|
|
||||||
$analytics.eventTrack('Set Environment URLs');
|
|
||||||
toastr.success(i18nService.environmentSaved);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,214 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsFeaturesController', function ($scope, i18nService, $analytics, constantsService, utilsService,
|
|
||||||
totpService, stateService, $timeout) {
|
|
||||||
$timeout(function () {
|
|
||||||
utilsService.initListSectionItemListeners(document, angular);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.disableGa = false;
|
|
||||||
$scope.disableAddLoginNotification = false;
|
|
||||||
$scope.disableContextMenuItem = false;
|
|
||||||
$scope.disableAutoTotpCopy = false;
|
|
||||||
$scope.enableAutoFillOnPageLoad = false;
|
|
||||||
$scope.disableFavicon = false;
|
|
||||||
|
|
||||||
chrome.storage.local.get(constantsService.enableAutoFillOnPageLoadKey, function (obj) {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.enableAutoFillOnPageLoad = obj && obj[constantsService.enableAutoFillOnPageLoadKey] === true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
chrome.storage.local.get(constantsService.disableGaKey, function (obj) {
|
|
||||||
$timeout(function () {
|
|
||||||
// Default for Firefox is disabled.
|
|
||||||
if ((utilsService.isFirefox() && obj[constantsService.disableGaKey] === undefined) ||
|
|
||||||
obj[constantsService.disableGaKey]) {
|
|
||||||
$scope.disableGa = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.disableGa = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
chrome.storage.local.get(constantsService.disableAddLoginNotificationKey, function (obj) {
|
|
||||||
$timeout(function () {
|
|
||||||
if (obj && obj[constantsService.disableAddLoginNotificationKey]) {
|
|
||||||
$scope.disableAddLoginNotification = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.disableAddLoginNotification = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
chrome.storage.local.get(constantsService.disableContextMenuItemKey, function (obj) {
|
|
||||||
$timeout(function () {
|
|
||||||
if (obj && obj[constantsService.disableContextMenuItemKey]) {
|
|
||||||
$scope.disableContextMenuItem = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.disableContextMenuItem = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
totpService.isAutoCopyEnabled().then(function (enabled) {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.disableAutoTotpCopy = !enabled;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
chrome.storage.local.get(constantsService.disableFaviconKey, function (obj) {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.disableFavicon = obj && obj[constantsService.disableFaviconKey] === true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.updateGa = function () {
|
|
||||||
chrome.storage.local.get(constantsService.disableGaKey, function (obj) {
|
|
||||||
// Default for Firefox is disabled.
|
|
||||||
if ((utilsService.isFirefox() && obj[constantsService.disableGaKey] === undefined) ||
|
|
||||||
obj[constantsService.disableGaKey]) {
|
|
||||||
// enable
|
|
||||||
obj[constantsService.disableGaKey] = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// disable
|
|
||||||
$analytics.eventTrack('Disabled Analytics');
|
|
||||||
obj[constantsService.disableGaKey] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.disableGa = obj[constantsService.disableGaKey];
|
|
||||||
});
|
|
||||||
if (!obj[constantsService.disableGaKey]) {
|
|
||||||
$analytics.eventTrack('Enabled Analytics');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.updateAddLoginNotification = function () {
|
|
||||||
chrome.storage.local.get(constantsService.disableAddLoginNotificationKey, function (obj) {
|
|
||||||
if (obj[constantsService.disableAddLoginNotificationKey]) {
|
|
||||||
// enable
|
|
||||||
obj[constantsService.disableAddLoginNotificationKey] = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// disable
|
|
||||||
$analytics.eventTrack('Disabled Add Login Notification');
|
|
||||||
obj[constantsService.disableAddLoginNotificationKey] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.disableAddLoginNotification = obj[constantsService.disableAddLoginNotificationKey];
|
|
||||||
});
|
|
||||||
if (!obj[constantsService.disableAddLoginNotificationKey]) {
|
|
||||||
$analytics.eventTrack('Enabled Add Login Notification');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.updateDisableContextMenuItem = function () {
|
|
||||||
chrome.storage.local.get(constantsService.disableContextMenuItemKey, function (obj) {
|
|
||||||
if (obj[constantsService.disableContextMenuItemKey]) {
|
|
||||||
// enable
|
|
||||||
obj[constantsService.disableContextMenuItemKey] = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// disable
|
|
||||||
$analytics.eventTrack('Disabled Context Menu Item');
|
|
||||||
obj[constantsService.disableContextMenuItemKey] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.disableContextMenuItem = obj[constantsService.disableContextMenuItemKey];
|
|
||||||
});
|
|
||||||
if (!obj[constantsService.disableContextMenuItemKey]) {
|
|
||||||
$analytics.eventTrack('Enabled Context Menu Item');
|
|
||||||
}
|
|
||||||
chrome.runtime.sendMessage({
|
|
||||||
command: 'bgUpdateContextMenu'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.updateAutoTotpCopy = function () {
|
|
||||||
chrome.storage.local.get(constantsService.disableAutoTotpCopyKey, function (obj) {
|
|
||||||
if (obj[constantsService.disableAutoTotpCopyKey]) {
|
|
||||||
// enable
|
|
||||||
obj[constantsService.disableAutoTotpCopyKey] = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// disable
|
|
||||||
$analytics.eventTrack('Disabled Auto Copy TOTP');
|
|
||||||
obj[constantsService.disableAutoTotpCopyKey] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.disableAutoTotpCopy = obj[constantsService.disableAutoTotpCopyKey];
|
|
||||||
});
|
|
||||||
if (!obj[constantsService.disableAutoTotpCopyKey]) {
|
|
||||||
$analytics.eventTrack('Enabled Auto Copy TOTP');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.updateAutoFillOnPageLoad = function () {
|
|
||||||
chrome.storage.local.get(constantsService.enableAutoFillOnPageLoadKey, function (obj) {
|
|
||||||
if (obj[constantsService.enableAutoFillOnPageLoadKey]) {
|
|
||||||
// disable
|
|
||||||
obj[constantsService.enableAutoFillOnPageLoadKey] = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// enable
|
|
||||||
$analytics.eventTrack('Enable Auto-fill Page Load');
|
|
||||||
obj[constantsService.enableAutoFillOnPageLoadKey] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.enableAutoFillOnPageLoad = obj[constantsService.enableAutoFillOnPageLoadKey];
|
|
||||||
});
|
|
||||||
if (!obj[constantsService.enableAutoFillOnPageLoadKey]) {
|
|
||||||
$analytics.eventTrack('Disable Auto-fill Page Load');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.updateDisableFavicon = function () {
|
|
||||||
chrome.storage.local.get(constantsService.disableFaviconKey, function (obj) {
|
|
||||||
if (obj[constantsService.disableFaviconKey]) {
|
|
||||||
// enable
|
|
||||||
obj[constantsService.disableFaviconKey] = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// disable
|
|
||||||
$analytics.eventTrack('Disabled Favicon');
|
|
||||||
obj[constantsService.disableFaviconKey] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
chrome.storage.local.set(obj, function () {
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.disableFavicon = obj[constantsService.disableFaviconKey];
|
|
||||||
stateService.saveState('faviconEnabled', !$scope.disableFavicon);
|
|
||||||
});
|
|
||||||
if (!obj[constantsService.disableFaviconKey]) {
|
|
||||||
$analytics.eventTrack('Enabled Favicon');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,29 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsFoldersController', function ($scope, folderService, $q, $state, i18nService) {
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.loaded = false;
|
|
||||||
|
|
||||||
load();
|
|
||||||
function load() {
|
|
||||||
folderService.getAllDecrypted().then(function (folders) {
|
|
||||||
if (folders.length > 0 && folders[0].id === null) {
|
|
||||||
// remove the "none" folder
|
|
||||||
$scope.folders = folders.slice(1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.folders = folders;
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.loaded = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.editFolder = function (folder) {
|
|
||||||
$state.go('editFolder', {
|
|
||||||
folderId: folder.id,
|
|
||||||
animation: 'in-slide-up'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,25 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsHelpController', function ($scope, $analytics, i18nService) {
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.email = function () {
|
|
||||||
$analytics.eventTrack('Selected Help Email');
|
|
||||||
chrome.tabs.create({ url: 'mailto:hello@bitwarden.com' });
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.website = function () {
|
|
||||||
$analytics.eventTrack('Selected Help Website');
|
|
||||||
chrome.tabs.create({ url: 'https://bitwarden.com/contact/' });
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.tutorial = function () {
|
|
||||||
$analytics.eventTrack('Selected Help Tutorial');
|
|
||||||
chrome.tabs.create({ url: 'https://bitwarden.com/browser-start/' });
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.bug = function () {
|
|
||||||
$analytics.eventTrack('Selected Help Bug Report');
|
|
||||||
chrome.tabs.create({ url: 'https://github.com/bitwarden/browser' });
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,2 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings', ['oitozero.ngSweetAlert', 'toastr']);
|
|
@ -1,50 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsPremiumController', function ($scope, i18nService, tokenService, apiService, toastr, SweetAlert,
|
|
||||||
$analytics, $timeout) {
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.isPremium = tokenService.getPremium();
|
|
||||||
$scope.price = '$10';
|
|
||||||
|
|
||||||
$scope.refresh = function () {
|
|
||||||
apiService.refreshIdentityToken().then(function () {
|
|
||||||
toastr.success(i18nService.refreshComplete);
|
|
||||||
$timeout(function () {
|
|
||||||
$scope.isPremium = tokenService.getPremium();
|
|
||||||
});
|
|
||||||
}, function (err) {
|
|
||||||
toastr.error(i18nService.errorsOccurred);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.purchase = function () {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.premiumPurchase,
|
|
||||||
text: i18nService.premiumPurchaseAlert,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.cancel
|
|
||||||
}, function (confirmed) {
|
|
||||||
$analytics.eventTrack('Clicked Purchase Premium');
|
|
||||||
if (confirmed) {
|
|
||||||
chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=purchase' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.manage = function () {
|
|
||||||
SweetAlert.swal({
|
|
||||||
title: i18nService.premiumManage,
|
|
||||||
text: i18nService.premiumManageAlert,
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: i18nService.yes,
|
|
||||||
cancelButtonText: i18nService.cancel
|
|
||||||
}, function (confirmed) {
|
|
||||||
$analytics.eventTrack('Clicked Manage Membership');
|
|
||||||
if (confirmed) {
|
|
||||||
chrome.tabs.create({ url: 'https://vault.bitwarden.com/#/?premium=manage' });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,35 +0,0 @@
|
|||||||
angular
|
|
||||||
.module('bit.settings')
|
|
||||||
|
|
||||||
.controller('settingsSyncController', function ($scope, syncService, toastr, $analytics, i18nService) {
|
|
||||||
$scope.i18n = i18nService;
|
|
||||||
$scope.lastSync = '--';
|
|
||||||
$scope.loading = false;
|
|
||||||
setLastSync();
|
|
||||||
|
|
||||||
$scope.sync = function () {
|
|
||||||
$scope.loading = true;
|
|
||||||
syncService.fullSync(true).then(function (success) {
|
|
||||||
$scope.loading = false;
|
|
||||||
if (success) {
|
|
||||||
setLastSync();
|
|
||||||
$analytics.eventTrack('Synced Full');
|
|
||||||
toastr.success(i18nService.syncingComplete);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
toastr.error(i18nService.syncingFailed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function setLastSync() {
|
|
||||||
syncService.getLastSync().then(function (lastSync) {
|
|
||||||
if (lastSync) {
|
|
||||||
$scope.lastSync = lastSync.toLocaleDateString() + ' ' + lastSync.toLocaleTimeString();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$scope.lastSync = i18nService.never;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,17 +1,17 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
|
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{$ctrl.i18n.back}}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title">{{i18n.sync}}</div>
|
<div class="title">{{$ctrl.i18n.sync}}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="centered-message">
|
<div class="centered-message">
|
||||||
<p style="margin-top: -50px;">
|
<p style="margin-top: -50px;">
|
||||||
<a href="" class="btn btn-lg btn-link btn-block" style="display: inline-block;" ng-click="sync()">
|
<a href="" class="btn btn-lg btn-link btn-block" style="display: inline-block;" ng-click="$ctrl.sync()">
|
||||||
{{i18n.syncVaultNow}}
|
{{$ctrl.i18n.syncVaultNow}}
|
||||||
</a>
|
</a>
|
||||||
<small class="text-muted">{{i18n.lastSync}} {{lastSync}}</small>
|
<small class="text-muted">{{$ctrl.i18n.lastSync}} {{$ctrl.lastSync}}</small>
|
||||||
<span ng-show="loading" style="display: block; margin-top: 20px;" class="text-center">
|
<span ng-show="$ctrl.loading" style="display: block; margin-top: 20px;" class="text-center">
|
||||||
<i class="text-muted fa fa-lg fa-spinner fa-spin"></i>
|
<i class="text-muted fa fa-lg fa-spinner fa-spin"></i>
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
47
src/popup/app/settings/sync.component.ts
Normal file
47
src/popup/app/settings/sync.component.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import * as template from './sync.component.html';
|
||||||
|
|
||||||
|
class SyncController {
|
||||||
|
i18n: any;
|
||||||
|
lastSync = '--';
|
||||||
|
loading = false;
|
||||||
|
|
||||||
|
constructor(private syncService: any, private toastr: any, private $analytics: any, private i18nService: any) {
|
||||||
|
this.i18n = i18nService;
|
||||||
|
|
||||||
|
this.setLastSync();
|
||||||
|
}
|
||||||
|
|
||||||
|
sync() {
|
||||||
|
this.loading = true;
|
||||||
|
this.syncService
|
||||||
|
.fullSync(true)
|
||||||
|
.then((success: boolean) => {
|
||||||
|
this.loading = false;
|
||||||
|
if (success) {
|
||||||
|
this.setLastSync();
|
||||||
|
this.$analytics.eventTrack('Synced Full');
|
||||||
|
this.toastr.success(this.i18nService.syncingComplete);
|
||||||
|
} else {
|
||||||
|
this.toastr.error(this.i18nService.syncingFailed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setLastSync() {
|
||||||
|
this.syncService
|
||||||
|
.getLastSync()
|
||||||
|
.then((lastSync: any) => {
|
||||||
|
if (lastSync) {
|
||||||
|
this.lastSync = lastSync.toLocaleDateString() + ' ' + lastSync.toLocaleTimeString();
|
||||||
|
} else {
|
||||||
|
this.lastSync = this.i18nService.never;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SyncComponent = {
|
||||||
|
bindings: {},
|
||||||
|
controller: SyncController,
|
||||||
|
template,
|
||||||
|
};
|
@ -1,31 +0,0 @@
|
|||||||
<div class="header">
|
|
||||||
<div class="left">
|
|
||||||
<a ui-sref="tabs.settings({animation: 'out-slide-right'})"><i class="fa fa-chevron-left"></i> {{i18n.back}}</a>
|
|
||||||
</div>
|
|
||||||
<div class="right">
|
|
||||||
<a ui-sref="addFolder({animation: 'in-slide-up'})" title="{{::i18n.addFolder}}"><i class="fa fa-plus fa-lg"></i></a>
|
|
||||||
</div>
|
|
||||||
<div class="title">{{i18n.folders}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
<div ng-if="folders.length">
|
|
||||||
<div class="list">
|
|
||||||
<div class="list-grouped">
|
|
||||||
<a href="" ng-click="editFolder(folder)" class="list-grouped-item" title="{{::i18n.edit}}"
|
|
||||||
ng-repeat="folder in theFolders = (folders | orderBy: ['name']) track by $index">
|
|
||||||
<span class="text">{{folder.name}}</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="centered-message" ng-if="loaded && !folders.length">
|
|
||||||
<p>
|
|
||||||
{{i18n.noFolders}}
|
|
||||||
<a ui-sref="addFolder({animation: 'in-slide-up'})" style="margin-top: 20px;"
|
|
||||||
class="btn btn-link btn-block">{{i18n.addFolder}}</a>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="page-loading" ng-if="!loaded">
|
|
||||||
<i class="fa fa-lg fa-spinner fa-spin"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue
Block a user