feature: add platform layer

This commit is contained in:
Baptiste Grob
2020-08-12 14:31:40 +02:00
parent d9e204fb66
commit 78031eed24
6 changed files with 132 additions and 87 deletions

View File

@@ -1,6 +1,7 @@
'use strict'; 'use strict';
declare const __VERSION__: string declare const __VERSION__: string;
declare const __PLATFORM_WEB__: boolean;
import angular from 'angular'; import angular from 'angular';
import { configRoutes } from './routes'; import { configRoutes } from './routes';
@@ -50,74 +51,84 @@ import {
import { trusted } from './filters'; import { trusted } from './filters';
import { isDev } from './utils'; import { isDev } from './utils';
import { Platform, WebPlatform } from './services/platform';
angular.module('app', ['ngSanitize']); if (__PLATFORM_WEB__) {
startApplication(new WebPlatform());
// Config } else {
angular (window as any).startApplication = startApplication;
.module('app') }
.config(configRoutes)
.constant('appVersion', __VERSION__); function startApplication(platform: Platform) {
angular.module('app', ['ngSanitize']);
// Controllers
angular // Config
.module('app') angular
.directive('applicationGroupView', () => new ApplicationGroupView()) .module('app')
.directive('applicationView', () => new ApplicationView()) .config(configRoutes)
.directive('editorGroupView', () => new EditorGroupView()) .constant('platform', platform)
.directive('editorView', () => new EditorView()) .constant('appVersion', __VERSION__);
.directive('tagsView', () => new TagsView())
.directive('notesView', () => new NotesView()) // Controllers
.directive('footerView', () => new FooterView()) angular
.module('app')
// Directives - Functional .directive('applicationGroupView', () => new ApplicationGroupView())
angular .directive('applicationView', () => new ApplicationView())
.module('app') .directive('editorGroupView', () => new EditorGroupView())
.directive('snAutofocus', ['$timeout', autofocus]) .directive('editorView', () => new EditorView())
.directive('clickOutside', ['$document', clickOutside]) .directive('tagsView', () => new TagsView())
.directive('delayHide', delayHide) .directive('notesView', () => new NotesView())
.directive('elemReady', elemReady) .directive('footerView', () => new FooterView())
.directive('fileChange', fileChange)
.directive('infiniteScroll', [infiniteScroll]) // Directives - Functional
.directive('lowercase', lowercase) angular
.directive('selectOnFocus', ['$window', selectOnFocus]) .module('app')
.directive('snEnter', snEnter); .directive('snAutofocus', ['$timeout', autofocus])
.directive('clickOutside', ['$document', clickOutside])
// Directives - Views .directive('delayHide', delayHide)
angular .directive('elemReady', elemReady)
.module('app') .directive('fileChange', fileChange)
.directive('accountMenu', () => new AccountMenu()) .directive('infiniteScroll', [infiniteScroll])
.directive('actionsMenu', () => new ActionsMenu()) .directive('lowercase', lowercase)
.directive('challengeModal', () => new ChallengeModal()) .directive('selectOnFocus', ['$window', selectOnFocus])
.directive('componentModal', () => new ComponentModal()) .directive('snEnter', snEnter);
.directive('componentView', () => new ComponentView())
.directive('editorMenu', () => new EditorMenu()) // Directives - Views
.directive('inputModal', () => new InputModal()) angular
.directive('menuRow', () => new MenuRow()) .module('app')
.directive('panelResizer', () => new PanelResizer()) .directive('accountMenu', () => new AccountMenu())
.directive('passwordWizard', () => new PasswordWizard()) .directive('actionsMenu', () => new ActionsMenu())
.directive('permissionsModal', () => new PermissionsModal()) .directive('challengeModal', () => new ChallengeModal())
.directive('privilegesAuthModal', () => new PrivilegesAuthModal()) .directive('componentModal', () => new ComponentModal())
.directive('privilegesManagementModal', () => new PrivilegesManagementModal()) .directive('componentView', () => new ComponentView())
.directive('revisionPreviewModal', () => new RevisionPreviewModal()) .directive('editorMenu', () => new EditorMenu())
.directive('sessionHistoryMenu', () => new SessionHistoryMenu()) .directive('inputModal', () => new InputModal())
.directive('syncResolutionMenu', () => new SyncResolutionMenu()); .directive('menuRow', () => new MenuRow())
.directive('panelResizer', () => new PanelResizer())
// Filters .directive('passwordWizard', () => new PasswordWizard())
angular .directive('permissionsModal', () => new PermissionsModal())
.module('app') .directive('privilegesAuthModal', () => new PrivilegesAuthModal())
.filter('trusted', ['$sce', trusted]); .directive('privilegesManagementModal', () => new PrivilegesManagementModal())
.directive('revisionPreviewModal', () => new RevisionPreviewModal())
// Services .directive('sessionHistoryMenu', () => new SessionHistoryMenu())
angular.module('app').service('mainApplicationGroup', ApplicationGroup); .directive('syncResolutionMenu', () => new SyncResolutionMenu());
// Debug // Filters
if (isDev) { angular
Object.defineProperties(window, { .module('app')
application: { .filter('trusted', ['$sce', trusted]);
get: () =>
(angular.element(document).injector().get('mainApplicationGroup') as any) // Services
.application, angular.module('app').service('mainApplicationGroup', ApplicationGroup);
},
}); // Debug
if (isDev) {
Object.defineProperties(window, {
application: {
get: () =>
(angular.element(document).injector().get('mainApplicationGroup') as any)
.application,
},
});
}
} }

View File

@@ -1,13 +1,16 @@
import { DeviceInterface, getGlobalScope, SNApplication } from 'snjs'; import { DeviceInterface, getGlobalScope, SNApplication } from 'snjs';
import { Database } from '@/database'; import { Database } from '@/database';
import { Platform } from './services/platform';
const KEYCHAIN_STORAGE_KEY = 'keychain';
export class WebDeviceInterface extends DeviceInterface { export class WebDeviceInterface extends DeviceInterface {
private database: Database private database: Database
constructor(namespace: string, timeout: any) { constructor(
namespace: string,
timeout: any,
private platform: Platform
) {
super( super(
namespace, namespace,
timeout || setTimeout.bind(getGlobalScope()), timeout || setTimeout.bind(getGlobalScope()),
@@ -97,19 +100,16 @@ export class WebDeviceInterface extends DeviceInterface {
return this.database.clearAllPayloads(); return this.database.clearAllPayloads();
} }
async getKeychainValue() { getKeychainValue(): Promise<unknown> {
const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY); return this.platform.getKeychainValue();
if (value) {
return JSON.parse(value);
}
} }
async setKeychainValue(value: any) { setKeychainValue(value: any) {
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value)); return this.platform.setKeychainValue(value);
} }
async clearKeychainValue() { clearKeychainValue() {
localStorage.removeItem(KEYCHAIN_STORAGE_KEY); return this.platform.clearKeychainValue();
} }
openUrl(url: string) { openUrl(url: string) {

View File

@@ -0,0 +1,24 @@
/** Platform-specific (i-e desktop/web) behavior is handled by a Platform object. */
export interface Platform {
getKeychainValue(): Promise<unknown>;
setKeychainValue(value: any): Promise<void>;
clearKeychainValue(): Promise<void>;
}
const KEYCHAIN_STORAGE_KEY = 'keychain';
export class WebPlatform implements Platform {
async getKeychainValue(): Promise<unknown> {
const value = localStorage.getItem(KEYCHAIN_STORAGE_KEY);
if (value) {
return JSON.parse(value);
}
}
async setKeychainValue(value: any): Promise<void> {
localStorage.setItem(KEYCHAIN_STORAGE_KEY, JSON.stringify(value));
}
async clearKeychainValue(): Promise<void> {
localStorage.removeItem(KEYCHAIN_STORAGE_KEY);
}
}

View File

@@ -26,6 +26,7 @@ import {
} from '@/services'; } from '@/services';
import { AppState } from '@/ui_models/app_state'; import { AppState } from '@/ui_models/app_state';
import { SNWebCrypto } from 'sncrypto/dist/sncrypto-web'; import { SNWebCrypto } from 'sncrypto/dist/sncrypto-web';
import { Platform } from '@/services/platform';
type WebServices = { type WebServices = {
appState: AppState appState: AppState
@@ -54,10 +55,15 @@ export class WebApplication extends SNApplication {
$compile: ng.ICompileService, $compile: ng.ICompileService,
$timeout: ng.ITimeoutService, $timeout: ng.ITimeoutService,
scope: ng.IScope, scope: ng.IScope,
onDeinit: (app: WebApplication) => void onDeinit: (app: WebApplication) => void,
platform: Platform,
) { ) {
const namespace = ''; const namespace = '';
const deviceInterface = new WebDeviceInterface(namespace, $timeout); const deviceInterface = new WebDeviceInterface(
namespace,
$timeout,
platform
);
super( super(
Environment.Web, Environment.Web,
platformFromString(getPlatformString()), platformFromString(getPlatformString()),

View File

@@ -11,6 +11,7 @@ import {
ThemeManager ThemeManager
} from '@/services'; } from '@/services';
import { AppState } from '@/ui_models/app_state'; import { AppState } from '@/ui_models/app_state';
import { Platform } from '@/services/platform';
type AppManagerChangeCallback = () => void type AppManagerChangeCallback = () => void
@@ -27,7 +28,8 @@ export class ApplicationGroup {
constructor( constructor(
$compile: ng.ICompileService, $compile: ng.ICompileService,
$rootScope: ng.IRootScopeService, $rootScope: ng.IRootScopeService,
$timeout: ng.ITimeoutService $timeout: ng.ITimeoutService,
private platform: Platform
) { ) {
this.$compile = $compile; this.$compile = $compile;
this.$timeout = $timeout; this.$timeout = $timeout;
@@ -68,7 +70,8 @@ export class ApplicationGroup {
this.$compile, this.$compile,
this.$timeout, this.$timeout,
scope, scope,
this.onApplicationDeinit this.onApplicationDeinit,
this.platform
); );
const appState = new AppState( const appState = new AppState(
this.$rootScope, this.$rootScope,

View File

@@ -9,7 +9,8 @@ module.exports = {
}, },
plugins: [ plugins: [
new webpack.DefinePlugin({ new webpack.DefinePlugin({
__VERSION__: JSON.stringify(require('./package.json').version) __VERSION__: JSON.stringify(require('./package.json').version),
__PLATFORM_WEB__: JSON.stringify(true),
}), }),
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output // Options similar to the same options in webpackOptions.output