diff --git a/jslib b/jslib index 5d3b99ce..31bd1f94 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit 5d3b99ce6f93c3c43adc9e3eb784bc00b728a7f2 +Subproject commit 31bd1f9423f71ed749d27f9500eb88d272f112d1 diff --git a/src/app/accounts/environment.component.html b/src/app/accounts/environment.component.html new file mode 100644 index 00000000..648b4a53 --- /dev/null +++ b/src/app/accounts/environment.component.html @@ -0,0 +1,59 @@ + diff --git a/src/app/accounts/environment.component.ts b/src/app/accounts/environment.component.ts new file mode 100644 index 00000000..0bf26249 --- /dev/null +++ b/src/app/accounts/environment.component.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { EnvironmentService } from 'jslib/abstractions/environment.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; + +import { EnvironmentComponent as BaseEnvironmentComponent } from 'jslib/angular/components/environment.component'; + +@Component({ + selector: 'app-environment', + templateUrl: 'environment.component.html', +}) +export class EnvironmentComponent extends BaseEnvironmentComponent { + constructor(analytics: Angulartics2, toasterService: ToasterService, + environmentService: EnvironmentService, i18nService: I18nService) { + super(analytics, toasterService, environmentService, i18nService); + } +} diff --git a/src/app/accounts/login.component.html b/src/app/accounts/login.component.html new file mode 100644 index 00000000..36e4e2d4 --- /dev/null +++ b/src/app/accounts/login.component.html @@ -0,0 +1,45 @@ +
+
+ bitwarden +

{{'loginOrCreateNewAccount' | i18n}}

+
+
+
+ + +
+
+
+ + +
+
+ + + +
+
+
+
+
+ + + {{'createAccount' | i18n}} + +
+ + +  {{'settings' | i18n}} + +
+
+ diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts new file mode 100644 index 00000000..506c41fc --- /dev/null +++ b/src/app/accounts/login.component.ts @@ -0,0 +1,45 @@ +import { + Component, + ComponentFactoryResolver, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { Router } from '@angular/router'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { EnvironmentComponent } from './environment.component'; + +import { AuthService } from 'jslib/abstractions/auth.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { SyncService } from 'jslib/abstractions/sync.service'; + +import { LoginComponent as BaseLoginComponent } from 'jslib/angular/components/login.component'; +import { ModalComponent } from 'jslib/angular/components/modal.component'; + +@Component({ + selector: 'app-login', + templateUrl: 'login.component.html', +}) +export class LoginComponent extends BaseLoginComponent { + @ViewChild('environment', { read: ViewContainerRef }) environmentModal: ViewContainerRef; + + constructor(authService: AuthService, router: Router, + analytics: Angulartics2, toasterService: ToasterService, + i18nService: I18nService, syncService: SyncService, + private componentFactoryResolver: ComponentFactoryResolver) { + super(authService, router, analytics, toasterService, i18nService, syncService); + } + + settings() { + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + const modal = this.environmentModal.createComponent(factory).instance; + const childComponent = modal.show(EnvironmentComponent, + this.environmentModal); + + childComponent.onSaved.subscribe(() => { + modal.close(); + }); + } +} diff --git a/src/app/accounts/two-factor-options.component.html b/src/app/accounts/two-factor-options.component.html new file mode 100644 index 00000000..740365d1 --- /dev/null +++ b/src/app/accounts/two-factor-options.component.html @@ -0,0 +1,28 @@ + diff --git a/src/app/accounts/two-factor-options.component.ts b/src/app/accounts/two-factor-options.component.ts new file mode 100644 index 00000000..6ab0be4f --- /dev/null +++ b/src/app/accounts/two-factor-options.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { AuthService } from 'jslib/abstractions/auth.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; + +import { + TwoFactorOptionsComponent as BaseTwoFactorOptionsComponent, +} from 'jslib/angular/components/two-factor-options.component'; + +@Component({ + selector: 'app-two-factor-options', + templateUrl: 'two-factor-options.component.html', +}) +export class TwoFactorOptionsComponent extends BaseTwoFactorOptionsComponent { + constructor(authService: AuthService, router: Router, + analytics: Angulartics2, toasterService: ToasterService, + i18nService: I18nService, platformUtilsService: PlatformUtilsService) { + super(authService, router, analytics, toasterService, i18nService, platformUtilsService, window); + } +} diff --git a/src/app/accounts/two-factor.component.html b/src/app/accounts/two-factor.component.html new file mode 100644 index 00000000..8bfe9fbf --- /dev/null +++ b/src/app/accounts/two-factor.component.html @@ -0,0 +1,75 @@ +
+
+

{{title}}

+

{{'enterVerificationCodeApp' | i18n}}

+

+ {{'enterVerificationCodeEmail' | i18n : twoFactorEmail}} +

+
+
+
+ + +
+
+ + +
+
+
+ +

{{'insertYubiKey' | i18n}}

+ +
+
+
+ + +
+
+ + +
+
+
+
+ +
+
+
+
+ + +
+
+
+
+
+
+
+

{{'noTwoStepProviders' | i18n}}

+

{{'noTwoStepProviders2' | i18n}}

+
+
+
+
+ + {{'cancel' | i18n}} +
+ +
+
+ diff --git a/src/app/accounts/two-factor.component.ts b/src/app/accounts/two-factor.component.ts new file mode 100644 index 00000000..7a035b27 --- /dev/null +++ b/src/app/accounts/two-factor.component.ts @@ -0,0 +1,58 @@ +import { + Component, + ComponentFactoryResolver, + ViewChild, + ViewContainerRef, +} from '@angular/core'; + +import { Router } from '@angular/router'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { TwoFactorOptionsComponent } from './two-factor-options.component'; + +import { TwoFactorProviderType } from 'jslib/enums/twoFactorProviderType'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { AuthService } from 'jslib/abstractions/auth.service'; +import { EnvironmentService } from 'jslib/abstractions/environment.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { SyncService } from 'jslib/abstractions/sync.service'; + +import { ModalComponent } from 'jslib/angular/components/modal.component'; +import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib/angular/components/two-factor.component'; + +@Component({ + selector: 'app-two-factor', + templateUrl: 'two-factor.component.html', +}) +export class TwoFactorComponent extends BaseTwoFactorComponent { + @ViewChild('twoFactorOptions', { read: ViewContainerRef }) twoFactorOptionsModal: ViewContainerRef; + + constructor(authService: AuthService, router: Router, + analytics: Angulartics2, toasterService: ToasterService, + i18nService: I18nService, apiService: ApiService, + platformUtilsService: PlatformUtilsService, syncService: SyncService, + environmentService: EnvironmentService, private componentFactoryResolver: ComponentFactoryResolver) { + super(authService, router, analytics, toasterService, i18nService, apiService, + platformUtilsService, syncService, window, environmentService); + } + + anotherMethod() { + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + const modal = this.twoFactorOptionsModal.createComponent(factory).instance; + const childComponent = modal.show(TwoFactorOptionsComponent, + this.twoFactorOptionsModal); + + childComponent.onProviderSelected.subscribe(async (provider: TwoFactorProviderType) => { + modal.close(); + this.selectedProviderType = provider; + await this.init(); + }); + childComponent.onRecoverSelected.subscribe(() => { + modal.close(); + }); + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts new file mode 100644 index 00000000..e996d879 --- /dev/null +++ b/src/app/app-routing.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { + RouterModule, + Routes, +} from '@angular/router'; + +import { AuthGuardService } from 'jslib/angular/services/auth-guard.service'; + +import { LoginComponent } from './accounts/login.component'; +import { TwoFactorComponent } from './accounts/two-factor.component'; +import { DashboardComponent } from './dashboard/dashboard.component'; + +const routes: Routes = [ + { path: '', redirectTo: '/dashboard', pathMatch: 'full' }, + { path: 'login', component: LoginComponent }, + { path: '2fa', component: TwoFactorComponent }, + { + path: 'dashboard', + component: DashboardComponent, + canActivate: [AuthGuardService], + }, +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes, { + useHash: true, + /*enableTracing: true,*/ + })], + exports: [RouterModule], +}) +export class AppRoutingModule { } diff --git a/src/app/app.component.ts b/src/app/app.component.ts new file mode 100644 index 00000000..0c83b463 --- /dev/null +++ b/src/app/app.component.ts @@ -0,0 +1,153 @@ +import { + ToasterConfig, + ToasterContainerComponent, +} from 'angular2-toaster'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; + +import { + Component, + ComponentFactoryResolver, + NgZone, + OnDestroy, + OnInit, + Type, + ViewChild, + ViewContainerRef, +} from '@angular/core'; +import { Router } from '@angular/router'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { ModalComponent } from 'jslib/angular/components/modal.component'; + +import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; + +import { AuthService } from 'jslib/abstractions/auth.service'; +import { CryptoService } from 'jslib/abstractions/crypto.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { MessagingService } from 'jslib/abstractions/messaging.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { StorageService } from 'jslib/abstractions/storage.service'; +import { TokenService } from 'jslib/abstractions/token.service'; +import { UserService } from 'jslib/abstractions/user.service'; + +import { ConstantsService } from 'jslib/services/constants.service'; + +const BroadcasterSubscriptionId = 'AppComponent'; + +@Component({ + selector: 'app-root', + styles: [], + template: ` + + + `, +}) +export class AppComponent implements OnInit { + @ViewChild('settings', { read: ViewContainerRef }) settingsRef: ViewContainerRef; + + toasterConfig: ToasterConfig = new ToasterConfig({ + showCloseButton: true, + mouseoverTimerStop: true, + animation: 'flyRight', + limit: 5, + }); + + private lastActivity: number = null; + private modal: ModalComponent = null; + + constructor(private angulartics2GoogleAnalytics: Angulartics2GoogleAnalytics, + private broadcasterService: BroadcasterService, private userService: UserService, + private tokenService: TokenService, private storageService: StorageService, + private authService: AuthService, private router: Router, private analytics: Angulartics2, + private toasterService: ToasterService, private i18nService: I18nService, + private platformUtilsService: PlatformUtilsService, private ngZone: NgZone, + private cryptoService: CryptoService, private componentFactoryResolver: ComponentFactoryResolver, + private messagingService: MessagingService) { } + + ngOnInit() { + this.ngZone.runOutsideAngular(() => { + setTimeout(async () => { + await this.updateAppMenu(); + }, 1000); + + window.onmousemove = () => this.recordActivity(); + window.onmousedown = () => this.recordActivity(); + window.ontouchstart = () => this.recordActivity(); + window.onclick = () => this.recordActivity(); + window.onscroll = () => this.recordActivity(); + window.onkeypress = () => this.recordActivity(); + }); + + this.broadcasterService.subscribe(BroadcasterSubscriptionId, async (message: any) => { + this.ngZone.run(async () => { + switch (message.command) { + case 'loggedIn': + case 'unlocked': + case 'loggedOut': + this.updateAppMenu(); + break; + case 'logout': + this.logOut(!!message.expired); + break; + default: + } + }); + }); + } + + ngOnDestroy() { + this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + } + + private async updateAppMenu() { + this.messagingService.send('updateAppMenu', { + isAuthenticated: await this.userService.isAuthenticated(), + isLocked: (await this.cryptoService.getKey()) == null, + }); + } + + private async logOut(expired: boolean) { + const userId = await this.userService.getUserId(); + + await Promise.all([ + this.tokenService.clearToken(), + this.cryptoService.clearKeys(), + this.userService.clear(), + ]); + + this.authService.logOut(async () => { + this.analytics.eventTrack.next({ action: 'Logged Out' }); + if (expired) { + this.toasterService.popAsync('warning', this.i18nService.t('loggedOut'), + this.i18nService.t('loginExpired')); + } + this.router.navigate(['login']); + }); + } + + private async recordActivity() { + const now = (new Date()).getTime(); + if (this.lastActivity != null && now - this.lastActivity < 250) { + return; + } + + this.lastActivity = now; + this.storageService.save(ConstantsService.lastActiveKey, now); + } + + private openModal(type: Type, ref: ViewContainerRef) { + if (this.modal != null) { + this.modal.close(); + } + + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = ref.createComponent(factory).instance; + const childComponent = this.modal.show(type, ref); + + this.modal.onClosed.subscribe(() => { + this.modal = null; + }); + } +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts new file mode 100644 index 00000000..8cfeec2b --- /dev/null +++ b/src/app/app.module.ts @@ -0,0 +1,79 @@ +import 'core-js'; +import 'zone.js/dist/zone'; + +import { ToasterModule } from 'angular2-toaster'; +import { Angulartics2Module } from 'angulartics2'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; + +import { AppRoutingModule } from './app-routing.module'; +import { ServicesModule } from './services.module'; + +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; + +import { AppComponent } from './app.component'; + +import { IconComponent } from 'jslib/angular/components/icon.component'; +import { ModalComponent } from 'jslib/angular/components/modal.component'; + +import { EnvironmentComponent } from './accounts/environment.component'; +import { LoginComponent } from './accounts/login.component'; +import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component'; +import { TwoFactorComponent } from './accounts/two-factor.component'; +import { DashboardComponent } from './dashboard/dashboard.component'; + +import { ApiActionDirective } from 'jslib/angular/directives/api-action.directive'; +import { AutofocusDirective } from 'jslib/angular/directives/autofocus.directive'; +import { BlurClickDirective } from 'jslib/angular/directives/blur-click.directive'; +import { BoxRowDirective } from 'jslib/angular/directives/box-row.directive'; +import { FallbackSrcDirective } from 'jslib/angular/directives/fallback-src.directive'; +import { StopClickDirective } from 'jslib/angular/directives/stop-click.directive'; +import { StopPropDirective } from 'jslib/angular/directives/stop-prop.directive'; + +import { I18nPipe } from 'jslib/angular/pipes/i18n.pipe'; +import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; + +@NgModule({ + imports: [ + BrowserModule, + BrowserAnimationsModule, + FormsModule, + AppRoutingModule, + ServicesModule, + Angulartics2Module.forRoot([Angulartics2GoogleAnalytics], { + pageTracking: { + clearQueryParams: true, + }, + }), + ToasterModule, + ], + declarations: [ + ApiActionDirective, + AppComponent, + AutofocusDirective, + BlurClickDirective, + BoxRowDirective, + DashboardComponent, + EnvironmentComponent, + FallbackSrcDirective, + I18nPipe, + IconComponent, + LoginComponent, + ModalComponent, + SearchCiphersPipe, + StopClickDirective, + StopPropDirective, + TwoFactorComponent, + TwoFactorOptionsComponent, + ], + entryComponents: [ + EnvironmentComponent, + ModalComponent, + TwoFactorOptionsComponent, + ], + providers: [], + bootstrap: [AppComponent], +}) +export class AppModule { } diff --git a/src/app/dashboard/dashboard.component.html b/src/app/dashboard/dashboard.component.html new file mode 100644 index 00000000..6773143e --- /dev/null +++ b/src/app/dashboard/dashboard.component.html @@ -0,0 +1 @@ +The dashboard!! diff --git a/src/app/dashboard/dashboard.component.ts b/src/app/dashboard/dashboard.component.ts new file mode 100644 index 00000000..1a233db0 --- /dev/null +++ b/src/app/dashboard/dashboard.component.ts @@ -0,0 +1,24 @@ +import { + Component, + ComponentFactoryResolver, + ViewChild, + ViewContainerRef, +} from '@angular/core'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { I18nService } from 'jslib/abstractions/i18n.service'; + +import { ModalComponent } from 'jslib/angular/components/modal.component'; + +@Component({ + selector: 'app-dashboard', + templateUrl: 'dashboard.component.html', +}) +export class DashboardComponent { + @ViewChild('settings', { read: ViewContainerRef }) settingsModal: ViewContainerRef; + + constructor(analytics: Angulartics2, toasterService: ToasterService, + i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver) {} +} diff --git a/src/app/main.ts b/src/app/main.ts new file mode 100644 index 00000000..c3eb3f52 --- /dev/null +++ b/src/app/main.ts @@ -0,0 +1,17 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { isDev } from 'jslib/electron/utils'; + +// tslint:disable-next-line +require('../scss/styles.scss'); +// tslint:disable-next-line +require('../../jslib/src/misc/duo.js'); + +import { AppModule } from './app.module'; + +if (!isDev()) { + enableProdMode(); +} + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/src/app/services.module.ts b/src/app/services.module.ts new file mode 100644 index 00000000..42d17dcd --- /dev/null +++ b/src/app/services.module.ts @@ -0,0 +1,129 @@ +import { + APP_INITIALIZER, + NgModule, +} from '@angular/core'; + +import { ToasterModule } from 'angular2-toaster'; + +import { ElectronLogService } from 'jslib/electron/services/electronLog.service'; +import { ElectronPlatformUtilsService } from 'jslib/electron/services/electronPlatformUtils.service'; +import { ElectronRendererMessagingService } from 'jslib/electron/services/electronRendererMessaging.service'; +import { ElectronRendererSecureStorageService } from 'jslib/electron/services/electronRendererSecureStorage.service'; +import { ElectronStorageService } from 'jslib/electron/services/electronStorage.service'; +import { isDev } from 'jslib/electron/utils'; + +import { I18nService } from '../services/i18n.service'; + +import { AuthGuardService } from 'jslib/angular/services/auth-guard.service'; +import { BroadcasterService } from 'jslib/angular/services/broadcaster.service'; +import { ValidationService } from 'jslib/angular/services/validation.service'; + +import { Analytics } from 'jslib/misc/analytics'; + +import { ApiService } from 'jslib/services/api.service'; +import { AppIdService } from 'jslib/services/appId.service'; +import { AuthService } from 'jslib/services/auth.service'; +import { ConstantsService } from 'jslib/services/constants.service'; +import { ContainerService } from 'jslib/services/container.service'; +import { CryptoService } from 'jslib/services/crypto.service'; +import { EnvironmentService } from 'jslib/services/environment.service'; +import { NodeCryptoFunctionService } from 'jslib/services/nodeCryptoFunction.service'; +import { StateService } from 'jslib/services/state.service'; +import { TokenService } from 'jslib/services/token.service'; +import { UserService } from 'jslib/services/user.service'; + +import { ApiService as ApiServiceAbstraction } from 'jslib/abstractions/api.service'; +import { AppIdService as AppIdServiceAbstraction } from 'jslib/abstractions/appId.service'; +import { AuthService as AuthServiceAbstraction } from 'jslib/abstractions/auth.service'; +import { CryptoService as CryptoServiceAbstraction } from 'jslib/abstractions/crypto.service'; +import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from 'jslib/abstractions/cryptoFunction.service'; +import { EnvironmentService as EnvironmentServiceAbstraction } from 'jslib/abstractions/environment.service'; +import { I18nService as I18nServiceAbstraction } from 'jslib/abstractions/i18n.service'; +import { LogService as LogServiceAbstraction } from 'jslib/abstractions/log.service'; +import { MessagingService as MessagingServiceAbstraction } from 'jslib/abstractions/messaging.service'; +import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from 'jslib/abstractions/platformUtils.service'; +import { StateService as StateServiceAbstraction } from 'jslib/abstractions/state.service'; +import { StorageService as StorageServiceAbstraction } from 'jslib/abstractions/storage.service'; +import { TokenService as TokenServiceAbstraction } from 'jslib/abstractions/token.service'; +import { UserService as UserServiceAbstraction } from 'jslib/abstractions/user.service'; + +const logService = new ElectronLogService(); +const i18nService = new I18nService(window.navigator.language, './locales'); +const stateService = new StateService(); +const platformUtilsService = new ElectronPlatformUtilsService(i18nService, true); +const broadcasterService = new BroadcasterService(); +const messagingService = new ElectronRendererMessagingService(broadcasterService); +const storageService: StorageServiceAbstraction = new ElectronStorageService(); +const secureStorageService: StorageServiceAbstraction = new ElectronRendererSecureStorageService(); +const cryptoFunctionService: CryptoFunctionServiceAbstraction = new NodeCryptoFunctionService(); +const cryptoService = new CryptoService(storageService, secureStorageService, cryptoFunctionService); +const appIdService = new AppIdService(storageService); +const tokenService = new TokenService(storageService); +const apiService = new ApiService(tokenService, platformUtilsService, + (expired: boolean) => messagingService.send('logout', { expired: expired })); +const environmentService = new EnvironmentService(apiService, storageService); +const userService = new UserService(tokenService, storageService); +const containerService = new ContainerService(cryptoService, platformUtilsService); +const authService = new AuthService(cryptoService, apiService, + userService, tokenService, appIdService, i18nService, platformUtilsService, messagingService); + +const analytics = new Analytics(window, () => isDev(), platformUtilsService, storageService, appIdService); +containerService.attachToWindow(window); +environmentService.setUrlsFromStorage().then(() => { + // Do nothing +}); + +export function initFactory(): Function { + return async () => { + await i18nService.init(); + await authService.init(); + const htmlEl = window.document.documentElement; + htmlEl.classList.add('os_' + platformUtilsService.getDeviceString()); + htmlEl.classList.add('locale_' + i18nService.translationLocale); + + let installAction = null; + const installedVersion = await storageService.get(ConstantsService.installedVersionKey); + const currentVersion = platformUtilsService.getApplicationVersion(); + if (installedVersion == null) { + installAction = 'install'; + } else if (installedVersion !== currentVersion) { + installAction = 'update'; + } + + if (installAction != null) { + await storageService.save(ConstantsService.installedVersionKey, currentVersion); + } + }; +} + +@NgModule({ + imports: [ + ToasterModule, + ], + declarations: [], + providers: [ + ValidationService, + AuthGuardService, + { provide: AuthServiceAbstraction, useValue: authService }, + { provide: EnvironmentServiceAbstraction, useValue: environmentService }, + { provide: TokenServiceAbstraction, useValue: tokenService }, + { provide: I18nServiceAbstraction, useValue: i18nService }, + { provide: CryptoServiceAbstraction, useValue: cryptoService }, + { provide: PlatformUtilsServiceAbstraction, useValue: platformUtilsService }, + { provide: ApiServiceAbstraction, useValue: apiService }, + { provide: UserServiceAbstraction, useValue: userService }, + { provide: MessagingServiceAbstraction, useValue: messagingService }, + { provide: BroadcasterService, useValue: broadcasterService }, + { provide: StorageServiceAbstraction, useValue: storageService }, + { provide: StateServiceAbstraction, useValue: stateService }, + { provide: LogServiceAbstraction, useValue: logService }, + { + provide: APP_INITIALIZER, + useFactory: initFactory, + deps: [], + multi: true, + }, + ], +}) +export class ServicesModule { +} diff --git a/src/images/icon.png b/src/images/icon.png new file mode 100644 index 00000000..54fd8903 Binary files /dev/null and b/src/images/icon.png differ diff --git a/src/images/loading.svg b/src/images/loading.svg new file mode 100644 index 00000000..70763105 --- /dev/null +++ b/src/images/loading.svg @@ -0,0 +1,6 @@ + + + Loading... + + diff --git a/src/images/logo.png b/src/images/logo.png new file mode 100644 index 00000000..33ced5b1 Binary files /dev/null and b/src/images/logo.png differ diff --git a/src/images/logo@2x.png b/src/images/logo@2x.png new file mode 100644 index 00000000..2a0ba60b Binary files /dev/null and b/src/images/logo@2x.png differ diff --git a/src/images/logo@3x.png b/src/images/logo@3x.png new file mode 100644 index 00000000..90473167 Binary files /dev/null and b/src/images/logo@3x.png differ diff --git a/src/images/u2fkey.jpg b/src/images/u2fkey.jpg new file mode 100644 index 00000000..8013df0e Binary files /dev/null and b/src/images/u2fkey.jpg differ diff --git a/src/images/yubikey.jpg b/src/images/yubikey.jpg new file mode 100644 index 00000000..9ddf755d Binary files /dev/null and b/src/images/yubikey.jpg differ diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json new file mode 100644 index 00000000..8bac4afa --- /dev/null +++ b/src/locales/en/messages.json @@ -0,0 +1,5 @@ +{ + "hello": { + "message": "hello" + } +} diff --git a/src/services/i18n.service.ts b/src/services/i18n.service.ts new file mode 100644 index 00000000..d18dc1be --- /dev/null +++ b/src/services/i18n.service.ts @@ -0,0 +1,15 @@ +import * as fs from 'fs'; +import * as path from 'path'; + +import { I18nService as BaseI18nService } from 'jslib/services/i18n.service'; + +export class I18nService extends BaseI18nService { + constructor(systemLanguage: string, localesDirectory: string) { + super(systemLanguage, localesDirectory, (formattedLocale: string) => { + const filePath = path.join(__dirname, this.localesDirectory + '/' + formattedLocale + '/messages.json'); + const localesJson = fs.readFileSync(filePath, 'utf8'); + const locales = JSON.parse(localesJson.replace(/^\uFEFF/, '')); // strip the BOM + return Promise.resolve(locales); + }); + } +} diff --git a/tsconfig.json b/tsconfig.json index de1c84c6..580f1783 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,6 +21,7 @@ "exclude": [ "node_modules", "jslib/node_modules", + "jslib/src/services/webCryptoFunction.service.ts", "dist", "jslib/dist", "build",