project initialized

This commit is contained in:
richard-loafle 2020-03-27 17:45:48 +09:00
commit c17f98928c
31 changed files with 10123 additions and 0 deletions

13
.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

46
.gitignore vendored Normal file
View File

@ -0,0 +1,46 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events*.json
speed-measure-plugin*.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"trailingComma": "none",
"tabWidth": 2,
"singleQuote": true
}

11
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,11 @@
{
"recommendations": [
"Angular.ng-template",
"msjsdiag.debugger-for-chrome",
"eamodio.gitlens",
"esbenp.prettier-vscode",
"ms-vscode.vscode-typescript-tslint-plugin",
"VisualStudioExptTeam.vscodeintellicode",
"nkokhelox.svg-font-previewer"
]
}

7
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": []
}

16
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,16 @@
{
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.autoClosingBrackets": "languageDefined",
"editor.trimAutoWhitespace": true,
"files.trimTrailingWhitespace": true,
"files.trimFinalNewlines": true,
"files.watcherExclude": {
"**/dist": true
},
"go.testFlags": ["-v"],
"go.testTimeout": "100s",
"debug.node.autoAttach": "on"
}

15
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,15 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "build:main:dev",
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# MMessengerLg
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.6.
## Development server
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

37
config/webpack.config.js Normal file
View File

@ -0,0 +1,37 @@
const path = require('path');
const webpackNodeExternals = require('webpack-node-externals');
const rootPath = path.join(__dirname, '..');
module.exports = [
{
mode: process.env.ENV || 'development',
entry: path.join(rootPath, 'src', 'main'),
target: 'electron-main',
devtool: 'source-map',
externals: [webpackNodeExternals()],
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [
{
test: /\.ts$/,
use: [
{
loader: 'ts-loader',
options: {
configFile: path.join(rootPath, 'tsconfig.app.json')
}
}
],
exclude: /node_modules/
}
]
},
output: {
path: path.join(rootPath, 'dist'),
filename: 'electron-main.js'
}
}
];

9256
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

55
package.json Normal file
View File

@ -0,0 +1,55 @@
{
"name": "ucap-lg-desktop",
"version": "0.0.0",
"scripts": {
"start": "npm run build && electron --nolazy --inspect-brk=9229 ./dist/electron-main.js",
"build": "webpack --config ./config/webpack.config.js"
},
"private": true,
"dependencies": {
"@tsed/core": "^5.44.11",
"@tsed/di": "^5.44.11",
"@ucap/electron-core": "~0.0.1",
"auto-launch": "^5.0.5",
"electron-log": "^4.1.0",
"electron-store": "^5.1.1",
"electron-updater": "^4.2.5",
"electron-window-state": "^5.0.3",
"fs-extra": "^9.0.0",
"rxjs": "^6.5.4",
"semver": "^7.1.3",
"tmp": "^0.1.0",
"tslib": "^1.10.0",
"v8-compile-cache": "^2.1.0"
},
"devDependencies": {
"@types/node": "^12.11.1",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"cross-env": "^7.0.2",
"devtron": "^1.4.0",
"electron": "^8.1.1",
"electron-builder": "^22.4.1",
"electron-debug": "^3.0.1",
"electron-devtools-installer": "^2.2.4",
"electron-reload": "^1.5.0",
"i18next": "^19.3.3",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.3.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~2.1.0",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.2",
"npm-run-all": "^4.1.5",
"protractor": "~5.4.3",
"ts-loader": "^6.2.1",
"ts-node": "~8.3.0",
"tslint": "~5.18.0",
"typescript": "~3.7.5",
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11",
"webpack-merge": "^4.2.2",
"webpack-node-externals": "^1.7.2"
}
}

51
src/app/app.ts Normal file
View File

@ -0,0 +1,51 @@
import * as Electron from 'electron';
import log from 'electron-log';
import { AppChannel } from '@ucap/electron-core';
import { AppLoader } from '../common/app/app-loader';
import { AppSettings } from '../common/app/decorators/app-settings';
import { On } from '../common/app/decorators/on';
import { AppWindow } from './app.window';
import { BrowserWindowRegistry } from '../common/browser-window/registries/browser-window.registry';
@AppSettings({
bootstrap: AppWindow
})
export class App extends AppLoader {
constructor() {
super();
}
@On(AppChannel.SecondInstance)
onSecondInstance(event: Event, argv: string[], workingDirectory: string) {
log.info('AppChannel.SecondInstance');
}
@On(AppChannel.Ready)
onReady(launchInfo: any) {
log.info('AppChannel.Ready');
this.createMainWindow();
}
@On(AppChannel.Activate)
onActivate(event: Event, hasVisibleWindows: boolean) {
log.info('AppChannel.Activate');
}
@On(AppChannel.WindowAllClosed)
onWindowAllClosed() {
log.info('AppChannel.WindowAllClosed');
}
private createMainWindow() {
const bootstrap = this.settings.get('bootstrap');
const provider = BrowserWindowRegistry.get(bootstrap);
const i = provider.useFactory(this.electronApp);
}
private createTrayIcon() {}
}

106
src/app/app.window.ts Normal file
View File

@ -0,0 +1,106 @@
import * as Electron from 'electron';
import log from 'electron-log';
import * as windowStateKeeper from 'electron-window-state';
import { BrowserWindow } from '../common/browser-window/decorators/browser-window';
import { On } from '../common/browser-window/decorators/on';
import { BrowserWindowChannel, AppChannel } from '@ucap/electron-core';
const MIN_WIDTH = 700;
const MIN_HEIGHT = 600;
const DEFAULT_WIDTH = 1160;
const DEFAULT_HEIGHT = 800;
let savedWindowState: windowStateKeeper.State;
@BrowserWindow({
constructorOptions: {
minWidth: MIN_WIDTH,
minHeight: MIN_HEIGHT,
center: true,
backgroundColor: '#fff',
webPreferences: {
// Disable auxclick event
// See https://developers.google.com/web/updates/2016/10/auxclick
disableBlinkFeatures: 'Auxclick',
// Enable, among other things, the ResizeObserver
experimentalFeatures: true,
nodeIntegration: true
},
acceptFirstMouse: true
},
beforeConstructor: constructorOptions => {
savedWindowState = windowStateKeeper({
defaultWidth: DEFAULT_WIDTH,
defaultHeight: DEFAULT_HEIGHT
});
return {
...constructorOptions,
x: savedWindowState.x,
y: savedWindowState.y,
width: savedWindowState.width,
height: savedWindowState.height
};
}
})
export class AppWindow {
constructor(
private app: Electron.App,
private window: Electron.BrowserWindow
) {
savedWindowState.manage(this.window);
this.attachHandlers();
}
@On(BrowserWindowChannel.ReadyToShow)
onReadyToShow() {
log.info('BrowserWindowChannel.ReadyToShow');
}
@On(BrowserWindowChannel.Close)
onClose(event: Event) {
log.info('BrowserWindowChannel.Close');
}
@On(BrowserWindowChannel.Focus)
onFocus() {
log.info('BrowserWindowChannel.Focus');
}
@On(BrowserWindowChannel.Blur)
onBlur() {
log.info('BrowserWindowChannel.Blur');
}
@On(BrowserWindowChannel.Minimize)
onMinimize() {
log.info('BrowserWindowChannel.Minimize');
}
@On(BrowserWindowChannel.Maximize)
onMaximize() {
log.info('BrowserWindowChannel.Maximize');
}
@On(BrowserWindowChannel.Unmaximize)
onUnmaximize() {
log.info('BrowserWindowChannel.Unmaximize');
}
@On(BrowserWindowChannel.Closed)
onClosed() {
log.info('BrowserWindowChannel.Closed');
}
private attachHandlers() {
this.attachAppHandlers();
}
private attachAppHandlers() {
this.app.on(AppChannel.BeforeQuit, (event: Electron.Event) => {
log.info('AppChannel.BeforeQuit');
});
}
}

View File

@ -0,0 +1,114 @@
import { Type, constructorOf, Store } from '@tsed/core';
import { GlobalProviders, InjectorService, registerProvider } from '@tsed/di';
import * as Electron from 'electron';
import { AppOptions } from './decorators/app-settings';
import { AppSettingsService } from './services/app-settings.service';
import { ELECTRON_APP } from './types/electron-app';
import {
AppProviderMetadata,
AppEventHandlerMetadata
} from './models/app-provider.metadata';
import { createElectronApp } from './utils/electron';
import { ElectronApp } from './decorators/electron-app';
export abstract class AppLoader {
readonly injector: InjectorService;
private startedAt = new Date();
constructor(settings: Partial<AppOptions> = {}) {
// create injector with initial configuration
this.injector = this.createInjector(this.getConfiguration(this, settings));
createElectronApp(this.injector);
this.attachEventHandler();
}
get settings(): AppSettingsService {
return this.injector.settings as AppSettingsService;
}
get electronApp(): Electron.App {
return this.injector.get<ElectronApp>(ElectronApp);
}
static async bootstrap<App extends AppLoader>(
module: Type<App>,
settings: Partial<AppOptions> = {}
): Promise<App> {
const app = new module(settings);
return app;
}
async start(): Promise<any> {
try {
} catch (err) {
return Promise.reject(err);
}
}
private createInjector(settings: Partial<AppOptions> = {}) {
const injector = new InjectorService();
injector.settings = this.createSettingsService(injector);
// injector.logger = $log;
// @ts-ignore
injector.settings.set(settings);
/* istanbul ignore next */
if (injector.settings.env === 'test') {
injector.logger.stop();
}
return injector;
}
private createSettingsService(injector: InjectorService): AppSettingsService {
const provider = GlobalProviders.get(AppSettingsService)!.clone();
provider.instance = injector.invoke<AppSettingsService>(provider.useClass);
injector.addProvider(AppSettingsService, provider);
return provider.instance as any;
}
private getConfiguration(module: any, configuration: any = {}) {
const provider = GlobalProviders.get(constructorOf(module))!;
return { ...provider.configuration, ...configuration };
}
private async invoke(
instance: any,
handlerMetadata: AppEventHandlerMetadata,
args: any[]
): Promise<any> {
const { methodClassName } = handlerMetadata;
return await instance[methodClassName](...args);
}
private attachEventHandler() {
const handlerMetadata: AppProviderMetadata = Store.from(this).get(
ELECTRON_APP
);
if (!handlerMetadata || !handlerMetadata.handlers) {
return;
}
const __this = this;
for (const handler in handlerMetadata.handlers) {
if (handlerMetadata.handlers.hasOwnProperty(handler)) {
const metadata = handlerMetadata.handlers[handler];
Electron.app.on(metadata.channel as any, (...args: any[]) => {
__this.invoke(__this, metadata, args);
});
}
}
}
}

View File

@ -0,0 +1,12 @@
import { Type, Store } from '@tsed/core';
import { IModuleOptions, registerProvider, Module } from '@tsed/di';
export const APP_OPTIONS = Symbol.for('APP_OPTIONS');
export interface AppOptions extends IModuleOptions {
bootstrap?: Type<any>;
}
export function AppSettings(options?: AppOptions): Function {
return Module({ ...options, root: true });
}

View File

@ -0,0 +1,19 @@
import * as Electron from 'electron';
import { Type } from '@tsed/core';
import { Inject } from '@tsed/di';
declare global {
namespace Electron {
interface App {}
}
}
export type ElectronApp = Electron.App;
export function ElectronApp(
target: Type<any>,
targetKey: string,
descriptor: TypedPropertyDescriptor<Function> | number
) {
return Inject(ElectronApp)(target, targetKey, descriptor);
}

View File

@ -0,0 +1,18 @@
import { Store } from '@tsed/core';
import { AppChannel } from '@ucap/electron-core';
import { ELECTRON_APP } from '../types/electron-app';
export function On(channel: AppChannel) {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
Store.from(target).merge(ELECTRON_APP, {
handlers: {
[propertyKey]: {
channel,
methodClassName: propertyKey
}
}
});
};
}

View File

@ -0,0 +1,12 @@
import { AppChannel } from '@ucap/electron-core';
export interface AppEventHandlerMetadata {
channel: AppChannel;
methodClassName: string;
}
export interface AppProviderMetadata {
handlers: {
[propertyKey: string]: AppEventHandlerMetadata;
};
}

View File

@ -0,0 +1,16 @@
import {
DIConfiguration,
Injectable,
ProviderScope,
ProviderType
} from '@tsed/di';
@Injectable({
scope: ProviderScope.SINGLETON,
global: true
})
export class AppSettingsService extends DIConfiguration {
constructor() {
super();
}
}

View File

@ -0,0 +1 @@
export const ELECTRON_APP = Symbol.for('ELECTRON_APP');

View File

@ -0,0 +1,20 @@
import { InjectorService, ProviderScope, registerProvider } from '@tsed/di';
import * as Electron from 'electron';
import { ElectronApp } from '../decorators/electron-app';
export function createElectronApp(injector: InjectorService): void {
injector.forkProvider(ElectronApp);
}
registerProvider({
provide: ElectronApp,
scope: ProviderScope.SINGLETON,
global: true,
useFactory() {
const app = Electron.app;
app.allowRendererProcessReuse = true;
return app;
}
});

View File

@ -0,0 +1,64 @@
import * as Electron from 'electron';
import { registerBrowserWindow } from '../registries/browser-window.registry';
import { Store } from '@tsed/core';
import { ELECTRON_BROWSER_WINDOW } from '../types/electron-browser-window';
import {
BrowserWindowEventHandlerMetadata,
BrowserWindowProviderMetadata
} from '../models/browser-window-provider.metadata';
export interface BrowserWindowOptions {
constructorOptions?: Electron.BrowserWindowConstructorOptions;
beforeConstructor?: (
constructorOptions: Electron.BrowserWindowConstructorOptions
) => Electron.BrowserWindowConstructorOptions;
}
export function BrowserWindow(options?: BrowserWindowOptions): Function {
return (target: any): void => {
registerBrowserWindow({
provide: target,
useFactory(app: Electron.App) {
options = options || {};
let constructorOptions = options.constructorOptions || {};
if (!!options.beforeConstructor) {
constructorOptions = options.beforeConstructor(constructorOptions);
}
const window = new Electron.BrowserWindow(constructorOptions);
const w = new target(app, window);
const providerMetadata: BrowserWindowProviderMetadata = Store.from(
w
).get(ELECTRON_BROWSER_WINDOW);
if (!!providerMetadata && !!providerMetadata.handlers) {
const __this = w;
const invoke = async (
instance: any,
handlerMetadata: BrowserWindowEventHandlerMetadata,
args: any[]
) => {
const { methodClassName } = handlerMetadata;
return await instance[methodClassName](...args);
};
for (const handler in providerMetadata.handlers) {
if (providerMetadata.handlers.hasOwnProperty(handler)) {
const metadata = providerMetadata.handlers[handler];
window.on(metadata.channel as any, (...args: any[]) => {
invoke(__this, metadata, args);
});
}
}
}
return w;
}
});
};
}

View File

@ -0,0 +1,18 @@
import { Store } from '@tsed/core';
import { BrowserWindowChannel } from '@ucap/electron-core';
import { ELECTRON_BROWSER_WINDOW } from '../types/electron-browser-window';
export function On(channel: BrowserWindowChannel) {
return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
Store.from(target).merge(ELECTRON_BROWSER_WINDOW, {
handlers: {
[propertyKey]: {
channel,
methodClassName: propertyKey
}
}
});
};
}

View File

@ -0,0 +1,12 @@
import { BrowserWindowChannel } from '@ucap/electron-core';
export interface BrowserWindowEventHandlerMetadata {
channel: BrowserWindowChannel;
methodClassName: string;
}
export interface BrowserWindowProviderMetadata {
handlers: {
[propertyKey: string]: BrowserWindowEventHandlerMetadata;
};
}

View File

@ -0,0 +1,12 @@
import { Provider, GlobalProviders, TypedProvidersRegistry } from '@tsed/di';
export const PROVIDER_TYPE_BROWSER_WINDOW = 'BrowserWindow';
export const BrowserWindowRegistry: TypedProvidersRegistry = GlobalProviders.createRegistry(
PROVIDER_TYPE_BROWSER_WINDOW,
Provider
);
export const registerBrowserWindow = GlobalProviders.createRegisterFn(
PROVIDER_TYPE_BROWSER_WINDOW
);

View File

@ -0,0 +1 @@
export const ELECTRON_BROWSER_WINDOW = Symbol.for('ELECTRON_BROWSER_WINDOW');

17
src/main.ts Normal file
View File

@ -0,0 +1,17 @@
import log from 'electron-log';
import { AppLoader } from './common/app/app-loader';
import { App } from './app/app';
async function bootstrap() {
try {
log.debug('Start app...');
const app = await AppLoader.bootstrap(App);
await app.start();
log.debug('App initialized');
} catch (er) {
log.error(er);
}
}
bootstrap();

9
tsconfig.app.json Normal file
View File

@ -0,0 +1,9 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"files": ["src/main.ts"],
"include": ["src/**/*.d.ts"]
}

24
tsconfig.json Normal file
View File

@ -0,0 +1,24 @@
{
"compileOnSave": false,
"compilerOptions": {
"baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
"downlevelIteration": true,
"experimentalDecorators": true,
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"target": "es2015",
"lib": ["es2018", "dom"],
"types": ["node"],
"paths": {
"@app/*": ["src/app/*"]
}
},
"angularCompilerOptions": {
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true
}
}

18
tsconfig.spec.json Normal file
View File

@ -0,0 +1,18 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts",
"src/polyfills.ts"
],
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}

91
tslint.json Normal file
View File

@ -0,0 +1,91 @@
{
"extends": "tslint:recommended",
"rules": {
"array-type": false,
"arrow-parens": false,
"deprecation": {
"severity": "warning"
},
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"directive-selector": [
true,
"attribute",
"app",
"camelCase"
],
"component-selector": [
true,
"element",
"app",
"kebab-case"
],
"import-blacklist": [
true,
"rxjs/Rx"
],
"interface-name": false,
"max-classes-per-file": false,
"max-line-length": [
true,
140
],
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true
},
"rulesDirectory": [
"codelyzer"
]
}