diff --git a/app/assets/javascripts/app.ts b/app/assets/javascripts/app.ts index 64fb23e63..ff91d5a65 100644 --- a/app/assets/javascripts/app.ts +++ b/app/assets/javascripts/app.ts @@ -1,6 +1,7 @@ 'use strict'; -declare const __VERSION__: string +declare const __VERSION__: string; +declare const __PLATFORM_WEB__: boolean; import angular from 'angular'; import { configRoutes } from './routes'; @@ -50,74 +51,84 @@ import { import { trusted } from './filters'; import { isDev } from './utils'; +import { Platform, WebPlatform } from './services/platform'; -angular.module('app', ['ngSanitize']); - -// Config -angular - .module('app') - .config(configRoutes) - .constant('appVersion', __VERSION__); - -// Controllers -angular - .module('app') - .directive('applicationGroupView', () => new ApplicationGroupView()) - .directive('applicationView', () => new ApplicationView()) - .directive('editorGroupView', () => new EditorGroupView()) - .directive('editorView', () => new EditorView()) - .directive('tagsView', () => new TagsView()) - .directive('notesView', () => new NotesView()) - .directive('footerView', () => new FooterView()) - -// Directives - Functional -angular - .module('app') - .directive('snAutofocus', ['$timeout', autofocus]) - .directive('clickOutside', ['$document', clickOutside]) - .directive('delayHide', delayHide) - .directive('elemReady', elemReady) - .directive('fileChange', fileChange) - .directive('infiniteScroll', [infiniteScroll]) - .directive('lowercase', lowercase) - .directive('selectOnFocus', ['$window', selectOnFocus]) - .directive('snEnter', snEnter); - -// Directives - Views -angular - .module('app') - .directive('accountMenu', () => new AccountMenu()) - .directive('actionsMenu', () => new ActionsMenu()) - .directive('challengeModal', () => new ChallengeModal()) - .directive('componentModal', () => new ComponentModal()) - .directive('componentView', () => new ComponentView()) - .directive('editorMenu', () => new EditorMenu()) - .directive('inputModal', () => new InputModal()) - .directive('menuRow', () => new MenuRow()) - .directive('panelResizer', () => new PanelResizer()) - .directive('passwordWizard', () => new PasswordWizard()) - .directive('permissionsModal', () => new PermissionsModal()) - .directive('privilegesAuthModal', () => new PrivilegesAuthModal()) - .directive('privilegesManagementModal', () => new PrivilegesManagementModal()) - .directive('revisionPreviewModal', () => new RevisionPreviewModal()) - .directive('sessionHistoryMenu', () => new SessionHistoryMenu()) - .directive('syncResolutionMenu', () => new SyncResolutionMenu()); - -// Filters -angular - .module('app') - .filter('trusted', ['$sce', trusted]); - -// Services -angular.module('app').service('mainApplicationGroup', ApplicationGroup); - -// Debug -if (isDev) { - Object.defineProperties(window, { - application: { - get: () => - (angular.element(document).injector().get('mainApplicationGroup') as any) - .application, - }, - }); +if (__PLATFORM_WEB__) { + startApplication(new WebPlatform()); +} else { + (window as any).startApplication = startApplication; +} + +function startApplication(platform: Platform) { + angular.module('app', ['ngSanitize']); + + // Config + angular + .module('app') + .config(configRoutes) + .constant('platform', platform) + .constant('appVersion', __VERSION__); + + // Controllers + angular + .module('app') + .directive('applicationGroupView', () => new ApplicationGroupView()) + .directive('applicationView', () => new ApplicationView()) + .directive('editorGroupView', () => new EditorGroupView()) + .directive('editorView', () => new EditorView()) + .directive('tagsView', () => new TagsView()) + .directive('notesView', () => new NotesView()) + .directive('footerView', () => new FooterView()) + + // Directives - Functional + angular + .module('app') + .directive('snAutofocus', ['$timeout', autofocus]) + .directive('clickOutside', ['$document', clickOutside]) + .directive('delayHide', delayHide) + .directive('elemReady', elemReady) + .directive('fileChange', fileChange) + .directive('infiniteScroll', [infiniteScroll]) + .directive('lowercase', lowercase) + .directive('selectOnFocus', ['$window', selectOnFocus]) + .directive('snEnter', snEnter); + + // Directives - Views + angular + .module('app') + .directive('accountMenu', () => new AccountMenu()) + .directive('actionsMenu', () => new ActionsMenu()) + .directive('challengeModal', () => new ChallengeModal()) + .directive('componentModal', () => new ComponentModal()) + .directive('componentView', () => new ComponentView()) + .directive('editorMenu', () => new EditorMenu()) + .directive('inputModal', () => new InputModal()) + .directive('menuRow', () => new MenuRow()) + .directive('panelResizer', () => new PanelResizer()) + .directive('passwordWizard', () => new PasswordWizard()) + .directive('permissionsModal', () => new PermissionsModal()) + .directive('privilegesAuthModal', () => new PrivilegesAuthModal()) + .directive('privilegesManagementModal', () => new PrivilegesManagementModal()) + .directive('revisionPreviewModal', () => new RevisionPreviewModal()) + .directive('sessionHistoryMenu', () => new SessionHistoryMenu()) + .directive('syncResolutionMenu', () => new SyncResolutionMenu()); + + // Filters + angular + .module('app') + .filter('trusted', ['$sce', trusted]); + + // Services + angular.module('app').service('mainApplicationGroup', ApplicationGroup); + + // Debug + if (isDev) { + Object.defineProperties(window, { + application: { + get: () => + (angular.element(document).injector().get('mainApplicationGroup') as any) + .application, + }, + }); + } } diff --git a/app/assets/javascripts/interface.ts b/app/assets/javascripts/interface.ts index 2e01f34a9..4b4b9f917 100644 --- a/app/assets/javascripts/interface.ts +++ b/app/assets/javascripts/interface.ts @@ -1,13 +1,16 @@ import { DeviceInterface, getGlobalScope, SNApplication } from 'snjs'; import { Database } from '@/database'; - -const KEYCHAIN_STORAGE_KEY = 'keychain'; +import { Platform } from './services/platform'; export class WebDeviceInterface extends DeviceInterface { private database: Database - constructor(namespace: string, timeout: any) { + constructor( + namespace: string, + timeout: any, + private platform: Platform + ) { super( namespace, timeout || setTimeout.bind(getGlobalScope()), @@ -97,19 +100,16 @@ export class WebDeviceInterface extends DeviceInterface { return this.database.clearAllPayloads(); } - async getKeychainValue() { - const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY); - if (value) { - return JSON.parse(value); - } + getKeychainValue(): Promise { + return this.platform.getKeychainValue(); } - async setKeychainValue(value: any) { - localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value)); + setKeychainValue(value: any) { + return this.platform.setKeychainValue(value); } - async clearKeychainValue() { - localStorage.removeItem(KEYCHAIN_STORAGE_KEY); + clearKeychainValue() { + return this.platform.clearKeychainValue(); } openUrl(url: string) { diff --git a/app/assets/javascripts/services/platform.ts b/app/assets/javascripts/services/platform.ts new file mode 100644 index 000000000..424749dc5 --- /dev/null +++ b/app/assets/javascripts/services/platform.ts @@ -0,0 +1,24 @@ +/** Platform-specific (i-e desktop/web) behavior is handled by a Platform object. */ +export interface Platform { + getKeychainValue(): Promise; + setKeychainValue(value: any): Promise; + clearKeychainValue(): Promise; +} + +const KEYCHAIN_STORAGE_KEY = 'keychain'; + +export class WebPlatform implements Platform { + async getKeychainValue(): Promise { + const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY); + if (value) { + return JSON.parse(value); + } + } + async setKeychainValue(value: any): Promise { + localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value)); + } + + async clearKeychainValue(): Promise { + localStorage.removeItem(KEYCHAIN_STORAGE_KEY); + } +} \ No newline at end of file diff --git a/app/assets/javascripts/ui_models/application.ts b/app/assets/javascripts/ui_models/application.ts index 8046e8731..d60411b5b 100644 --- a/app/assets/javascripts/ui_models/application.ts +++ b/app/assets/javascripts/ui_models/application.ts @@ -26,6 +26,7 @@ import { } from '@/services'; import { AppState } from '@/ui_models/app_state'; import { SNWebCrypto } from 'sncrypto/dist/sncrypto-web'; +import { Platform } from '@/services/platform'; type WebServices = { appState: AppState @@ -54,10 +55,15 @@ export class WebApplication extends SNApplication { $compile: ng.ICompileService, $timeout: ng.ITimeoutService, scope: ng.IScope, - onDeinit: (app: WebApplication) => void + onDeinit: (app: WebApplication) => void, + platform: Platform, ) { const namespace = ''; - const deviceInterface = new WebDeviceInterface(namespace, $timeout); + const deviceInterface = new WebDeviceInterface( + namespace, + $timeout, + platform + ); super( Environment.Web, platformFromString(getPlatformString()), diff --git a/app/assets/javascripts/ui_models/application_group.ts b/app/assets/javascripts/ui_models/application_group.ts index 125519373..b2dd3b2a3 100644 --- a/app/assets/javascripts/ui_models/application_group.ts +++ b/app/assets/javascripts/ui_models/application_group.ts @@ -11,6 +11,7 @@ import { ThemeManager } from '@/services'; import { AppState } from '@/ui_models/app_state'; +import { Platform } from '@/services/platform'; type AppManagerChangeCallback = () => void @@ -27,7 +28,8 @@ export class ApplicationGroup { constructor( $compile: ng.ICompileService, $rootScope: ng.IRootScopeService, - $timeout: ng.ITimeoutService + $timeout: ng.ITimeoutService, + private platform: Platform ) { this.$compile = $compile; this.$timeout = $timeout; @@ -68,7 +70,8 @@ export class ApplicationGroup { this.$compile, this.$timeout, scope, - this.onApplicationDeinit + this.onApplicationDeinit, + this.platform ); const appState = new AppState( this.$rootScope, diff --git a/webpack.config.js b/webpack.config.js index db41f998d..0574c7650 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -9,7 +9,8 @@ module.exports = { }, plugins: [ new webpack.DefinePlugin({ - __VERSION__: JSON.stringify(require('./package.json').version) + __VERSION__: JSON.stringify(require('./package.json').version), + __PLATFORM_WEB__: JSON.stringify(true), }), new MiniCssExtractPlugin({ // Options similar to the same options in webpackOptions.output