sync
This commit is contained in:
parent
e1d820434e
commit
5fade48f5e
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
/dist
|
||||||
|
/pack
|
||||||
/docs
|
/docs
|
||||||
/tmp
|
/tmp
|
||||||
/out-tsc
|
/out-tsc
|
||||||
|
|
6003
package-lock.json
generated
6003
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
39
package.json
39
package.json
|
@ -4,29 +4,44 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean:all": "rimraf dist/*",
|
"clean:all": "rimraf dist/*",
|
||||||
"build:all": "npm-run-all -s build:core build:common build:notify-window build:updater-window",
|
"build:all": "npm-run-all -s build:logger build:core build:common build:notify-window build:updater-window build:native",
|
||||||
"build:core": "node ./scripts/build.js core",
|
"build:core": "node ./scripts/build.js core",
|
||||||
"build:common": "node ./scripts/build.js common",
|
"build:common": "node ./scripts/build.js common",
|
||||||
|
"build:logger": "node ./scripts/build.js logger",
|
||||||
"build:notify-window": "node ./scripts/build.js notify-window",
|
"build:notify-window": "node ./scripts/build.js notify-window",
|
||||||
"build:updater-window": "node ./scripts/build.js updater-window",
|
"build:updater-window": "node ./scripts/build.js updater-window",
|
||||||
"publish:all": "npm-run-all -s publish:core publish:notify-window publish:updater-window",
|
"build:native": "node ./scripts/build.js native",
|
||||||
|
"publish:all": "npm-run-all -s publish:logger publish:core publish:common publish:notify-window publish:updater-window",
|
||||||
"publish:core": "cd ./dist/core && npm publish",
|
"publish:core": "cd ./dist/core && npm publish",
|
||||||
"publish:common": "cd ./dist/common && npm publish",
|
"publish:common": "cd ./dist/common && npm publish",
|
||||||
"publish:notify-window": "cd ./dist/core && npm publish",
|
"publish:logger": "cd ./dist/logger && npm publish",
|
||||||
"publish:updater-window": "cd ./dist/core && npm publish"
|
"publish:notify-window": "cd ./dist/notify-window && npm publish",
|
||||||
|
"publish:updater-window": "cd ./dist/updater-window && npm publish",
|
||||||
|
"publish:native": "cd ./dist/native && npm publish"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsed/core": "^5.44.11",
|
"@types/fs-extra": "^9.0.1",
|
||||||
"@tsed/di": "^5.44.11",
|
|
||||||
"@types/fs-extra": "^8.1.0",
|
|
||||||
"@types/node": "^12.11.1",
|
"@types/node": "^12.11.1",
|
||||||
"@ucap/electron-core": "file:dist/core/ucap-electron-core-0.0.1.tgz",
|
"@ucap/core": "~0.0.15",
|
||||||
"concurrently": "^5.1.0",
|
"@ucap/domain-common": "~0.0.1",
|
||||||
"electron": "^8.1.1",
|
"@ucap/domain-organization": "~0.0.1",
|
||||||
"electron-log": "^4.1.0",
|
"@ucap/domain-status": "~0.0.1",
|
||||||
"fs-extra": "^8.1.0",
|
"@ucap/electron-common": "file:pack/ucap-electron-common-0.0.32.tgz",
|
||||||
|
"@ucap/electron-core": "file:pack/ucap-electron-core-0.0.16.tgz",
|
||||||
|
"@ucap/electron-logger": "file:pack/ucap-electron-logger-0.0.1.tgz",
|
||||||
|
"@ucap/electron-native": "file:pack/ucap-electron-native-0.0.19.tgz",
|
||||||
|
"@ucap/electron-notify-window": "file:pack/ucap-electron-notify-window-0.0.13.tgz",
|
||||||
|
"@ucap/logger": "~0.0.14",
|
||||||
|
"@ucap/native": "~0.0.27",
|
||||||
|
"axios": "^0.19.2",
|
||||||
|
"concurrently": "^5.2.0",
|
||||||
|
"electron": "^9.0.3",
|
||||||
|
"electron-log": "^4.2.1",
|
||||||
|
"file-type": "^14.1.4",
|
||||||
|
"fs-extra": "^9.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "^6.5.4",
|
"rxjs": "^6.5.4",
|
||||||
"terser-webpack-plugin": "^2.3.5",
|
"terser-webpack-plugin": "^2.3.5",
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/electron-common",
|
"name": "@ucap/electron-common",
|
||||||
"version": "0.0.1",
|
"version": "0.0.32",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {
|
"peerDependencies": {
|
||||||
"@tsed/core": "^5.44.11",
|
"electron": "^9.0.3",
|
||||||
"@tsed/di": "^5.44.11",
|
|
||||||
"electron": "^8.0.0",
|
|
||||||
"rxjs": "^6.5.4"
|
"rxjs": "^6.5.4"
|
||||||
},
|
}
|
||||||
"devDependencies": {}
|
|
||||||
}
|
}
|
||||||
|
|
86
projects/common/src/lib/api/app/attacher.ts
Normal file
86
projects/common/src/lib/api/app/attacher.ts
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
import { EventHandlerMetadata } from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
export abstract class Attacher {
|
||||||
|
private static readonly eventHandlerMap: Map<
|
||||||
|
any,
|
||||||
|
{ channel: string; listener: (...args: any[]) => void }[]
|
||||||
|
> = new Map();
|
||||||
|
|
||||||
|
static attach(instance: any) {
|
||||||
|
const metadatas = EventHandlerRegistry.getEventHandlers(
|
||||||
|
ObjectUtil.classOf(instance)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!!metadatas) {
|
||||||
|
metadatas.reverse().forEach((metadata) => {
|
||||||
|
metadata.eventHandlers.forEach((channels, attachType) => {
|
||||||
|
switch (attachType) {
|
||||||
|
case EventAttachType.on:
|
||||||
|
{
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
const listener = (...args: any[]) => {
|
||||||
|
Attacher.invoke(metadata, instance, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
Electron.app.on(channel as any, listener);
|
||||||
|
Attacher.addEventHandler(instance, channel, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettach(instance: any) {
|
||||||
|
Attacher.removeEventHandler(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettachAll() {
|
||||||
|
Electron.app.removeAllListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
static invoke(
|
||||||
|
metadata: EventHandlerMetadata,
|
||||||
|
instance: any,
|
||||||
|
args: any[]
|
||||||
|
): any {
|
||||||
|
if (!!metadata.parent) {
|
||||||
|
return Attacher.invoke(metadata.parent, instance, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: ban-types
|
||||||
|
return (metadata.target.prototype[metadata.propertyKey] as Function).call(
|
||||||
|
instance,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static addEventHandler(instance: any, channel: any, listener: any) {
|
||||||
|
let listeners = Attacher.eventHandlerMap.get(instance);
|
||||||
|
if (!listeners) {
|
||||||
|
listeners = [];
|
||||||
|
Attacher.eventHandlerMap.set(instance, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.push({ channel, listener });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static removeEventHandler(instance: any) {
|
||||||
|
const listeners = Attacher.eventHandlerMap.get(instance);
|
||||||
|
if (!listeners) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const listener of listeners) {
|
||||||
|
Electron.app.removeListener(listener.channel as any, listener.listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
projects/common/src/lib/api/app/builder.ts
Normal file
15
projects/common/src/lib/api/app/builder.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { Type } from '@ucap/core';
|
||||||
|
import { AppConfiguration } from './models/app-configuration';
|
||||||
|
|
||||||
|
export abstract class Builder {
|
||||||
|
static build<T>(
|
||||||
|
appType: Type<T>,
|
||||||
|
configuration: Partial<AppConfiguration>
|
||||||
|
): T {
|
||||||
|
const app = new appType(configuration);
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
30
projects/common/src/lib/api/app/decorators/app-settings.ts
Normal file
30
projects/common/src/lib/api/app/decorators/app-settings.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { Type, DecoratorUtil, Store } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AppConfiguration } from '../models/app-configuration';
|
||||||
|
|
||||||
|
export const STORE_KEY_APP_SETTINGS = '@ucap::electron::app::app-settings';
|
||||||
|
|
||||||
|
export function AppSettings(
|
||||||
|
configuration: Partial<AppConfiguration> = {}
|
||||||
|
// tslint:disable-next-line: ban-types
|
||||||
|
): Function {
|
||||||
|
return <T>(target: Type<any>): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target])) {
|
||||||
|
case 'class':
|
||||||
|
Store.setBy(STORE_KEY_APP_SETTINGS, configuration)(target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAppSettings(
|
||||||
|
target: Type<any>,
|
||||||
|
configuration: Partial<AppConfiguration> = {}
|
||||||
|
): AppConfiguration {
|
||||||
|
const store = Store.from(target).get(STORE_KEY_APP_SETTINGS) || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...store,
|
||||||
|
...configuration
|
||||||
|
};
|
||||||
|
}
|
26
projects/common/src/lib/api/app/decorators/on.ts
Normal file
26
projects/common/src/lib/api/app/decorators/on.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { DecoratorUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AppChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from '../registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export function On(channel: AppChannel) {
|
||||||
|
return <T>(
|
||||||
|
target: any,
|
||||||
|
targetKey?: string,
|
||||||
|
descriptor?: TypedPropertyDescriptor<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target, targetKey, descriptor])) {
|
||||||
|
case 'method':
|
||||||
|
EventHandlerRegistry.use(
|
||||||
|
target,
|
||||||
|
targetKey!,
|
||||||
|
EventAttachType.on,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
28
projects/common/src/lib/api/app/index.ts
Normal file
28
projects/common/src/lib/api/app/index.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { AppSettings, getAppSettings } from './decorators/app-settings';
|
||||||
|
import { On } from './decorators/on';
|
||||||
|
|
||||||
|
import { AppConfiguration } from './models/app-configuration';
|
||||||
|
import {
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata
|
||||||
|
} from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
|
||||||
|
import { Attacher } from './attacher';
|
||||||
|
import { Builder } from './builder';
|
||||||
|
|
||||||
|
export {
|
||||||
|
AppSettings,
|
||||||
|
getAppSettings,
|
||||||
|
On,
|
||||||
|
AppConfiguration,
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata,
|
||||||
|
EventHandlerRegistry,
|
||||||
|
EventAttachType,
|
||||||
|
Attacher,
|
||||||
|
Builder
|
||||||
|
};
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { Type } from '@ucap/core';
|
||||||
|
|
||||||
|
export interface AppConfiguration {
|
||||||
|
rootDir?: string;
|
||||||
|
bootstrap?: Type<any>;
|
||||||
|
services?: Type<any>[];
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Type, Metadata, ReflectUtil, ObjectUtil, Store } from '@ucap/core';
|
||||||
|
import { AppChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export interface EventHandlerConstructorOptions {
|
||||||
|
target: Type<any>;
|
||||||
|
propertyKey: string | symbol;
|
||||||
|
eventHandlers?: Map<EventAttachType, AppChannel[]>;
|
||||||
|
type?: any;
|
||||||
|
parent?: EventHandlerMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventHandlerMetadata extends Metadata
|
||||||
|
implements EventHandlerConstructorOptions {
|
||||||
|
eventHandlers: Map<EventAttachType, AppChannel[]> = new Map();
|
||||||
|
|
||||||
|
readonly parent: EventHandlerMetadata | undefined;
|
||||||
|
|
||||||
|
constructor(options: EventHandlerConstructorOptions) {
|
||||||
|
super(
|
||||||
|
options.target,
|
||||||
|
options.propertyKey,
|
||||||
|
Object.getOwnPropertyDescriptor(options.target, options.propertyKey)
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
target,
|
||||||
|
parent,
|
||||||
|
propertyKey,
|
||||||
|
eventHandlers = new Map(),
|
||||||
|
type
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
this._type = ReflectUtil.getReturnType(target, propertyKey);
|
||||||
|
|
||||||
|
this.eventHandlers = eventHandlers;
|
||||||
|
this.type = type;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): Type<any> {
|
||||||
|
return ObjectUtil.isPromise(this._type) ||
|
||||||
|
ObjectUtil.isArrayOrArrayClass(this._type) ||
|
||||||
|
this._type === Object
|
||||||
|
? undefined!
|
||||||
|
: this._type;
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(type: Type<any>) {
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key: any) {
|
||||||
|
const ctrlValue = Store.from(this.target).get(key);
|
||||||
|
let meta = ObjectUtil.deepExtends(undefined, ctrlValue);
|
||||||
|
const endpointValue = this.store.get(key);
|
||||||
|
if (endpointValue !== undefined) {
|
||||||
|
meta = ObjectUtil.deepExtends(meta, endpointValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(attachType: EventAttachType, channel: AppChannel): this {
|
||||||
|
if (!this.eventHandlers.has(attachType)) {
|
||||||
|
this.eventHandlers.set(attachType, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventHandlers.get(attachType).push(channel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { Type, ReflectUtil, ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AppChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventHandlerMetadata } from '../models/event-handler.metadata';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export const REFLECT_KEY_EVENTHANDLERS = 'ucap_electron_app_event_handlers';
|
||||||
|
|
||||||
|
export class EventHandlerRegistry {
|
||||||
|
static getOwnEventHandlers(target: Type<any>): EventHandlerMetadata[] {
|
||||||
|
if (!this.hasEventHandlers(target)) {
|
||||||
|
ReflectUtil.set(REFLECT_KEY_EVENTHANDLERS, [], target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getEventHandlers(target: Type<any>): EventHandlerMetadata[] {
|
||||||
|
return this.getOwnEventHandlers(target).concat(this.inherit(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
static hasEventHandlers(target: Type<any>): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(
|
||||||
|
target: Type<any>,
|
||||||
|
propertyKey: string | symbol
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
if (!this.has(target, propertyKey)) {
|
||||||
|
const evnetHandler = new EventHandlerMetadata({
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
});
|
||||||
|
this.getOwnEventHandlers(target).push(evnetHandler);
|
||||||
|
ReflectUtil.set(
|
||||||
|
REFLECT_KEY_EVENTHANDLERS,
|
||||||
|
evnetHandler,
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target, propertyKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static has(target: Type<any>, method: string | symbol): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
static use(
|
||||||
|
target: Type<any>,
|
||||||
|
targetKey: string | symbol,
|
||||||
|
attachType: EventAttachType,
|
||||||
|
channel: AppChannel
|
||||||
|
) {
|
||||||
|
this.get(target, targetKey).merge(attachType, channel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static inherit(clazz: Type<any>) {
|
||||||
|
const eventHandlers: EventHandlerMetadata[] = [];
|
||||||
|
let inheritedClass = ObjectUtil.getInheritedClass(clazz);
|
||||||
|
|
||||||
|
while (
|
||||||
|
inheritedClass &&
|
||||||
|
EventHandlerRegistry.hasEventHandlers(inheritedClass)
|
||||||
|
) {
|
||||||
|
this.getOwnEventHandlers(inheritedClass).forEach(
|
||||||
|
(eventHandler: EventHandlerMetadata) => {
|
||||||
|
eventHandlers.push(inheritEndpoint(clazz, eventHandler));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
inheritedClass = ObjectUtil.getInheritedClass(inheritedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventHandlers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inheritEndpoint(
|
||||||
|
target: Type<any>,
|
||||||
|
eventHandler: EventHandlerMetadata
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
return new EventHandlerMetadata({
|
||||||
|
...eventHandler,
|
||||||
|
target,
|
||||||
|
type: eventHandler.type,
|
||||||
|
parent: eventHandler
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export enum EventAttachType {
|
||||||
|
on = 'on'
|
||||||
|
}
|
89
projects/common/src/lib/api/auto-updater/attacher.ts
Normal file
89
projects/common/src/lib/api/auto-updater/attacher.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
import { EventHandlerMetadata } from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
export abstract class Attacher {
|
||||||
|
private static readonly eventHandlerMap: Map<
|
||||||
|
any,
|
||||||
|
{ channel: string; listener: (...args: any[]) => void }[]
|
||||||
|
> = new Map();
|
||||||
|
|
||||||
|
static attach(instance: any) {
|
||||||
|
const metadatas = EventHandlerRegistry.getEventHandlers(
|
||||||
|
ObjectUtil.classOf(instance)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!!metadatas) {
|
||||||
|
metadatas.reverse().forEach((metadata) => {
|
||||||
|
metadata.eventHandlers.forEach((channels, attachType) => {
|
||||||
|
switch (attachType) {
|
||||||
|
case EventAttachType.on:
|
||||||
|
{
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
const listener = (...args: any[]) => {
|
||||||
|
Attacher.invoke(metadata, instance, args);
|
||||||
|
};
|
||||||
|
|
||||||
|
Electron.autoUpdater.on(channel as any, listener);
|
||||||
|
Attacher.addEventHandler(instance, channel, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettach(instance: any) {
|
||||||
|
Attacher.removeEventHandler(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettachAll() {
|
||||||
|
Electron.autoUpdater.removeAllListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
static invoke(
|
||||||
|
metadata: EventHandlerMetadata,
|
||||||
|
instance: any,
|
||||||
|
args: any[]
|
||||||
|
): any {
|
||||||
|
if (!!metadata.parent) {
|
||||||
|
return Attacher.invoke(metadata.parent, instance, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: ban-types
|
||||||
|
return (metadata.target.prototype[metadata.propertyKey] as Function).call(
|
||||||
|
instance,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static addEventHandler(instance: any, channel: any, listener: any) {
|
||||||
|
let listeners = Attacher.eventHandlerMap.get(instance);
|
||||||
|
if (!listeners) {
|
||||||
|
listeners = [];
|
||||||
|
Attacher.eventHandlerMap.set(instance, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.push({ channel, listener });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static removeEventHandler(instance: any) {
|
||||||
|
const listeners = Attacher.eventHandlerMap.get(instance);
|
||||||
|
if (!listeners) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const listener of listeners) {
|
||||||
|
Electron.autoUpdater.removeListener(
|
||||||
|
listener.channel as any,
|
||||||
|
listener.listener
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
projects/common/src/lib/api/auto-updater/builder.ts
Normal file
16
projects/common/src/lib/api/auto-updater/builder.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { Type } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AutoUpdaterConfiguration } from './models/auto-updater-configuration';
|
||||||
|
|
||||||
|
export abstract class Builder {
|
||||||
|
static build<T>(
|
||||||
|
autoUpdaterType: Type<T>,
|
||||||
|
configuration: Partial<AutoUpdaterConfiguration>
|
||||||
|
): T {
|
||||||
|
const autoUpdater = new autoUpdaterType(configuration);
|
||||||
|
|
||||||
|
return autoUpdater;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { Type, DecoratorUtil, Store } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AutoUpdaterConfiguration } from '../models/auto-updater-configuration';
|
||||||
|
|
||||||
|
export const STORE_KEY_AUTO_UPDATER_SETTINGS =
|
||||||
|
'@ucap::electron::auto-updater::auto-updater-settings';
|
||||||
|
|
||||||
|
export function AutoUpdaterSettings(
|
||||||
|
configuration: Partial<AutoUpdaterConfiguration> = {}
|
||||||
|
// tslint:disable-next-line: ban-types
|
||||||
|
): Function {
|
||||||
|
return <T>(target: Type<any>): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target])) {
|
||||||
|
case 'class':
|
||||||
|
Store.setBy(STORE_KEY_AUTO_UPDATER_SETTINGS, configuration)(target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAppSettings(
|
||||||
|
target: Type<any>,
|
||||||
|
configuration: Partial<AutoUpdaterConfiguration> = {}
|
||||||
|
): AutoUpdaterConfiguration {
|
||||||
|
const store = Store.from(target).get(STORE_KEY_AUTO_UPDATER_SETTINGS) || {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...store,
|
||||||
|
...configuration
|
||||||
|
};
|
||||||
|
}
|
26
projects/common/src/lib/api/auto-updater/decorators/on.ts
Normal file
26
projects/common/src/lib/api/auto-updater/decorators/on.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { DecoratorUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AutoUpdaterChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from '../registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export function On(channel: AutoUpdaterChannel) {
|
||||||
|
return <T>(
|
||||||
|
target: any,
|
||||||
|
targetKey?: string,
|
||||||
|
descriptor?: TypedPropertyDescriptor<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target, targetKey, descriptor])) {
|
||||||
|
case 'method':
|
||||||
|
EventHandlerRegistry.use(
|
||||||
|
target,
|
||||||
|
targetKey!,
|
||||||
|
EventAttachType.on,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
31
projects/common/src/lib/api/auto-updater/index.ts
Normal file
31
projects/common/src/lib/api/auto-updater/index.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import {
|
||||||
|
AutoUpdaterSettings,
|
||||||
|
getAppSettings
|
||||||
|
} from './decorators/auto-updater-settings';
|
||||||
|
import { On } from './decorators/on';
|
||||||
|
|
||||||
|
import { AutoUpdaterConfiguration } from './models/auto-updater-configuration';
|
||||||
|
import {
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata
|
||||||
|
} from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
|
||||||
|
import { Attacher } from './attacher';
|
||||||
|
import { Builder } from './builder';
|
||||||
|
|
||||||
|
export {
|
||||||
|
AutoUpdaterSettings,
|
||||||
|
getAppSettings,
|
||||||
|
On,
|
||||||
|
AutoUpdaterConfiguration,
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata,
|
||||||
|
EventHandlerRegistry,
|
||||||
|
EventAttachType,
|
||||||
|
Attacher,
|
||||||
|
Builder
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
export interface AutoUpdaterConfiguration {
|
||||||
|
rootDir?: string;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { Type, Metadata, ReflectUtil, ObjectUtil, Store } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AutoUpdaterChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export interface EventHandlerConstructorOptions {
|
||||||
|
target: Type<any>;
|
||||||
|
propertyKey: string | symbol;
|
||||||
|
eventHandlers?: Map<EventAttachType, AutoUpdaterChannel[]>;
|
||||||
|
type?: any;
|
||||||
|
parent?: EventHandlerMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventHandlerMetadata extends Metadata
|
||||||
|
implements EventHandlerConstructorOptions {
|
||||||
|
eventHandlers: Map<EventAttachType, AutoUpdaterChannel[]> = new Map();
|
||||||
|
|
||||||
|
readonly parent: EventHandlerMetadata | undefined;
|
||||||
|
|
||||||
|
constructor(options: EventHandlerConstructorOptions) {
|
||||||
|
super(
|
||||||
|
options.target,
|
||||||
|
options.propertyKey,
|
||||||
|
Object.getOwnPropertyDescriptor(options.target, options.propertyKey)
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
target,
|
||||||
|
parent,
|
||||||
|
propertyKey,
|
||||||
|
eventHandlers = new Map(),
|
||||||
|
type
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
this._type = ReflectUtil.getReturnType(target, propertyKey);
|
||||||
|
|
||||||
|
this.eventHandlers = eventHandlers;
|
||||||
|
this.type = type;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): Type<any> {
|
||||||
|
return ObjectUtil.isPromise(this._type) ||
|
||||||
|
ObjectUtil.isArrayOrArrayClass(this._type) ||
|
||||||
|
this._type === Object
|
||||||
|
? undefined!
|
||||||
|
: this._type;
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(type: Type<any>) {
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key: any) {
|
||||||
|
const ctrlValue = Store.from(this.target).get(key);
|
||||||
|
let meta = ObjectUtil.deepExtends(undefined, ctrlValue);
|
||||||
|
const endpointValue = this.store.get(key);
|
||||||
|
if (endpointValue !== undefined) {
|
||||||
|
meta = ObjectUtil.deepExtends(meta, endpointValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(attachType: EventAttachType, channel: AutoUpdaterChannel): this {
|
||||||
|
if (!this.eventHandlers.has(attachType)) {
|
||||||
|
this.eventHandlers.set(attachType, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventHandlers.get(attachType).push(channel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
import { Type, ReflectUtil, ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { AutoUpdaterChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventHandlerMetadata } from '../models/event-handler.metadata';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export const REFLECT_KEY_EVENTHANDLERS =
|
||||||
|
'ucap_electron_auto_updater_event_handlers';
|
||||||
|
|
||||||
|
export class EventHandlerRegistry {
|
||||||
|
static getOwnEventHandlers(target: Type<any>) {
|
||||||
|
if (!this.hasEventHandlers(target)) {
|
||||||
|
ReflectUtil.set(REFLECT_KEY_EVENTHANDLERS, [], target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getEventHandlers(target: Type<any>): EventHandlerMetadata[] {
|
||||||
|
return this.getOwnEventHandlers(target).concat(this.inherit(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
static hasEventHandlers(target: Type<any>): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(
|
||||||
|
target: Type<any>,
|
||||||
|
propertyKey: string | symbol
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
if (!this.has(target, propertyKey)) {
|
||||||
|
const evnetHandler = new EventHandlerMetadata({
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
});
|
||||||
|
this.getOwnEventHandlers(target).push(evnetHandler);
|
||||||
|
ReflectUtil.set(
|
||||||
|
REFLECT_KEY_EVENTHANDLERS,
|
||||||
|
evnetHandler,
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target, propertyKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static has(target: Type<any>, method: string | symbol): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
static use(
|
||||||
|
target: Type<any>,
|
||||||
|
targetKey: string | symbol,
|
||||||
|
attachType: EventAttachType,
|
||||||
|
channel: AutoUpdaterChannel
|
||||||
|
) {
|
||||||
|
this.get(target, targetKey).merge(attachType, channel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static inherit(clazz: Type<any>) {
|
||||||
|
const eventHandlers: EventHandlerMetadata[] = [];
|
||||||
|
let inheritedClass = ObjectUtil.getInheritedClass(clazz);
|
||||||
|
|
||||||
|
while (
|
||||||
|
inheritedClass &&
|
||||||
|
EventHandlerRegistry.hasEventHandlers(inheritedClass)
|
||||||
|
) {
|
||||||
|
this.getOwnEventHandlers(inheritedClass).forEach(
|
||||||
|
(eventHandler: EventHandlerMetadata) => {
|
||||||
|
eventHandlers.push(inheritEndpoint(clazz, eventHandler));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
inheritedClass = ObjectUtil.getInheritedClass(inheritedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventHandlers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inheritEndpoint(
|
||||||
|
target: Type<any>,
|
||||||
|
eventHandler: EventHandlerMetadata
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
return new EventHandlerMetadata({
|
||||||
|
...eventHandler,
|
||||||
|
target,
|
||||||
|
type: eventHandler.type,
|
||||||
|
parent: eventHandler
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export enum EventAttachType {
|
||||||
|
on = 'on'
|
||||||
|
}
|
55
projects/common/src/lib/api/browser-window/attacher.ts
Normal file
55
projects/common/src/lib/api/browser-window/attacher.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
import { EventHandlerMetadata } from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
import { ElectronBrowserWindow } from './electron-browser-window';
|
||||||
|
|
||||||
|
export abstract class Attacher {
|
||||||
|
static attach<T extends ElectronBrowserWindow>(instance: T) {
|
||||||
|
const metadatas = EventHandlerRegistry.getEventHandlers(
|
||||||
|
ObjectUtil.classOf(instance)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!!metadatas) {
|
||||||
|
metadatas.reverse().forEach((metadata) => {
|
||||||
|
metadata.eventHandlers.forEach((channels, attachType) => {
|
||||||
|
switch (attachType) {
|
||||||
|
case EventAttachType.on:
|
||||||
|
{
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
instance.native.on(channel as any, (...args: any[]) => {
|
||||||
|
Attacher.invoke(metadata, instance, args);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettach<T extends ElectronBrowserWindow>(instance: T) {
|
||||||
|
instance.native.removeAllListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
static invoke(
|
||||||
|
metadata: EventHandlerMetadata,
|
||||||
|
instance: any,
|
||||||
|
args: any[]
|
||||||
|
): any {
|
||||||
|
if (!!metadata.parent) {
|
||||||
|
return Attacher.invoke(metadata.parent, instance, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: ban-types
|
||||||
|
return (metadata.target.prototype[metadata.propertyKey] as Function).call(
|
||||||
|
instance,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
36
projects/common/src/lib/api/browser-window/builder.ts
Normal file
36
projects/common/src/lib/api/browser-window/builder.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { Type, ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { getBrowserWindowSettings } from './decorators/browser-window-configuration';
|
||||||
|
import { AppConfiguration } from '../app';
|
||||||
|
import { Attacher } from './attacher';
|
||||||
|
import { ElectronBrowserWindow } from './electron-browser-window';
|
||||||
|
|
||||||
|
export abstract class Builder {
|
||||||
|
static build<T extends ElectronBrowserWindow>(
|
||||||
|
configuration: Partial<AppConfiguration>,
|
||||||
|
browserWindowType: Type<T>
|
||||||
|
): T {
|
||||||
|
const _configuration = getBrowserWindowSettings(
|
||||||
|
ObjectUtil.classOf(browserWindowType)
|
||||||
|
);
|
||||||
|
|
||||||
|
const constructorOptions = ObjectUtil.isFunction(
|
||||||
|
_configuration.constructorOptions
|
||||||
|
)
|
||||||
|
? _configuration.constructorOptions(configuration)
|
||||||
|
: _configuration.constructorOptions;
|
||||||
|
|
||||||
|
const _browserWindow = new Electron.BrowserWindow(constructorOptions);
|
||||||
|
const browserWindow = new browserWindowType(_configuration, _browserWindow);
|
||||||
|
|
||||||
|
Attacher.attach(browserWindow);
|
||||||
|
|
||||||
|
return browserWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
static destroy<T extends ElectronBrowserWindow>(instance: T) {
|
||||||
|
Attacher.dettach(instance);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
import * as Elctron from 'electron';
|
||||||
|
|
||||||
|
import { Type, DecoratorUtil, Store } from '@ucap/core';
|
||||||
|
|
||||||
|
import { BrowserWindowConfiguration } from '../models/browser-window-configuration';
|
||||||
|
|
||||||
|
export const STORE_KEY_BROWSER_WINDOW_SETTINGS =
|
||||||
|
'@ucap::electron::browser-window::browser-window-settings';
|
||||||
|
|
||||||
|
export function BrowserWindowSettings(
|
||||||
|
configuration: Partial<BrowserWindowConfiguration> = {}
|
||||||
|
// tslint:disable-next-line: ban-types
|
||||||
|
): Function {
|
||||||
|
return <T extends Elctron.BrowserWindow>(
|
||||||
|
target: Type<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target])) {
|
||||||
|
case 'class':
|
||||||
|
Store.setBy(STORE_KEY_BROWSER_WINDOW_SETTINGS, configuration)(target);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBrowserWindowSettings<T extends Elctron.BrowserWindow>(
|
||||||
|
target: Type<T>
|
||||||
|
): BrowserWindowConfiguration {
|
||||||
|
return Store.from(target).get(STORE_KEY_BROWSER_WINDOW_SETTINGS) || {};
|
||||||
|
}
|
26
projects/common/src/lib/api/browser-window/decorators/on.ts
Normal file
26
projects/common/src/lib/api/browser-window/decorators/on.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { DecoratorUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { BrowserWindowChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from '../registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export function On(channel: BrowserWindowChannel) {
|
||||||
|
return <T>(
|
||||||
|
target: any,
|
||||||
|
targetKey?: string,
|
||||||
|
descriptor?: TypedPropertyDescriptor<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target, targetKey, descriptor])) {
|
||||||
|
case 'method':
|
||||||
|
EventHandlerRegistry.use(
|
||||||
|
target,
|
||||||
|
targetKey!,
|
||||||
|
EventAttachType.on,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
export interface ElectronBrowserWindow {
|
||||||
|
native: Electron.BrowserWindow;
|
||||||
|
}
|
34
projects/common/src/lib/api/browser-window/index.ts
Normal file
34
projects/common/src/lib/api/browser-window/index.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import {
|
||||||
|
BrowserWindowSettings,
|
||||||
|
getBrowserWindowSettings
|
||||||
|
} from './decorators/browser-window-configuration';
|
||||||
|
import { On } from './decorators/on';
|
||||||
|
|
||||||
|
import { BrowserWindowConfiguration } from './models/browser-window-configuration';
|
||||||
|
import {
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata
|
||||||
|
} from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
|
||||||
|
import { Attacher } from './attacher';
|
||||||
|
import { Builder } from './builder';
|
||||||
|
|
||||||
|
import { ElectronBrowserWindow } from './electron-browser-window';
|
||||||
|
|
||||||
|
export {
|
||||||
|
BrowserWindowSettings,
|
||||||
|
getBrowserWindowSettings,
|
||||||
|
On,
|
||||||
|
BrowserWindowConfiguration,
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata,
|
||||||
|
EventHandlerRegistry,
|
||||||
|
EventAttachType,
|
||||||
|
Attacher,
|
||||||
|
Builder,
|
||||||
|
ElectronBrowserWindow
|
||||||
|
};
|
|
@ -0,0 +1,12 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
import { AppConfiguration } from '../../app';
|
||||||
|
|
||||||
|
export type BrowserWindowConstructorOptionsFunc = (
|
||||||
|
appConfiguration: AppConfiguration
|
||||||
|
) => Electron.BrowserWindowConstructorOptions;
|
||||||
|
|
||||||
|
export interface BrowserWindowConfiguration {
|
||||||
|
constructorOptions?:
|
||||||
|
| Electron.BrowserWindowConstructorOptions
|
||||||
|
| BrowserWindowConstructorOptionsFunc;
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { Type, Metadata, ReflectUtil, ObjectUtil, Store } from '@ucap/core';
|
||||||
|
|
||||||
|
import { BrowserWindowChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export interface EventHandlerConstructorOptions {
|
||||||
|
target: Type<any>;
|
||||||
|
propertyKey: string | symbol;
|
||||||
|
eventHandlers?: Map<EventAttachType, BrowserWindowChannel[]>;
|
||||||
|
type?: any;
|
||||||
|
parent?: EventHandlerMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventHandlerMetadata extends Metadata
|
||||||
|
implements EventHandlerConstructorOptions {
|
||||||
|
eventHandlers: Map<EventAttachType, BrowserWindowChannel[]> = new Map();
|
||||||
|
|
||||||
|
readonly parent: EventHandlerMetadata | undefined;
|
||||||
|
|
||||||
|
constructor(options: EventHandlerConstructorOptions) {
|
||||||
|
super(
|
||||||
|
options.target,
|
||||||
|
options.propertyKey,
|
||||||
|
Object.getOwnPropertyDescriptor(options.target, options.propertyKey)
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
target,
|
||||||
|
parent,
|
||||||
|
propertyKey,
|
||||||
|
eventHandlers = new Map(),
|
||||||
|
type
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
this._type = ReflectUtil.getReturnType(target, propertyKey);
|
||||||
|
|
||||||
|
this.eventHandlers = eventHandlers;
|
||||||
|
this.type = type;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): Type<any> {
|
||||||
|
return ObjectUtil.isPromise(this._type) ||
|
||||||
|
ObjectUtil.isArrayOrArrayClass(this._type) ||
|
||||||
|
this._type === Object
|
||||||
|
? undefined!
|
||||||
|
: this._type;
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(type: Type<any>) {
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key: any) {
|
||||||
|
const ctrlValue = Store.from(this.target).get(key);
|
||||||
|
let meta = ObjectUtil.deepExtends(undefined, ctrlValue);
|
||||||
|
const endpointValue = this.store.get(key);
|
||||||
|
if (endpointValue !== undefined) {
|
||||||
|
meta = ObjectUtil.deepExtends(meta, endpointValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(attachType: EventAttachType, channel: BrowserWindowChannel): this {
|
||||||
|
if (!this.eventHandlers.has(attachType)) {
|
||||||
|
this.eventHandlers.set(attachType, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventHandlers.get(attachType).push(channel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { Type, ReflectUtil, ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { BrowserWindowChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { EventHandlerMetadata } from '../models/event-handler.metadata';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export const REFLECT_KEY_EVENTHANDLERS =
|
||||||
|
'ucap_electron_browser_window_event_handlers';
|
||||||
|
|
||||||
|
export class EventHandlerRegistry {
|
||||||
|
static getOwnEventHandlers(target: Type<any>) {
|
||||||
|
if (!EventHandlerRegistry.hasEventHandlers(target)) {
|
||||||
|
ReflectUtil.set(REFLECT_KEY_EVENTHANDLERS, [], target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getEventHandlers(target: Type<any>): EventHandlerMetadata[] {
|
||||||
|
return EventHandlerRegistry.getOwnEventHandlers(target).concat(
|
||||||
|
EventHandlerRegistry.inherit(target)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static hasEventHandlers(target: Type<any>): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(
|
||||||
|
target: Type<any>,
|
||||||
|
propertyKey: string | symbol
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
if (!EventHandlerRegistry.has(target, propertyKey)) {
|
||||||
|
const evnetHandler = new EventHandlerMetadata({
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
});
|
||||||
|
EventHandlerRegistry.getOwnEventHandlers(target).push(evnetHandler);
|
||||||
|
ReflectUtil.set(
|
||||||
|
REFLECT_KEY_EVENTHANDLERS,
|
||||||
|
evnetHandler,
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target, propertyKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static has(target: Type<any>, method: string | symbol): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
static use(
|
||||||
|
target: Type<any>,
|
||||||
|
targetKey: string | symbol,
|
||||||
|
attachType: EventAttachType,
|
||||||
|
channel: BrowserWindowChannel
|
||||||
|
) {
|
||||||
|
EventHandlerRegistry.get(target, targetKey).merge(attachType, channel);
|
||||||
|
|
||||||
|
return EventHandlerRegistry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static inherit(clazz: Type<any>) {
|
||||||
|
const eventHandlers: EventHandlerMetadata[] = [];
|
||||||
|
let inheritedClass = ObjectUtil.getInheritedClass(clazz);
|
||||||
|
|
||||||
|
while (
|
||||||
|
inheritedClass &&
|
||||||
|
EventHandlerRegistry.hasEventHandlers(inheritedClass)
|
||||||
|
) {
|
||||||
|
EventHandlerRegistry.getOwnEventHandlers(inheritedClass).forEach(
|
||||||
|
(eventHandler: EventHandlerMetadata) => {
|
||||||
|
eventHandlers.push(inheritEndpoint(clazz, eventHandler));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
inheritedClass = ObjectUtil.getInheritedClass(inheritedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventHandlers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inheritEndpoint(
|
||||||
|
target: Type<any>,
|
||||||
|
eventHandler: EventHandlerMetadata
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
return new EventHandlerMetadata({
|
||||||
|
...eventHandler,
|
||||||
|
target,
|
||||||
|
type: eventHandler.type,
|
||||||
|
parent: eventHandler
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export enum EventAttachType {
|
||||||
|
on = 'on'
|
||||||
|
}
|
118
projects/common/src/lib/api/ipc-main/attacher.ts
Normal file
118
projects/common/src/lib/api/ipc-main/attacher.ts
Normal file
|
@ -0,0 +1,118 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
import { EventHandlerMetadata } from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
export abstract class Attacher {
|
||||||
|
private static readonly eventHandlerMap: Map<
|
||||||
|
any,
|
||||||
|
{ channel: string; listener: (...args: any[]) => void }[]
|
||||||
|
> = new Map();
|
||||||
|
|
||||||
|
static attach(instance: any) {
|
||||||
|
const metadatas = EventHandlerRegistry.getEventHandlers(
|
||||||
|
ObjectUtil.classOf(instance)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!!metadatas) {
|
||||||
|
metadatas.reverse().forEach((metadata) => {
|
||||||
|
metadata.eventHandlers.forEach((channels, attachType) => {
|
||||||
|
switch (attachType) {
|
||||||
|
case EventAttachType.on:
|
||||||
|
{
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
const listener = (...args: any[]) => {
|
||||||
|
Attacher.invoke(metadata, instance, args);
|
||||||
|
};
|
||||||
|
Electron.ipcMain.on(channel as any, listener);
|
||||||
|
Attacher.addEventHandler(instance, channel, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EventAttachType.once:
|
||||||
|
{
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
const listener = (...args: any[]) => {
|
||||||
|
Attacher.invoke(metadata, instance, args);
|
||||||
|
};
|
||||||
|
Electron.ipcMain.once(channel as any, listener);
|
||||||
|
Attacher.addEventHandler(instance, channel, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EventAttachType.handle:
|
||||||
|
{
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
const listener = (...args: any[]) => {
|
||||||
|
return Attacher.invoke(metadata, instance, args);
|
||||||
|
};
|
||||||
|
Electron.ipcMain.handle(channel as any, listener);
|
||||||
|
Attacher.addEventHandler(instance, channel, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EventAttachType.handleOnce:
|
||||||
|
{
|
||||||
|
channels.forEach((channel) => {
|
||||||
|
const listener = (...args: any[]) => {
|
||||||
|
return Attacher.invoke(metadata, instance, args);
|
||||||
|
};
|
||||||
|
Electron.ipcMain.handleOnce(channel as any, listener);
|
||||||
|
Attacher.addEventHandler(instance, channel, listener);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettach(instance: any) {
|
||||||
|
Attacher.removeEventHandler(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettachAll() {
|
||||||
|
Electron.ipcMain.removeAllListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
static invoke(
|
||||||
|
metadata: EventHandlerMetadata,
|
||||||
|
instance: any,
|
||||||
|
args: any[]
|
||||||
|
): any {
|
||||||
|
if (!!metadata.parent) {
|
||||||
|
return Attacher.invoke(metadata.parent, instance, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: ban-types
|
||||||
|
return (metadata.target.prototype[metadata.propertyKey] as Function).call(
|
||||||
|
instance,
|
||||||
|
...args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static addEventHandler(instance: any, channel: any, listener: any) {
|
||||||
|
let listeners = Attacher.eventHandlerMap.get(instance);
|
||||||
|
if (!listeners) {
|
||||||
|
listeners = [];
|
||||||
|
Attacher.eventHandlerMap.set(instance, listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
listeners.push({ channel, listener });
|
||||||
|
}
|
||||||
|
|
||||||
|
private static removeEventHandler(instance: any) {
|
||||||
|
const listeners = Attacher.eventHandlerMap.get(instance);
|
||||||
|
if (!listeners) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const listener of listeners) {
|
||||||
|
Electron.ipcMain.removeListener(listener.channel, listener.listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { DecoratorUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from '../registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export function HandleOnce(channel: string) {
|
||||||
|
return <T>(
|
||||||
|
target: any,
|
||||||
|
targetKey?: string,
|
||||||
|
descriptor?: TypedPropertyDescriptor<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target, targetKey, descriptor])) {
|
||||||
|
case 'method':
|
||||||
|
EventHandlerRegistry.use(
|
||||||
|
target,
|
||||||
|
targetKey!,
|
||||||
|
EventAttachType.handleOnce,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
24
projects/common/src/lib/api/ipc-main/decorators/handle.ts
Normal file
24
projects/common/src/lib/api/ipc-main/decorators/handle.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { DecoratorUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from '../registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export function Handle(channel: string) {
|
||||||
|
return <T>(
|
||||||
|
target: any,
|
||||||
|
targetKey?: string,
|
||||||
|
descriptor?: TypedPropertyDescriptor<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target, targetKey, descriptor])) {
|
||||||
|
case 'method':
|
||||||
|
EventHandlerRegistry.use(
|
||||||
|
target,
|
||||||
|
targetKey!,
|
||||||
|
EventAttachType.handle,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
24
projects/common/src/lib/api/ipc-main/decorators/on.ts
Normal file
24
projects/common/src/lib/api/ipc-main/decorators/on.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { DecoratorUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from '../registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export function On(channel: string) {
|
||||||
|
return <T>(
|
||||||
|
target: any,
|
||||||
|
targetKey?: string,
|
||||||
|
descriptor?: TypedPropertyDescriptor<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target, targetKey, descriptor])) {
|
||||||
|
case 'method':
|
||||||
|
EventHandlerRegistry.use(
|
||||||
|
target,
|
||||||
|
targetKey!,
|
||||||
|
EventAttachType.on,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
24
projects/common/src/lib/api/ipc-main/decorators/once.ts
Normal file
24
projects/common/src/lib/api/ipc-main/decorators/once.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { DecoratorUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from '../registries/event-handler.registry';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export function Once(channel: string) {
|
||||||
|
return <T>(
|
||||||
|
target: any,
|
||||||
|
targetKey?: string,
|
||||||
|
descriptor?: TypedPropertyDescriptor<T>
|
||||||
|
): TypedPropertyDescriptor<T> | void => {
|
||||||
|
switch (DecoratorUtil.getDecoratorType([target, targetKey, descriptor])) {
|
||||||
|
case 'method':
|
||||||
|
EventHandlerRegistry.use(
|
||||||
|
target,
|
||||||
|
targetKey!,
|
||||||
|
EventAttachType.once,
|
||||||
|
channel
|
||||||
|
);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
27
projects/common/src/lib/api/ipc-main/index.ts
Normal file
27
projects/common/src/lib/api/ipc-main/index.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Handle } from './decorators/handle';
|
||||||
|
import { HandleOnce } from './decorators/handle-once';
|
||||||
|
import { On } from './decorators/on';
|
||||||
|
import { Once } from './decorators/once';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata
|
||||||
|
} from './models/event-handler.metadata';
|
||||||
|
|
||||||
|
import { EventHandlerRegistry } from './registries/event-handler.registry';
|
||||||
|
|
||||||
|
import { EventAttachType } from './types/event-attach.type';
|
||||||
|
|
||||||
|
import { Attacher } from './attacher';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Handle,
|
||||||
|
HandleOnce,
|
||||||
|
On,
|
||||||
|
Once,
|
||||||
|
EventHandlerConstructorOptions,
|
||||||
|
EventHandlerMetadata,
|
||||||
|
EventHandlerRegistry,
|
||||||
|
EventAttachType,
|
||||||
|
Attacher
|
||||||
|
};
|
|
@ -0,0 +1,72 @@
|
||||||
|
import { Type, Metadata, ReflectUtil, ObjectUtil, Store } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export interface EventHandlerConstructorOptions {
|
||||||
|
target: Type<any>;
|
||||||
|
propertyKey: string | symbol;
|
||||||
|
eventHandlers?: Map<EventAttachType, string[]>;
|
||||||
|
type?: any;
|
||||||
|
parent?: EventHandlerMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EventHandlerMetadata extends Metadata
|
||||||
|
implements EventHandlerConstructorOptions {
|
||||||
|
eventHandlers: Map<EventAttachType, string[]> = new Map();
|
||||||
|
|
||||||
|
readonly parent: EventHandlerMetadata | undefined;
|
||||||
|
|
||||||
|
constructor(options: EventHandlerConstructorOptions) {
|
||||||
|
super(
|
||||||
|
options.target,
|
||||||
|
options.propertyKey,
|
||||||
|
Object.getOwnPropertyDescriptor(options.target, options.propertyKey)
|
||||||
|
);
|
||||||
|
const {
|
||||||
|
target,
|
||||||
|
parent,
|
||||||
|
propertyKey,
|
||||||
|
eventHandlers = new Map(),
|
||||||
|
type
|
||||||
|
} = options;
|
||||||
|
|
||||||
|
this._type = ReflectUtil.getReturnType(target, propertyKey);
|
||||||
|
|
||||||
|
this.eventHandlers = eventHandlers;
|
||||||
|
this.type = type;
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): Type<any> {
|
||||||
|
return ObjectUtil.isPromise(this._type) ||
|
||||||
|
ObjectUtil.isArrayOrArrayClass(this._type) ||
|
||||||
|
this._type === Object
|
||||||
|
? undefined!
|
||||||
|
: this._type;
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(type: Type<any>) {
|
||||||
|
this._type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(key: any) {
|
||||||
|
const ctrlValue = Store.from(this.target).get(key);
|
||||||
|
let meta = ObjectUtil.deepExtends(undefined, ctrlValue);
|
||||||
|
const endpointValue = this.store.get(key);
|
||||||
|
if (endpointValue !== undefined) {
|
||||||
|
meta = ObjectUtil.deepExtends(meta, endpointValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
merge(attachType: EventAttachType, channel: string): this {
|
||||||
|
if (!this.eventHandlers.has(attachType)) {
|
||||||
|
this.eventHandlers.set(attachType, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.eventHandlers.get(attachType).push(channel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { Type, ReflectUtil, ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { EventHandlerMetadata } from '../models/event-handler.metadata';
|
||||||
|
import { EventAttachType } from '../types/event-attach.type';
|
||||||
|
|
||||||
|
export const REFLECT_KEY_EVENTHANDLERS = 'ucap_electron_app_event_handlers';
|
||||||
|
|
||||||
|
export class EventHandlerRegistry {
|
||||||
|
static getOwnEventHandlers(target: Type<any>) {
|
||||||
|
if (!this.hasEventHandlers(target)) {
|
||||||
|
ReflectUtil.set(REFLECT_KEY_EVENTHANDLERS, [], target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getEventHandlers(target: Type<any>): EventHandlerMetadata[] {
|
||||||
|
return this.getOwnEventHandlers(target).concat(this.inherit(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
static hasEventHandlers(target: Type<any>): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static get(
|
||||||
|
target: Type<any>,
|
||||||
|
propertyKey: string | symbol
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
if (!this.has(target, propertyKey)) {
|
||||||
|
const evnetHandler = new EventHandlerMetadata({
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
});
|
||||||
|
this.getOwnEventHandlers(target).push(evnetHandler);
|
||||||
|
ReflectUtil.set(
|
||||||
|
REFLECT_KEY_EVENTHANDLERS,
|
||||||
|
evnetHandler,
|
||||||
|
target,
|
||||||
|
propertyKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReflectUtil.getOwn(REFLECT_KEY_EVENTHANDLERS, target, propertyKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
static has(target: Type<any>, method: string | symbol): boolean {
|
||||||
|
return ReflectUtil.hasOwn(REFLECT_KEY_EVENTHANDLERS, target, method);
|
||||||
|
}
|
||||||
|
|
||||||
|
static use(
|
||||||
|
target: Type<any>,
|
||||||
|
targetKey: string | symbol,
|
||||||
|
attachType: EventAttachType,
|
||||||
|
channel: string
|
||||||
|
) {
|
||||||
|
this.get(target, targetKey).merge(attachType, channel);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static inherit(clazz: Type<any>) {
|
||||||
|
const eventHandlers: EventHandlerMetadata[] = [];
|
||||||
|
let inheritedClass = ObjectUtil.getInheritedClass(clazz);
|
||||||
|
|
||||||
|
while (
|
||||||
|
inheritedClass &&
|
||||||
|
EventHandlerRegistry.hasEventHandlers(inheritedClass)
|
||||||
|
) {
|
||||||
|
this.getOwnEventHandlers(inheritedClass).forEach(
|
||||||
|
(eventHandler: EventHandlerMetadata) => {
|
||||||
|
eventHandlers.push(inheritEndpoint(clazz, eventHandler));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
inheritedClass = ObjectUtil.getInheritedClass(inheritedClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventHandlers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function inheritEndpoint(
|
||||||
|
target: Type<any>,
|
||||||
|
eventHandler: EventHandlerMetadata
|
||||||
|
): EventHandlerMetadata {
|
||||||
|
return new EventHandlerMetadata({
|
||||||
|
...eventHandler,
|
||||||
|
target,
|
||||||
|
type: eventHandler.type,
|
||||||
|
parent: eventHandler
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
export enum EventAttachType {
|
||||||
|
on = 'on',
|
||||||
|
once = 'once',
|
||||||
|
handle = 'handle',
|
||||||
|
handleOnce = 'handleOnce'
|
||||||
|
}
|
|
@ -1,91 +0,0 @@
|
||||||
import { Type, constructorOf } from '@tsed/core';
|
|
||||||
import {
|
|
||||||
GlobalProviders,
|
|
||||||
InjectorService,
|
|
||||||
ProviderScope,
|
|
||||||
registerProvider
|
|
||||||
} from '@tsed/di';
|
|
||||||
|
|
||||||
import * as Electron from 'electron';
|
|
||||||
|
|
||||||
import { AppOptions } from './decorators/app-settings';
|
|
||||||
import { AppSettingsService } from './services/app-settings.service';
|
|
||||||
import { ElectronApp } from './decorators/electron-app';
|
|
||||||
|
|
||||||
export abstract class App {
|
|
||||||
readonly injector: InjectorService;
|
|
||||||
private startedAt = new Date();
|
|
||||||
|
|
||||||
constructor(settings: Partial<AppOptions> = {}) {
|
|
||||||
// create injector with initial configuration
|
|
||||||
this.injector = this.createInjector(this.getConfiguration(this, settings));
|
|
||||||
|
|
||||||
this.createElectronApp(this.injector);
|
|
||||||
}
|
|
||||||
|
|
||||||
get settings(): AppSettingsService {
|
|
||||||
return this.injector.settings as AppSettingsService;
|
|
||||||
}
|
|
||||||
|
|
||||||
get electronApp(): ElectronApp {
|
|
||||||
return this.injector.get<ElectronApp>(ElectronApp)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async bootstrap(
|
|
||||||
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 createElectronApp(injector: InjectorService): void {
|
|
||||||
injector.forkProvider(ElectronApp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
registerProvider({
|
|
||||||
provide: ElectronApp,
|
|
||||||
scope: ProviderScope.SINGLETON,
|
|
||||||
global: true,
|
|
||||||
useValue: Electron.app
|
|
||||||
});
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { Type } from '@tsed/core';
|
|
||||||
import { IModuleOptions, Module } from '@tsed/di';
|
|
||||||
|
|
||||||
export interface AppOptions extends IModuleOptions {
|
|
||||||
bootstrap: Type<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AppSettings(settings: Partial<AppOptions> = {}): any {
|
|
||||||
return Module({ ...settings, root: true });
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
import { Type } from '@tsed/core';
|
|
||||||
import { Inject } from '@tsed/di';
|
|
||||||
import * as Electron from 'electron';
|
|
||||||
|
|
||||||
export type ElectronApp = Electron.App;
|
|
||||||
|
|
||||||
export function ElectronApp(
|
|
||||||
target: Type<any>,
|
|
||||||
targetKey: string,
|
|
||||||
// tslint:disable-next-line: ban-types
|
|
||||||
descriptor: TypedPropertyDescriptor<Function> | number
|
|
||||||
) {
|
|
||||||
return Inject(ElectronApp)(target, targetKey, descriptor);
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import {
|
|
||||||
DIConfiguration,
|
|
||||||
Injectable,
|
|
||||||
ProviderScope,
|
|
||||||
ProviderType
|
|
||||||
} from '@tsed/di';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
scope: ProviderScope.SINGLETON,
|
|
||||||
global: true
|
|
||||||
})
|
|
||||||
export class AppSettingsService extends DIConfiguration {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
}
|
|
25
projects/common/src/lib/platform/attacher.ts
Normal file
25
projects/common/src/lib/platform/attacher.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { Attacher as AppAttacher } from '../api/app/attacher';
|
||||||
|
import { Attacher as AutoUpdaterAttacher } from '../api/auto-updater/attacher';
|
||||||
|
import { Attacher as IpcMainAttacher } from '../api/ipc-main/attacher';
|
||||||
|
|
||||||
|
export abstract class Attacher {
|
||||||
|
static attach(instance: any) {
|
||||||
|
AppAttacher.attach(instance);
|
||||||
|
AutoUpdaterAttacher.attach(instance);
|
||||||
|
IpcMainAttacher.attach(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettach(instance: any) {
|
||||||
|
AppAttacher.dettach(instance);
|
||||||
|
AutoUpdaterAttacher.dettach(instance);
|
||||||
|
IpcMainAttacher.dettach(instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dettachAll() {
|
||||||
|
AppAttacher.dettachAll();
|
||||||
|
AutoUpdaterAttacher.dettachAll();
|
||||||
|
IpcMainAttacher.dettachAll();
|
||||||
|
}
|
||||||
|
}
|
39
projects/common/src/lib/platform/hooks.ts
Normal file
39
projects/common/src/lib/platform/hooks.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
export enum LifecycleHooks {
|
||||||
|
AfterInit,
|
||||||
|
BeforeDestroy
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AfterInit {
|
||||||
|
ucapAfterInit(): void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BeforeDestroy {
|
||||||
|
ucapBeforeDestroy(): void | Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hookNameOf = (lifecycleHook: LifecycleHooks) => {
|
||||||
|
switch (lifecycleHook) {
|
||||||
|
case LifecycleHooks.AfterInit:
|
||||||
|
return 'ucapAfterInit';
|
||||||
|
case LifecycleHooks.BeforeDestroy:
|
||||||
|
return 'ucapBeforeDestroy';
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getHook = (instance: any, lifecycleHook: LifecycleHooks) => {
|
||||||
|
const n = hookNameOf(lifecycleHook);
|
||||||
|
if (!n) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const h = instance[n];
|
||||||
|
if (!ObjectUtil.isFunction(h)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return h;
|
||||||
|
};
|
21
projects/common/src/lib/platform/index.ts
Normal file
21
projects/common/src/lib/platform/index.ts
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { Attacher } from './attacher';
|
||||||
|
import {
|
||||||
|
AfterInit,
|
||||||
|
BeforeDestroy,
|
||||||
|
LifecycleHooks,
|
||||||
|
hookNameOf,
|
||||||
|
getHook
|
||||||
|
} from './hooks';
|
||||||
|
import { Platform } from './platform';
|
||||||
|
import { ServiceBuilder } from './service-builder';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Attacher,
|
||||||
|
Platform,
|
||||||
|
ServiceBuilder,
|
||||||
|
AfterInit,
|
||||||
|
BeforeDestroy,
|
||||||
|
LifecycleHooks,
|
||||||
|
getHook,
|
||||||
|
hookNameOf
|
||||||
|
};
|
157
projects/common/src/lib/platform/platform.ts
Normal file
157
projects/common/src/lib/platform/platform.ts
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { Type, ObjectUtil } from '@ucap/core';
|
||||||
|
import { AppChannel } from '@ucap/electron-core';
|
||||||
|
|
||||||
|
import { Builder as AppBuilder } from '../api/app/builder';
|
||||||
|
import { AppConfiguration } from '../api/app/models/app-configuration';
|
||||||
|
import { getAppSettings } from '../api/app/decorators/app-settings';
|
||||||
|
import { Builder as BrowserWindowBuilder } from '../api/browser-window/builder';
|
||||||
|
import { ElectronBrowserWindow } from '../api/browser-window/electron-browser-window';
|
||||||
|
|
||||||
|
import { ServiceBuilder } from './service-builder';
|
||||||
|
import { LifecycleHooks, getHook } from './hooks';
|
||||||
|
|
||||||
|
import { Attacher } from './attacher';
|
||||||
|
|
||||||
|
export const CLASS_NAME = 'ucapClassName';
|
||||||
|
|
||||||
|
export class Platform {
|
||||||
|
protected static instanceMap: Map<string, any> = new Map();
|
||||||
|
protected static instance: Platform;
|
||||||
|
|
||||||
|
protected startedAt = new Date();
|
||||||
|
protected appConfig: AppConfiguration;
|
||||||
|
protected _app: any;
|
||||||
|
|
||||||
|
static getInstance<T>(typeOfInstance: Type<any>): T {
|
||||||
|
return Platform.instanceMap.get(Platform.getClassName(typeOfInstance)) as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAppConfig(): AppConfiguration {
|
||||||
|
return Platform.instance.appConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildBrowserWindow<T extends ElectronBrowserWindow>(
|
||||||
|
browserWindowType: Type<T>
|
||||||
|
): T {
|
||||||
|
const win = BrowserWindowBuilder.build(
|
||||||
|
Platform.instance.appConfig,
|
||||||
|
browserWindowType
|
||||||
|
);
|
||||||
|
Platform.setInstance(browserWindowType, win);
|
||||||
|
Platform.inject(win);
|
||||||
|
Attacher.attach(win);
|
||||||
|
|
||||||
|
return win;
|
||||||
|
}
|
||||||
|
|
||||||
|
static destroyBrowserWindow<T extends ElectronBrowserWindow>(
|
||||||
|
browserWindow: T
|
||||||
|
) {
|
||||||
|
BrowserWindowBuilder.destroy(browserWindow);
|
||||||
|
Platform.removeInstance(browserWindow);
|
||||||
|
Attacher.dettach(browserWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async bootstrap(
|
||||||
|
app: Type<any>,
|
||||||
|
configuration: Partial<AppConfiguration> = {}
|
||||||
|
): Promise<Platform> {
|
||||||
|
const _configuration = getAppSettings(app, configuration);
|
||||||
|
|
||||||
|
Platform.instance = new Platform();
|
||||||
|
Platform.instance.appConfig = _configuration;
|
||||||
|
|
||||||
|
Platform.instance._app = AppBuilder.build(app, _configuration);
|
||||||
|
Platform.setInstance(app, Platform.instance._app);
|
||||||
|
|
||||||
|
if (!!_configuration.services && 0 < _configuration.services.length) {
|
||||||
|
_configuration.services.forEach((serviceType) => {
|
||||||
|
const i = ServiceBuilder.build(serviceType, _configuration);
|
||||||
|
Platform.setInstance(serviceType, i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.instanceMap.forEach((i, n) => {
|
||||||
|
Platform.inject(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
let afterInitHook: (...args: any[]) => any;
|
||||||
|
let afterInitHookReturn: Promise<any> | undefined;
|
||||||
|
|
||||||
|
Platform.instanceMap.forEach(async (i, n) => {
|
||||||
|
afterInitHook = getHook(i, LifecycleHooks.AfterInit);
|
||||||
|
if (!!afterInitHook) {
|
||||||
|
afterInitHookReturn = afterInitHook.call(i);
|
||||||
|
if (afterInitHookReturn instanceof Promise) {
|
||||||
|
await Promise.resolve(afterInitHookReturn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Platform.instanceMap.forEach((i, n) => {
|
||||||
|
Attacher.attach(i);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Platform.instance.runLifecycle();
|
||||||
|
|
||||||
|
return Platform.instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static getClassName(typeOfInstance: Type<any>): string {
|
||||||
|
const name = typeOfInstance[CLASS_NAME];
|
||||||
|
if (!name) {
|
||||||
|
throw Error(
|
||||||
|
`name of class is not defined. use static property ${CLASS_NAME}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static setInstance(typeOfInstance: Type<any>, instance: any) {
|
||||||
|
const name = typeOfInstance[CLASS_NAME];
|
||||||
|
if (Platform.instanceMap.has(name)) {
|
||||||
|
throw Error(`duplicated name of class[${name}]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.instanceMap.set(name, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static removeInstance(instance: any) {
|
||||||
|
const name = ObjectUtil.getConstructor(instance)[CLASS_NAME];
|
||||||
|
if (!Platform.instanceMap.has(name)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Platform.instanceMap.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static inject(instance: any) {
|
||||||
|
Platform.instanceMap.forEach((i, n) => {
|
||||||
|
instance[n] = i;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<void> {}
|
||||||
|
|
||||||
|
private async runLifecycle(): Promise<void> {
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
const __this = this;
|
||||||
|
Electron.app.once(AppChannel.beforeQuit, async () => {
|
||||||
|
let beforeDestroyHook: (...args: any[]) => any;
|
||||||
|
let beforeDestroyHookReturn: Promise<any> | undefined;
|
||||||
|
|
||||||
|
Platform.instanceMap.forEach(async (i, n) => {
|
||||||
|
beforeDestroyHook = getHook(i, LifecycleHooks.AfterInit);
|
||||||
|
if (!!beforeDestroyHook) {
|
||||||
|
beforeDestroyHookReturn = beforeDestroyHook.call(i);
|
||||||
|
if (beforeDestroyHookReturn instanceof Promise) {
|
||||||
|
await Promise.resolve(beforeDestroyHookReturn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
13
projects/common/src/lib/platform/service-builder.ts
Normal file
13
projects/common/src/lib/platform/service-builder.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { Type } from '@ucap/core';
|
||||||
|
import { AppConfiguration } from '../api/app/models/app-configuration';
|
||||||
|
|
||||||
|
export abstract class ServiceBuilder {
|
||||||
|
static build<T>(
|
||||||
|
serviceType: Type<T>,
|
||||||
|
configuration: Partial<AppConfiguration>
|
||||||
|
): T {
|
||||||
|
const service = new serviceType(configuration);
|
||||||
|
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,10 @@
|
||||||
* Public API Surface of common
|
* Public API Surface of common
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './lib/app/decorators/app-settings';
|
import * as AppApi from './lib/api/app';
|
||||||
export * from './lib/app/decorators/electron-app';
|
import * as AutoUpdaterApi from './lib/api/auto-updater';
|
||||||
|
import * as BrowserWindowApi from './lib/api/browser-window';
|
||||||
|
import * as IpcMainApi from './lib/api/ipc-main';
|
||||||
|
import * as PlatformApi from './lib/platform';
|
||||||
|
|
||||||
export * from './lib/app/services/app-settings.service';
|
export { AppApi, AutoUpdaterApi, BrowserWindowApi, IpcMainApi, PlatformApi };
|
||||||
|
|
||||||
export * from './lib/app/app';
|
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/electron-core",
|
"name": "@ucap/electron-core",
|
||||||
"version": "0.0.1",
|
"version": "0.0.16",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {},
|
"peerDependencies": {}
|
||||||
"devDependencies": {}
|
|
||||||
}
|
}
|
||||||
|
|
36
projects/core/src/lib/types/app-channel.type.ts
Normal file
36
projects/core/src/lib/types/app-channel.type.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
export enum AppChannel {
|
||||||
|
accessibilitySupportChanged = 'accessibility-support-changed',
|
||||||
|
activate = 'activate',
|
||||||
|
activityWasContinued = 'activity-was-continued',
|
||||||
|
beforeQuit = 'before-quit',
|
||||||
|
browserWindowBlur = 'browser-window-blur',
|
||||||
|
browserWindowCreated = 'browser-window-created',
|
||||||
|
browserWindowFocus = 'browser-window-focus',
|
||||||
|
certificateError = 'certificate-error',
|
||||||
|
continueActivity = 'continue-activity',
|
||||||
|
continueActivityError = 'continue-activity-error',
|
||||||
|
desktopCapturerGetSources = 'desktop-capturer-get-sources',
|
||||||
|
gpuInfoUpdate = 'gpu-info-update',
|
||||||
|
gpuProcessCrashed = 'gpu-process-crashed',
|
||||||
|
login = 'login',
|
||||||
|
newWindowForTab = 'new-window-for-tab',
|
||||||
|
openFile = 'open-file',
|
||||||
|
openUrl = 'open-url',
|
||||||
|
quit = 'quit',
|
||||||
|
ready = 'ready',
|
||||||
|
remoteGetBuiltin = 'remote-get-builtin',
|
||||||
|
remoteGetCurrentWebContents = 'remote-get-current-web-contents',
|
||||||
|
remoteGetCurrentWindow = 'remote-get-current-window',
|
||||||
|
remoteGetGlobal = 'remote-get-global',
|
||||||
|
remoteRequire = 'remote-require',
|
||||||
|
rendererProcessCrashed = 'renderer-process-crashed',
|
||||||
|
secondInstance = 'second-instance',
|
||||||
|
selectClientCertificate = 'select-client-certificate',
|
||||||
|
sessionCreated = 'session-created',
|
||||||
|
updateActivityState = 'update-activity-state',
|
||||||
|
webContentsCreated = 'web-contents-created',
|
||||||
|
willContinueActivity = 'will-continue-activity',
|
||||||
|
willFinishLaunching = 'will-finish-launching',
|
||||||
|
willQuit = 'will-quit',
|
||||||
|
windowAllClosed = 'window-all-closed'
|
||||||
|
}
|
11
projects/core/src/lib/types/auto-updater-channel.type.ts
Normal file
11
projects/core/src/lib/types/auto-updater-channel.type.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
export enum AutoUpdaterChannel {
|
||||||
|
beforeQuitForUpdate = 'before-quit-for-update',
|
||||||
|
checkingForUpdate = 'checking-for-update',
|
||||||
|
downloadProgress = 'download-progress',
|
||||||
|
error = 'error',
|
||||||
|
login = 'login',
|
||||||
|
updateAvailable = 'update-available',
|
||||||
|
updateCancelled = 'update-cancelled',
|
||||||
|
updateDownloaded = 'update-downloaded',
|
||||||
|
updateNotAvailable = 'update-not-available'
|
||||||
|
}
|
36
projects/core/src/lib/types/browser-window.type.ts
Normal file
36
projects/core/src/lib/types/browser-window.type.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
export enum BrowserWindowChannel {
|
||||||
|
alwaysOnTopChanged = 'always-on-top-changed',
|
||||||
|
appCommand = 'app-command',
|
||||||
|
blur = 'blur',
|
||||||
|
close = 'close',
|
||||||
|
closed = 'closed',
|
||||||
|
enterFullScreen = 'enter-full-screen',
|
||||||
|
enterHtmlFullScreen = 'enter-html-full-screen',
|
||||||
|
focus = 'focus',
|
||||||
|
hide = 'hide',
|
||||||
|
leaveFullScreen = 'leave-full-screen',
|
||||||
|
leaveHtmlFullScreen = 'leave-html-full-screen',
|
||||||
|
maximize = 'maximize',
|
||||||
|
minimize = 'minimize',
|
||||||
|
move = 'move',
|
||||||
|
moved = 'moved',
|
||||||
|
newWindowForTab = 'new-window-for-tab',
|
||||||
|
pageTitleUpdated = 'page-title-updated',
|
||||||
|
readyToShow = 'ready-to-show',
|
||||||
|
resize = 'resize',
|
||||||
|
responsive = 'responsive',
|
||||||
|
restore = 'restore',
|
||||||
|
rotateGesture = 'rotate-gesture',
|
||||||
|
scrollTouchBegin = 'scroll-touch-begin',
|
||||||
|
scrollTouchEdge = 'scroll-touch-edge',
|
||||||
|
scrollTouchEnd = 'scroll-touch-end',
|
||||||
|
sessionEnd = 'session-end',
|
||||||
|
sheetBegin = 'sheet-begin',
|
||||||
|
sheetEnd = 'sheet-end',
|
||||||
|
show = 'show',
|
||||||
|
swipe = 'swipe',
|
||||||
|
unmaximize = 'unmaximize',
|
||||||
|
unresponsive = 'unresponsive',
|
||||||
|
willMove = 'will-move',
|
||||||
|
willResize = 'will-resize'
|
||||||
|
}
|
|
@ -1,39 +0,0 @@
|
||||||
export enum AppChannel {
|
|
||||||
WillFinishLaunching = 'will-finish-launching',
|
|
||||||
Ready = 'ready',
|
|
||||||
WindowAllClosed = 'window-all-closed',
|
|
||||||
BeforeQuit = 'before-quit',
|
|
||||||
WillQuit = 'will-quit',
|
|
||||||
Quit = 'quit',
|
|
||||||
OpenFile = 'open-file',
|
|
||||||
OpenUrl = 'open-url',
|
|
||||||
Activate = 'activate',
|
|
||||||
ContinueActivity = 'continue-activity',
|
|
||||||
WillContinueActivity = 'will-continue-activity',
|
|
||||||
ContinueActivityError = 'continue-activity-error',
|
|
||||||
ActivityWasContinued = 'activity-was-continued',
|
|
||||||
SecondInstance = 'second-instance'
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum BrowserWindowChannel {
|
|
||||||
EnterFullScreen = 'enter-full-screen',
|
|
||||||
LeaveFullScreen = 'leave-full-screen',
|
|
||||||
Maximize = 'maximize',
|
|
||||||
Minimize = 'minimize',
|
|
||||||
Unmaximize = 'unmaximize',
|
|
||||||
Restore = 'restore',
|
|
||||||
Hide = 'hide',
|
|
||||||
Show = 'show',
|
|
||||||
Close = 'close',
|
|
||||||
Closed = 'closed',
|
|
||||||
ReadyToShow = 'ready-to-show',
|
|
||||||
Focus = 'focus',
|
|
||||||
Blur = 'blur'
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum WebContentsChannel {
|
|
||||||
DevtoolsOpened = 'devtools-opened',
|
|
||||||
DidStartLoading = 'did-start-loading',
|
|
||||||
DidFinishLoad = 'did-finish-load',
|
|
||||||
DidFailLoad = 'did-fail-load'
|
|
||||||
}
|
|
57
projects/core/src/lib/types/web-contents-channel.type.ts
Normal file
57
projects/core/src/lib/types/web-contents-channel.type.ts
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
export enum WebContentsChannel {
|
||||||
|
beforeInputEvent = 'before-input-event',
|
||||||
|
certificateError = 'certificate-error',
|
||||||
|
consoleMessage = 'console-message',
|
||||||
|
contextMenu = 'context-menu',
|
||||||
|
crashed = 'crashed',
|
||||||
|
cursorChanged = 'cursor-changed',
|
||||||
|
desktopCapturerGetSources = 'desktop-capturer-get-sources',
|
||||||
|
destroyed = 'destroyed',
|
||||||
|
devtoolsClosed = 'devtools-closed',
|
||||||
|
devtoolsFocused = 'devtools-focused',
|
||||||
|
devtoolsOpened = 'devtools-opened',
|
||||||
|
devtoolsReloadPage = 'devtools-reload-page',
|
||||||
|
didAttachWebview = 'did-attach-webview',
|
||||||
|
didChangeThemeColor = 'did-change-theme-color',
|
||||||
|
didFailLoad = 'did-fail-load',
|
||||||
|
didFailProvisionalLoad = 'did-fail-provisional-load',
|
||||||
|
didFinishLoad = 'did-finish-load',
|
||||||
|
didFrameFinishLoad = 'did-frame-finish-load',
|
||||||
|
didFrameNavigate = 'did-frame-navigate',
|
||||||
|
didNavigate = 'did-navigate',
|
||||||
|
didNavigateInPage = 'did-navigate-in-page',
|
||||||
|
didRedirectNavigation = 'did-redirect-navigation',
|
||||||
|
didStartLoading = 'did-start-loading',
|
||||||
|
didStartNavigation = 'did-start-navigation',
|
||||||
|
didStopLoading = 'did-stop-loading',
|
||||||
|
domReady = 'dom-ready',
|
||||||
|
enterHtmlFullScreen = 'enter-html-full-screen',
|
||||||
|
foundInPage = 'found-in-page',
|
||||||
|
ipcMessage = 'ipc-message',
|
||||||
|
ipcMessageSync = 'ipc-message-sync',
|
||||||
|
leaveHtmlFullScreen = 'leave-html-full-screen',
|
||||||
|
login = 'login',
|
||||||
|
mediaPaused = 'media-paused',
|
||||||
|
mediaStartedPlaying = 'media-started-playing',
|
||||||
|
newWindow = 'new-window',
|
||||||
|
pageFaviconUpdated = 'page-favicon-updated',
|
||||||
|
pageTitleUpdated = 'page-title-updated',
|
||||||
|
paint = 'paint',
|
||||||
|
pluginCrashed = 'plugin-crashed',
|
||||||
|
preloadError = 'preload-error',
|
||||||
|
remoteGetBuiltin = 'remote-get-builtin',
|
||||||
|
remoteGetCurrentWebContents = 'remote-get-current-web-contents',
|
||||||
|
remoteGetCurrentWindow = 'remote-get-current-window',
|
||||||
|
remoteGetGlobal = 'remote-get-global',
|
||||||
|
remoteRequire = 'remote-require',
|
||||||
|
responsive = 'responsive',
|
||||||
|
selectBluetoothDevice = 'select-bluetooth-device',
|
||||||
|
selectClientCertificate = 'select-client-certificate',
|
||||||
|
unresponsive = 'unresponsive',
|
||||||
|
updateTargetUrl = 'update-target-url',
|
||||||
|
willAttachWebview = 'will-attach-webview',
|
||||||
|
willNavigate = 'will-navigate',
|
||||||
|
willPreventUnload = 'will-prevent-unload',
|
||||||
|
willRedirect = 'will-redirect',
|
||||||
|
zoomChanged = 'zoom-changed'
|
||||||
|
}
|
22
projects/core/src/lib/utils/browser-window.util.ts
Normal file
22
projects/core/src/lib/utils/browser-window.util.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
import { PlatformUtil } from './platform.util';
|
||||||
|
|
||||||
|
export class BrowserWindowUtil {
|
||||||
|
static all(): Electron.BrowserWindow[] | undefined {
|
||||||
|
if (PlatformUtil.isRenderer()) {
|
||||||
|
return Electron.remote.BrowserWindow.getAllWindows();
|
||||||
|
} else {
|
||||||
|
return Electron.BrowserWindow.getAllWindows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static main(): Electron.BrowserWindow | undefined {
|
||||||
|
const browserWindows: Electron.BrowserWindow[] = BrowserWindowUtil.all();
|
||||||
|
|
||||||
|
if (!browserWindows || 0 === browserWindows.length) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return browserWindows[browserWindows.length - 1];
|
||||||
|
}
|
||||||
|
}
|
192
projects/core/src/lib/utils/file.util.ts
Normal file
192
projects/core/src/lib/utils/file.util.ts
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as fse from 'fs-extra';
|
||||||
|
|
||||||
|
export interface UniqueFileNameOption {
|
||||||
|
separator?: string;
|
||||||
|
mode?:
|
||||||
|
| 'numeric'
|
||||||
|
| 'alpha'
|
||||||
|
| 'ALPHA'
|
||||||
|
| 'alphanumeric'
|
||||||
|
| 'ALPHANUMERIC'
|
||||||
|
| 'charset';
|
||||||
|
paddingCharacter?: string;
|
||||||
|
paddingSize?: number;
|
||||||
|
alwaysAppend?: boolean;
|
||||||
|
charset?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const charsets = {
|
||||||
|
alpha: 'abcdefghijklmnopqrstuvwxyz',
|
||||||
|
alphanumeric: '0123456789abcdefghijklmnopqrstuvwxyz',
|
||||||
|
ALPHA: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||||
|
ALPHANUMERIC: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
};
|
||||||
|
|
||||||
|
interface UniqueFile {
|
||||||
|
dir?: string;
|
||||||
|
ext?: string;
|
||||||
|
base?: string;
|
||||||
|
increment?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FileUtil {
|
||||||
|
static blobToBuffer(blob: Blob): Promise<Buffer> {
|
||||||
|
if (typeof Blob === 'undefined' || !(blob instanceof Blob)) {
|
||||||
|
throw new Error('first argument must be a Blob');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => {
|
||||||
|
resolve(Buffer.from(reader.result as ArrayBuffer));
|
||||||
|
};
|
||||||
|
reader.onerror = () => {
|
||||||
|
reader.abort();
|
||||||
|
reject(reader.error);
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static uniqueFileName(
|
||||||
|
filePath: string,
|
||||||
|
options?: UniqueFileNameOption
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
const dir = path.dirname(filePath);
|
||||||
|
const ext = path.extname(filePath);
|
||||||
|
const base = path.basename(filePath, ext);
|
||||||
|
|
||||||
|
const uniqueFile: UniqueFile = {
|
||||||
|
dir,
|
||||||
|
ext,
|
||||||
|
base
|
||||||
|
};
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.separator = options.separator || '-';
|
||||||
|
options.mode = options.mode || 'numeric';
|
||||||
|
|
||||||
|
if ('numeric' !== options.mode) {
|
||||||
|
if (charsets[options.mode]) {
|
||||||
|
options.charset = charsets[options.mode];
|
||||||
|
options.mode = 'charset';
|
||||||
|
} else if (
|
||||||
|
'charset' !== options.mode ||
|
||||||
|
('charset' === options.mode && !options.charset)
|
||||||
|
) {
|
||||||
|
options.mode = 'numeric';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.paddingSize && !options.paddingCharacter) {
|
||||||
|
options.paddingCharacter = '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
FileUtil.uniqueFileNameProcess(
|
||||||
|
uniqueFile,
|
||||||
|
options,
|
||||||
|
(fileName: string) => {
|
||||||
|
resolve(fileName);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uniqueFileNameProcess(
|
||||||
|
uniqueFile: UniqueFile,
|
||||||
|
options: UniqueFileNameOption,
|
||||||
|
callback: (fileName: string) => void
|
||||||
|
) {
|
||||||
|
let fileName: string;
|
||||||
|
let append = '';
|
||||||
|
|
||||||
|
if (options.alwaysAppend && !uniqueFile.increment) {
|
||||||
|
uniqueFile.increment = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uniqueFile.increment) {
|
||||||
|
if ('numeric' === options.mode) {
|
||||||
|
append = '' + uniqueFile.increment;
|
||||||
|
} else {
|
||||||
|
append = FileUtil.numberToString(uniqueFile.increment, options.charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.paddingSize) {
|
||||||
|
while (append.length < options.paddingSize) {
|
||||||
|
append = options.paddingCharacter + append;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append = options.separator + append;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName = path.join(
|
||||||
|
uniqueFile.dir,
|
||||||
|
uniqueFile.base + append + uniqueFile.ext
|
||||||
|
);
|
||||||
|
if (fse.existsSync(fileName)) {
|
||||||
|
if (uniqueFile.increment) {
|
||||||
|
uniqueFile.increment += 1;
|
||||||
|
} else {
|
||||||
|
uniqueFile.increment = 'numeric' === options.mode ? 2 : 1;
|
||||||
|
}
|
||||||
|
return FileUtil.uniqueFileNameProcess(uniqueFile, options, callback);
|
||||||
|
} else {
|
||||||
|
return callback(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static numberToString(nbr: number, charset: string) {
|
||||||
|
const charsetLen = charset.length;
|
||||||
|
let strLen = 0;
|
||||||
|
let strThisLen = 0;
|
||||||
|
let tmp: number;
|
||||||
|
|
||||||
|
for (let maxpower = 20; maxpower >= 0; maxpower--) {
|
||||||
|
const maxvalue = FileUtil.sumOfPowerFromOne(charsetLen, maxpower);
|
||||||
|
|
||||||
|
if (maxvalue < nbr) {
|
||||||
|
strLen = maxpower + 1;
|
||||||
|
strThisLen = maxvalue + Math.pow(charsetLen, maxpower + 1) - maxvalue;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === strLen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let str = '';
|
||||||
|
while (--strLen >= 0) {
|
||||||
|
if (strLen === 0) {
|
||||||
|
str += charset.charAt(nbr - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
strThisLen = Math.pow(charsetLen, strLen);
|
||||||
|
const initial = FileUtil.sumOfPowerFromOne(charsetLen, strLen - 1);
|
||||||
|
|
||||||
|
for (tmp = charsetLen; tmp >= 1; tmp--) {
|
||||||
|
if (initial + tmp * strThisLen < nbr) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nbr -= tmp * strThisLen;
|
||||||
|
str += charset.charAt(tmp - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static sumOfPowerFromOne(base: number, maxpower: number) {
|
||||||
|
let value = 0;
|
||||||
|
for (let tmp = maxpower; tmp >= 1; tmp--) {
|
||||||
|
value += Math.pow(base, tmp);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
65
projects/core/src/lib/utils/platform.util.ts
Normal file
65
projects/core/src/lib/utils/platform.util.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export const NODE_ENV = 'NODE_ENV';
|
||||||
|
|
||||||
|
export class PlatformUtil {
|
||||||
|
static isRenderer() {
|
||||||
|
return !!process && !!process.type && 'renderer' === process.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isProduction() {
|
||||||
|
return (
|
||||||
|
!!process &&
|
||||||
|
!!process.env &&
|
||||||
|
!!process.env[NODE_ENV] &&
|
||||||
|
'production' === process.env[NODE_ENV]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static isPackaged() {
|
||||||
|
if (
|
||||||
|
!!process.mainModule &&
|
||||||
|
-1 !== process.mainModule.filename.indexOf('app.asar')
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else if (
|
||||||
|
0 < process.argv.filter((a) => -1 !== a.indexOf('app.asar')).length
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static isWindows() {
|
||||||
|
return !!process && !!process.platform && 'win32' === process.platform;
|
||||||
|
}
|
||||||
|
static isDarwin() {
|
||||||
|
return !!process && !!process.platform && 'darwin' === process.platform;
|
||||||
|
}
|
||||||
|
static isLinux() {
|
||||||
|
return !!process && !!process.platform && 'linux' === process.platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
static rootPath(basePath: string = './') {
|
||||||
|
if (PlatformUtil.isPackaged()) {
|
||||||
|
if (PlatformUtil.isWindows()) {
|
||||||
|
return path.join(basePath, '..');
|
||||||
|
} else {
|
||||||
|
return path.join(basePath, '..');
|
||||||
|
}
|
||||||
|
} else if (PlatformUtil.isProduction()) {
|
||||||
|
if (PlatformUtil.isRenderer()) {
|
||||||
|
return path.join(basePath, '..');
|
||||||
|
} else if (!module.parent) {
|
||||||
|
return path.join(basePath, '..');
|
||||||
|
} else {
|
||||||
|
return path.join(basePath, '..');
|
||||||
|
}
|
||||||
|
} else if (PlatformUtil.isRenderer()) {
|
||||||
|
return path.join(basePath, '..');
|
||||||
|
} else {
|
||||||
|
return path.join(basePath, '..');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,4 +2,11 @@
|
||||||
* Public API Surface of core
|
* Public API Surface of core
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './lib/types/channel.type';
|
export * from './lib/types/app-channel.type';
|
||||||
|
export * from './lib/types/auto-updater-channel.type';
|
||||||
|
export * from './lib/types/browser-window.type';
|
||||||
|
export * from './lib/types/web-contents-channel.type';
|
||||||
|
|
||||||
|
export * from './lib/utils/browser-window.util';
|
||||||
|
export * from './lib/utils/file.util';
|
||||||
|
export * from './lib/utils/platform.util';
|
||||||
|
|
14
projects/i18n/package.json
Normal file
14
projects/i18n/package.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"name": "@ucap/electron-i18n",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
|
},
|
||||||
|
"scripts": {},
|
||||||
|
"peerDependencies": {
|
||||||
|
"electron": "^9.0.3",
|
||||||
|
"electron-log": "^4.2.1",
|
||||||
|
"i18next": "^19.3.2",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
1
projects/i18n/src/lib/services/i18n.service.ts
Normal file
1
projects/i18n/src/lib/services/i18n.service.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export class I18nService {}
|
7
projects/i18n/src/public-api.ts
Normal file
7
projects/i18n/src/public-api.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Public API Surface of electron-native
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './lib/types/channel.type';
|
||||||
|
|
||||||
|
export * from './lib/services/electron-native.service';
|
12
projects/i18n/tsconfig.lib.json
Normal file
12
projects/i18n/tsconfig.lib.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/lib",
|
||||||
|
"target": "es2015",
|
||||||
|
"declaration": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": [],
|
||||||
|
"lib": ["dom", "es2018"]
|
||||||
|
},
|
||||||
|
"exclude": ["src/test.ts", "**/*.spec.ts"]
|
||||||
|
}
|
3
projects/i18n/tsconfig.lib.prod.json
Normal file
3
projects/i18n/tsconfig.lib.prod.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.lib.json"
|
||||||
|
}
|
9
projects/i18n/tsconfig.spec.json
Normal file
9
projects/i18n/tsconfig.spec.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/spec",
|
||||||
|
"types": ["jasmine", "node"]
|
||||||
|
},
|
||||||
|
"files": ["src/test.ts"],
|
||||||
|
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||||
|
}
|
3
projects/i18n/tslint.json
Normal file
3
projects/i18n/tslint.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tslint.json"
|
||||||
|
}
|
7
projects/i18n/ucap-package.json
Normal file
7
projects/i18n/ucap-package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"dest": "../../dist/native",
|
||||||
|
"docDest": "../../docs/native",
|
||||||
|
"lib": {
|
||||||
|
"entryFile": "src/public-api.ts"
|
||||||
|
}
|
||||||
|
}
|
13
projects/logger/package.json
Normal file
13
projects/logger/package.json
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"name": "@ucap/electron-logger",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
|
},
|
||||||
|
"scripts": {},
|
||||||
|
"peerDependencies": {
|
||||||
|
"electron": "^9.0.3",
|
||||||
|
"electron-log": "^4.2.1",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
49
projects/logger/src/lib/appenders/electron-log.appender.ts
Normal file
49
projects/logger/src/lib/appenders/electron-log.appender.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { LogAppender, LogEntry, LogLevel } from '@ucap/logger';
|
||||||
|
|
||||||
|
import electron_log from 'electron-log';
|
||||||
|
|
||||||
|
export class ElectronLogAppender extends LogAppender {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
log(entry: LogEntry): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
switch (entry.level) {
|
||||||
|
case LogLevel.Trace:
|
||||||
|
electron_log.silly(...entry.args);
|
||||||
|
break;
|
||||||
|
case LogLevel.Debug:
|
||||||
|
electron_log.debug(...entry.args);
|
||||||
|
break;
|
||||||
|
case LogLevel.Info:
|
||||||
|
electron_log.verbose(...entry.args);
|
||||||
|
break;
|
||||||
|
case LogLevel.Warn:
|
||||||
|
electron_log.info(...entry.args);
|
||||||
|
break;
|
||||||
|
case LogLevel.Error:
|
||||||
|
electron_log.warn(...entry.args);
|
||||||
|
break;
|
||||||
|
case LogLevel.Fatal:
|
||||||
|
electron_log.error(...entry.args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
5
projects/logger/src/public-api.ts
Normal file
5
projects/logger/src/public-api.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/*
|
||||||
|
* Public API Surface of electron-logger
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './lib/appenders/electron-log.appender';
|
12
projects/logger/tsconfig.lib.json
Normal file
12
projects/logger/tsconfig.lib.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/lib",
|
||||||
|
"target": "es2015",
|
||||||
|
"declaration": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": ["node"],
|
||||||
|
"lib": ["dom", "es2018"]
|
||||||
|
},
|
||||||
|
"exclude": ["src/test.ts", "**/*.spec.ts"]
|
||||||
|
}
|
3
projects/logger/tsconfig.lib.prod.json
Normal file
3
projects/logger/tsconfig.lib.prod.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.lib.json"
|
||||||
|
}
|
9
projects/logger/tsconfig.spec.json
Normal file
9
projects/logger/tsconfig.spec.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/spec",
|
||||||
|
"types": ["jasmine", "node"]
|
||||||
|
},
|
||||||
|
"files": ["src/test.ts"],
|
||||||
|
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||||
|
}
|
3
projects/logger/tslint.json
Normal file
3
projects/logger/tslint.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tslint.json"
|
||||||
|
}
|
7
projects/logger/ucap-package.json
Normal file
7
projects/logger/ucap-package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"dest": "../../dist/logger",
|
||||||
|
"docDest": "../../docs/logger",
|
||||||
|
"lib": {
|
||||||
|
"entryFile": "src/public-api.ts"
|
||||||
|
}
|
||||||
|
}
|
20
projects/native/package.json
Normal file
20
projects/native/package.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"name": "@ucap/electron-native",
|
||||||
|
"version": "0.0.19",
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
|
},
|
||||||
|
"scripts": {},
|
||||||
|
"peerDependencies": {
|
||||||
|
"electron": "^9.0.3",
|
||||||
|
"electron-log": "^4.2.1",
|
||||||
|
"@ucap/core": "~0.0.1",
|
||||||
|
"@ucap/domain-common": "~0.0.1",
|
||||||
|
"@ucap/domain-organization": "~0.0.1",
|
||||||
|
"@ucap/domain-status": "~0.0.1",
|
||||||
|
"@ucap/native": "~0.0.1",
|
||||||
|
"axios": "^0.19.2",
|
||||||
|
"rxjs": "~6.5.4",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
717
projects/native/src/lib/services/electron-native.service.ts
Normal file
717
projects/native/src/lib/services/electron-native.service.ts
Normal file
|
@ -0,0 +1,717 @@
|
||||||
|
import { Subject, BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
import { AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
|
import { StatusCode } from '@ucap/domain-status';
|
||||||
|
|
||||||
|
import {
|
||||||
|
NativeService,
|
||||||
|
UpdateCheckConfig,
|
||||||
|
UpdateInfo,
|
||||||
|
NativeType,
|
||||||
|
WindowState,
|
||||||
|
WindowIdle,
|
||||||
|
NotificationRequest,
|
||||||
|
AppInitInfo
|
||||||
|
} from '@ucap/native';
|
||||||
|
import {
|
||||||
|
PlatformChannel,
|
||||||
|
FileChannel,
|
||||||
|
WindowChannel,
|
||||||
|
IdleChannel,
|
||||||
|
AppChannel,
|
||||||
|
ChatChannel,
|
||||||
|
MessageChannel
|
||||||
|
} from '../types/channel.type';
|
||||||
|
|
||||||
|
const listenersOfChannel = [
|
||||||
|
WindowChannel.onState$,
|
||||||
|
WindowChannel.onFocus$,
|
||||||
|
IdleChannel.onState$,
|
||||||
|
AppChannel.onUpdate$,
|
||||||
|
AppChannel.onLogout$,
|
||||||
|
AppChannel.onStatus$,
|
||||||
|
AppChannel.onShowSetting$,
|
||||||
|
AppChannel.onExit$,
|
||||||
|
ChatChannel.onOpen$,
|
||||||
|
MessageChannel.onOpen$
|
||||||
|
];
|
||||||
|
|
||||||
|
export class ElectronNativeService extends NativeService {
|
||||||
|
private ipcRenderer: typeof Electron.ipcRenderer;
|
||||||
|
private webFrame: typeof Electron.webFrame;
|
||||||
|
private remote: typeof Electron.remote;
|
||||||
|
private shell: typeof Electron.shell;
|
||||||
|
|
||||||
|
private platform: string;
|
||||||
|
|
||||||
|
constructor(private axios: AxiosInstance) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.ipcRenderer = (window as any).require('electron').ipcRenderer;
|
||||||
|
this.remote = (window as any).require('electron').remote;
|
||||||
|
this.shell = (window as any).require('electron').shell;
|
||||||
|
this.webFrame = (window as any).require('electron').webFrame;
|
||||||
|
|
||||||
|
this.platform = (window as any).require('os').platform();
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_nativeType(): Promise<NativeType> {
|
||||||
|
return new Promise<any>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(NativeType.Electron);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_networkInfo(): Promise<any> {
|
||||||
|
return new Promise<any>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const networkInfo = await this.ipcRenderer.invoke(
|
||||||
|
PlatformChannel.networkInfo
|
||||||
|
);
|
||||||
|
resolve(networkInfo);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_execute(executableName: string): Promise<number> {
|
||||||
|
return new Promise<number>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const id = await this.ipcRenderer.invoke(
|
||||||
|
PlatformChannel.execute,
|
||||||
|
executableName
|
||||||
|
);
|
||||||
|
resolve(id);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_openDefaultBrowser(
|
||||||
|
url: string,
|
||||||
|
options?: {
|
||||||
|
name?: string;
|
||||||
|
features?: string;
|
||||||
|
replace?: boolean;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(
|
||||||
|
PlatformChannel.openDefaultBrowser,
|
||||||
|
url,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_readFromClipboard(): Promise<{
|
||||||
|
text?: string;
|
||||||
|
rtf?: string;
|
||||||
|
html?: string;
|
||||||
|
image?: Buffer;
|
||||||
|
imageDataUrl?: string;
|
||||||
|
}> {
|
||||||
|
return new Promise<{
|
||||||
|
text?: string;
|
||||||
|
rtf?: string;
|
||||||
|
html?: string;
|
||||||
|
image?: Buffer;
|
||||||
|
imageDataUrl?: string;
|
||||||
|
}>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
PlatformChannel.readFromClipboard
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
file_save(
|
||||||
|
buffer: Buffer,
|
||||||
|
fileName: string,
|
||||||
|
mimeType: string,
|
||||||
|
path?: string
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise<string>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
FileChannel.save,
|
||||||
|
buffer,
|
||||||
|
fileName,
|
||||||
|
mimeType,
|
||||||
|
path
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_read(path: string): Promise<Buffer> {
|
||||||
|
return new Promise<Buffer>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(FileChannel.read, path);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_openFolder(folderPath?: string, make?: boolean): Promise<boolean> {
|
||||||
|
return new Promise<boolean>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
FileChannel.openFolder,
|
||||||
|
folderPath,
|
||||||
|
make
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_openItem(filePath?: string): Promise<boolean> {
|
||||||
|
return new Promise<boolean>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
FileChannel.openItem,
|
||||||
|
filePath
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_path(
|
||||||
|
name: 'home' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos',
|
||||||
|
...appendPaths: string[]
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise<string>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
FileChannel.path,
|
||||||
|
name,
|
||||||
|
appendPaths
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_selectForOpen(option: {
|
||||||
|
title?: string;
|
||||||
|
defaultPath?: string;
|
||||||
|
filters?: {
|
||||||
|
extensions: string[];
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
properties?: Array<
|
||||||
|
| 'openFile'
|
||||||
|
| 'openDirectory'
|
||||||
|
| 'multiSelections'
|
||||||
|
| 'showHiddenFiles'
|
||||||
|
| 'createDirectory'
|
||||||
|
| 'promptToCreate'
|
||||||
|
| 'noResolveAliases'
|
||||||
|
| 'treatPackageAsDirectory'
|
||||||
|
>;
|
||||||
|
message?: string;
|
||||||
|
}): Promise<string> {
|
||||||
|
return new Promise<string>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
FileChannel.selectForOpen,
|
||||||
|
option
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_selectForSave(option: {
|
||||||
|
title?: string;
|
||||||
|
defaultPath?: string;
|
||||||
|
filters?: {
|
||||||
|
extensions: string[];
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
message?: string;
|
||||||
|
}): Promise<{
|
||||||
|
canceled: boolean;
|
||||||
|
filePath: string;
|
||||||
|
}> {
|
||||||
|
return new Promise<{
|
||||||
|
canceled: boolean;
|
||||||
|
filePath: string;
|
||||||
|
}>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
FileChannel.selectForSave,
|
||||||
|
option
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window_state(): Promise<WindowState> {
|
||||||
|
return new Promise<WindowState>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(WindowChannel.state);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window_onState$(): BehaviorSubject<WindowState> {
|
||||||
|
if (!this._window_onStateSubject) {
|
||||||
|
this.ipcRenderer.on(
|
||||||
|
WindowChannel.onState$,
|
||||||
|
(event: any, state: WindowState) => {
|
||||||
|
if (!!this._window_onStateSubject) {
|
||||||
|
this._window_onStateSubject.next(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.window_state().then((state) => {
|
||||||
|
this._window_onStateSubject.next(state);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.window_onState$();
|
||||||
|
}
|
||||||
|
window_focused(): Promise<boolean> {
|
||||||
|
return new Promise<boolean>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(WindowChannel.focused);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window_onFocus$(): BehaviorSubject<boolean> {
|
||||||
|
if (!this._window_onFocusSubject) {
|
||||||
|
this.ipcRenderer.on(
|
||||||
|
WindowChannel.onFocus$,
|
||||||
|
(event: any, focused: boolean) => {
|
||||||
|
if (!!this._window_onFocusSubject) {
|
||||||
|
this._window_onFocusSubject.next(focused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
this.window_focused().then((focused) => {
|
||||||
|
this._window_onFocusSubject.next(focused);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.window_onFocus$();
|
||||||
|
}
|
||||||
|
window_close(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const currentWindow = this.remote.getCurrentWindow();
|
||||||
|
if (!!currentWindow) {
|
||||||
|
currentWindow.hide();
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window_minimize(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const currentWindow = this.remote.getCurrentWindow();
|
||||||
|
if (!!currentWindow) {
|
||||||
|
currentWindow.minimize();
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window_maximize(altKey: boolean = false): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const currentWindow = this.remote.getCurrentWindow();
|
||||||
|
if (!!currentWindow) {
|
||||||
|
if ('darwin' === this.platform) {
|
||||||
|
if (altKey) {
|
||||||
|
if (currentWindow.isMaximizable()) {
|
||||||
|
if (currentWindow.isMaximized()) {
|
||||||
|
currentWindow.unmaximize();
|
||||||
|
} else {
|
||||||
|
currentWindow.maximize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
currentWindow.setFullScreen(!currentWindow.isFullScreen());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (currentWindow.isMaximized()) {
|
||||||
|
currentWindow.unmaximize();
|
||||||
|
} else {
|
||||||
|
currentWindow.maximize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
idle_startCheck(limitTime: number): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const started = await this.ipcRenderer.invoke(IdleChannel.started);
|
||||||
|
if (started) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.ipcRenderer.invoke(IdleChannel.startCheck, limitTime);
|
||||||
|
|
||||||
|
this.ipcRenderer.on(
|
||||||
|
IdleChannel.onState$,
|
||||||
|
this._idle_onState$Callback.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_idle_onState$Callback(event: any, idleState: WindowIdle) {
|
||||||
|
if (!!this._idle_onStateSubject) {
|
||||||
|
this._idle_onStateSubject.next(idleState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idle_onState$(): BehaviorSubject<WindowIdle> {
|
||||||
|
return super.idle_onState$();
|
||||||
|
}
|
||||||
|
idle_stopCheck(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const started = await this.ipcRenderer.invoke(IdleChannel.started);
|
||||||
|
if (!started) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.ipcRenderer.invoke(IdleChannel.stopCheck);
|
||||||
|
|
||||||
|
this.ipcRenderer.off(
|
||||||
|
IdleChannel.onState$,
|
||||||
|
this._idle_onState$Callback.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
idle_changeLimitTime(limitTime: number): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(IdleChannel.changeLimitTime, limitTime);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app_version(): Promise<string> {
|
||||||
|
return new Promise<any>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(AppChannel.version);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postInit(): Promise<AppInitInfo> {
|
||||||
|
return new Promise<AppInitInfo>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(AppChannel.postInit);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postLogin(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.postLogin);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postLogout(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.postLogout);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postDestroy(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.postDestroy);
|
||||||
|
|
||||||
|
for (const channel of listenersOfChannel) {
|
||||||
|
this.ipcRenderer.removeAllListeners(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app_changeAutoLaunch(autoLaunch: boolean): Promise<boolean> {
|
||||||
|
return new Promise<boolean>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const data = await this.ipcRenderer.invoke(
|
||||||
|
AppChannel.changeAutoLaunch,
|
||||||
|
autoLaunch
|
||||||
|
);
|
||||||
|
resolve(data);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_showNotify(req: NotificationRequest): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.showNotify, req);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_closeAllNotify(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.closeAllNotify);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_checkForUpdates(currentVersion: string): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(
|
||||||
|
AppChannel.checkForUpdates,
|
||||||
|
currentVersion
|
||||||
|
);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_applyInstantUpdates(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.applyInstantUpdates);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_startCheckForUpdate(config: UpdateCheckConfig): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const started = await this.ipcRenderer.invoke(
|
||||||
|
AppChannel.startedCheckForUpdate
|
||||||
|
);
|
||||||
|
if (started) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.startCheckForUpdate, config);
|
||||||
|
|
||||||
|
this.ipcRenderer.on(
|
||||||
|
AppChannel.onUpdate$,
|
||||||
|
this._app_onUpdate$Callback.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_app_onUpdate$Callback(event: any, info: UpdateInfo) {
|
||||||
|
if (!!this._app_onUpdateSubject) {
|
||||||
|
this._app_onUpdateSubject.next(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app_onUpdate$(): BehaviorSubject<UpdateInfo> {
|
||||||
|
return super.app_onUpdate$();
|
||||||
|
}
|
||||||
|
app_stopCheckForUpdate(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const started = await this.ipcRenderer.invoke(
|
||||||
|
AppChannel.startedCheckForUpdate
|
||||||
|
);
|
||||||
|
if (!started) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.stopCheckForUpdate);
|
||||||
|
|
||||||
|
this.ipcRenderer.off(
|
||||||
|
AppChannel.onUpdate$,
|
||||||
|
this._app_onUpdate$Callback.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app_exit(): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(AppChannel.exit);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app_onLogout$(): Subject<void> {
|
||||||
|
if (!this._app_onLogoutSubject) {
|
||||||
|
this.ipcRenderer.on(AppChannel.onLogout$, (event: any) => {
|
||||||
|
if (!!this._app_onLogoutSubject) {
|
||||||
|
this._app_onLogoutSubject.next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.app_onLogout$();
|
||||||
|
}
|
||||||
|
app_onStatus$(): BehaviorSubject<StatusCode> {
|
||||||
|
if (!this._app_onStatusSubject) {
|
||||||
|
this.ipcRenderer.on(
|
||||||
|
AppChannel.onStatus$,
|
||||||
|
(event: any, code: StatusCode) => {
|
||||||
|
if (!!this._app_onStatusSubject) {
|
||||||
|
this._app_onStatusSubject.next(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.app_onStatus$();
|
||||||
|
}
|
||||||
|
app_onShowSetting$(): Subject<void> {
|
||||||
|
if (!this._app_onShowSettingSubject) {
|
||||||
|
this.ipcRenderer.on(AppChannel.onShowSetting$, (event: any) => {
|
||||||
|
if (!!this._app_onShowSettingSubject) {
|
||||||
|
this._app_onShowSettingSubject.next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.app_onShowSetting$();
|
||||||
|
}
|
||||||
|
|
||||||
|
app_onExit$(): Subject<void> {
|
||||||
|
if (!this._app_onExitSubject) {
|
||||||
|
this.ipcRenderer.on(AppChannel.onExit$, (event: any) => {
|
||||||
|
if (!!this._app_onExitSubject) {
|
||||||
|
this._app_onExitSubject.next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.app_onExit$();
|
||||||
|
}
|
||||||
|
|
||||||
|
chat_openRoom(openUrl: string): Promise<void> {
|
||||||
|
return new Promise<void>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
await this.ipcRenderer.invoke(ChatChannel.openRoom, openUrl);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
chat_onOpen$(): Subject<string> {
|
||||||
|
if (!this._chat_onOpenSubject) {
|
||||||
|
this.ipcRenderer.on(ChatChannel.onOpen$, (event: any, seq: string) => {
|
||||||
|
if (!!this._chat_onOpenSubject) {
|
||||||
|
this._chat_onOpenSubject.next(seq);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.chat_onOpen$();
|
||||||
|
}
|
||||||
|
|
||||||
|
message_onOpen$(): Subject<string> {
|
||||||
|
if (!this._message_onOpenSubject) {
|
||||||
|
this.ipcRenderer.on(MessageChannel.onOpen$, (event: any, seq: string) => {
|
||||||
|
if (!!this._message_onOpenSubject) {
|
||||||
|
this._message_onOpenSubject.next(seq);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.message_onOpen$();
|
||||||
|
}
|
||||||
|
}
|
65
projects/native/src/lib/types/channel.type.ts
Normal file
65
projects/native/src/lib/types/channel.type.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
export enum PlatformChannel {
|
||||||
|
networkInfo = 'ucap::native::platform::networkInfo',
|
||||||
|
execute = 'ucap::native::platform::execute',
|
||||||
|
openDefaultBrowser = 'ucap::native::platform::openDefaultBrowser',
|
||||||
|
readFromClipboard = 'ucap::native::platform::readFromClipboard'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum FileChannel {
|
||||||
|
save = 'ucap::native::file::save',
|
||||||
|
read = 'ucap::native::file::read',
|
||||||
|
openFolder = 'ucap::native::file::openFolder',
|
||||||
|
openItem = 'ucap::native::file::openItem',
|
||||||
|
path = 'ucap::native::file::path',
|
||||||
|
selectForOpen = 'ucap::native::file::selectForOpen',
|
||||||
|
selectForSave = 'ucap::native::file::selectForSave'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WindowChannel {
|
||||||
|
close = 'ucap::native::window::close',
|
||||||
|
minimize = 'ucap::native::window::minimize',
|
||||||
|
maximize = 'ucap::native::window::maximize',
|
||||||
|
state = 'ucap::native::window::state',
|
||||||
|
onState$ = 'ucap::native::window::onState$',
|
||||||
|
focused = 'ucap::native::window::focused',
|
||||||
|
onFocus$ = 'ucap::native::window::onFocus$'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum IdleChannel {
|
||||||
|
started = 'ucap::native::idle::started',
|
||||||
|
startCheck = 'ucap::native::idle::startCheck',
|
||||||
|
onState$ = 'ucap::native::idle::onState$',
|
||||||
|
stopCheck = 'ucap::native::idle::stopCheck',
|
||||||
|
changeLimitTime = 'ucap::native::idle::changeLimitTime'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AppChannel {
|
||||||
|
version = 'ucap::native::app::version',
|
||||||
|
postInit = 'ucap::native::app::postInit',
|
||||||
|
postLogin = 'ucap::native::app::postLogin',
|
||||||
|
postLogout = 'ucap::native::app::postLogout',
|
||||||
|
postDestroy = 'ucap::native::app::postDestroy',
|
||||||
|
changeAutoLaunch = 'ucap::native::app::changeAutoLaunch',
|
||||||
|
showNotify = 'ucap::native::app::showNotify',
|
||||||
|
closeAllNotify = 'ucap::native::app::closeAllNotify',
|
||||||
|
checkForUpdates = 'ucap::native::app::checkForUpdates',
|
||||||
|
applyInstantUpdates = 'ucap::native::app::applyInstantUpdates',
|
||||||
|
startedCheckForUpdate = 'ucap::native::app::startedCheckForUpdate',
|
||||||
|
startCheckForUpdate = 'ucap::native::app::startCheckForUpdate',
|
||||||
|
stopCheckForUpdate = 'ucap::native::app::stopCheckForUpdate',
|
||||||
|
onUpdate$ = 'ucap::native::app::onUpdate$',
|
||||||
|
exit = 'ucap::native::app::exit',
|
||||||
|
onLogout$ = 'ucap::native::app::onLogout$',
|
||||||
|
onStatus$ = 'ucap::native::app::onStatus$',
|
||||||
|
onShowSetting$ = 'ucap::native::app::onShowSetting$',
|
||||||
|
onExit$ = 'ucap::native::app::onExit$'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ChatChannel {
|
||||||
|
openRoom = 'ucap::native::chat::openRoom',
|
||||||
|
onOpen$ = 'ucap::native::chat::onOpen$'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum MessageChannel {
|
||||||
|
onOpen$ = 'ucap::native::message::onOpen$'
|
||||||
|
}
|
7
projects/native/src/public-api.ts
Normal file
7
projects/native/src/public-api.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/*
|
||||||
|
* Public API Surface of electron-native
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './lib/types/channel.type';
|
||||||
|
|
||||||
|
export * from './lib/services/electron-native.service';
|
12
projects/native/tsconfig.lib.json
Normal file
12
projects/native/tsconfig.lib.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/lib",
|
||||||
|
"target": "es2015",
|
||||||
|
"declaration": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": [],
|
||||||
|
"lib": ["dom", "es2018"]
|
||||||
|
},
|
||||||
|
"exclude": ["src/test.ts", "**/*.spec.ts"]
|
||||||
|
}
|
3
projects/native/tsconfig.lib.prod.json
Normal file
3
projects/native/tsconfig.lib.prod.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.lib.json"
|
||||||
|
}
|
9
projects/native/tsconfig.spec.json
Normal file
9
projects/native/tsconfig.spec.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/spec",
|
||||||
|
"types": ["jasmine", "node"]
|
||||||
|
},
|
||||||
|
"files": ["src/test.ts"],
|
||||||
|
"include": ["**/*.spec.ts", "**/*.d.ts"]
|
||||||
|
}
|
3
projects/native/tslint.json
Normal file
3
projects/native/tslint.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tslint.json"
|
||||||
|
}
|
7
projects/native/ucap-package.json
Normal file
7
projects/native/ucap-package.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"dest": "../../dist/native",
|
||||||
|
"docDest": "../../docs/native",
|
||||||
|
"lib": {
|
||||||
|
"entryFile": "src/public-api.ts"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/electron-notify-window",
|
"name": "@ucap/electron-notify-window",
|
||||||
"version": "0.0.1",
|
"version": "0.0.13",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {
|
"peerDependencies": {
|
||||||
"@ucap/electron-core": "~0.0.1",
|
"@ucap/electron-core": "~0.0.1",
|
||||||
"electron": "^8.0.0",
|
"electron": "^9.0.3",
|
||||||
"electron-log": "^4.1.0",
|
"electron-log": "^4.2.1",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^9.0.1",
|
||||||
"rxjs": "^6.5.4"
|
"rxjs": "^6.5.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { BrowserWindowConstructorOptions } from 'electron';
|
import * as Electron from 'electron';
|
||||||
|
|
||||||
export interface NotifyWindowOptions {
|
export interface NotifyWindowOptions {
|
||||||
width?: number;
|
width?: number;
|
||||||
|
@ -32,7 +32,7 @@ export interface NotifyWindowOptions {
|
||||||
defaultStyleText?: {
|
defaultStyleText?: {
|
||||||
[attribute: string]: any;
|
[attribute: string]: any;
|
||||||
};
|
};
|
||||||
defaultWindow?: BrowserWindowConstructorOptions;
|
defaultWindow?: Electron.BrowserWindowConstructorOptions;
|
||||||
templatePath?: string;
|
templatePath?: string;
|
||||||
htmlTemplate?: string;
|
htmlTemplate?: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import fse from 'fs-extra';
|
import fse from 'fs-extra';
|
||||||
|
import * as Electron from 'electron';
|
||||||
import log from 'electron-log';
|
import log from 'electron-log';
|
||||||
|
|
||||||
import { WebContentsChannel } from '@ucap/electron-core';
|
import { WebContentsChannel } from '@ucap/electron-core';
|
||||||
|
@ -9,7 +10,6 @@ import {
|
||||||
NotifyWindowOptions,
|
NotifyWindowOptions,
|
||||||
DefaultNotifyWindowOptions
|
DefaultNotifyWindowOptions
|
||||||
} from '../models/notify-window-options';
|
} from '../models/notify-window-options';
|
||||||
import { screen, BrowserWindow, ipcMain, IpcMainEvent, shell } from 'electron';
|
|
||||||
import { NotifyWindow } from '../models/notify-window';
|
import { NotifyWindow } from '../models/notify-window';
|
||||||
import { NotifyWindowEventType } from '../types/event.type';
|
import { NotifyWindowEventType } from '../types/event.type';
|
||||||
import { Channel } from '../types/channel.type';
|
import { Channel } from '../types/channel.type';
|
||||||
|
@ -28,18 +28,18 @@ interface ENDimension {
|
||||||
}
|
}
|
||||||
|
|
||||||
class BrowserWindowPooler {
|
class BrowserWindowPooler {
|
||||||
private readonly inactiveWindows: BrowserWindow[];
|
private readonly inactiveWindows: Electron.BrowserWindow[];
|
||||||
|
|
||||||
constructor(private readonly minSize: number) {
|
constructor(private readonly minSize: number) {
|
||||||
this.minSize = 0 > this.minSize ? 0 : this.minSize;
|
this.minSize = 0 > this.minSize ? 0 : this.minSize;
|
||||||
this.inactiveWindows = [];
|
this.inactiveWindows = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getLength() {
|
get length() {
|
||||||
return this.inactiveWindows.length;
|
return this.inactiveWindows.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
push(...items: BrowserWindow[]): number {
|
push(...items: Electron.BrowserWindow[]): number {
|
||||||
const length = this.inactiveWindows.push(...items);
|
const length = this.inactiveWindows.push(...items);
|
||||||
|
|
||||||
if (this.minSize < length) {
|
if (this.minSize < length) {
|
||||||
|
@ -48,7 +48,7 @@ class BrowserWindowPooler {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
pop(): BrowserWindow {
|
pop(): Electron.BrowserWindow {
|
||||||
if (!this.inactiveWindows || 0 === this.inactiveWindows.length) {
|
if (!this.inactiveWindows || 0 === this.inactiveWindows.length) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ export class NotifyWindowService {
|
||||||
private firstPosition: ENPoint;
|
private firstPosition: ENPoint;
|
||||||
private lowerRightCornerPosition: ENPoint;
|
private lowerRightCornerPosition: ENPoint;
|
||||||
private maxVisibleNotifications: number;
|
private maxVisibleNotifications: number;
|
||||||
private activeNotifications: BrowserWindow[];
|
private activeNotifications: Electron.BrowserWindow[];
|
||||||
private browserWindowPooler: BrowserWindowPooler;
|
private browserWindowPooler: BrowserWindowPooler;
|
||||||
|
|
||||||
private notificationQueue: NotifyWindow[];
|
private notificationQueue: NotifyWindow[];
|
||||||
|
@ -103,7 +103,7 @@ export class NotifyWindowService {
|
||||||
this.setupEvents();
|
this.setupEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
setOptions(options: NotifyWindowOptions) {
|
set options(options: NotifyWindowOptions) {
|
||||||
if (!!options) {
|
if (!!options) {
|
||||||
this.customOptions = {
|
this.customOptions = {
|
||||||
...this.customOptions,
|
...this.customOptions,
|
||||||
|
@ -113,18 +113,18 @@ export class NotifyWindowService {
|
||||||
this.calcDimensions();
|
this.calcDimensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptions(): NotifyWindowOptions {
|
get options(): NotifyWindowOptions {
|
||||||
return this.customOptions;
|
return this.customOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
setTemplatePath(templatePath: string) {
|
set templatePath(templatePath: string) {
|
||||||
if (!!templatePath) {
|
if (!!templatePath) {
|
||||||
this.customOptions.templatePath = templatePath;
|
this.customOptions.templatePath = templatePath;
|
||||||
this.updateTemplatePath();
|
this.updateTemplatePath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getTemplatePath(): string {
|
get templatePath(): string {
|
||||||
if (!this.templateUrl) {
|
if (!this.templateUrl) {
|
||||||
this.updateTemplatePath();
|
this.updateTemplatePath();
|
||||||
}
|
}
|
||||||
|
@ -143,13 +143,13 @@ export class NotifyWindowService {
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
this.animationQueue.clear();
|
this.animationQueue.clear();
|
||||||
this.activeNotifications.forEach(window => window.close());
|
this.activeNotifications.forEach((window) => window.close());
|
||||||
this.browserWindowPooler.closeAll();
|
this.browserWindowPooler.closeAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
closeAll(): void {
|
closeAll(): void {
|
||||||
this.animationQueue.clear();
|
this.animationQueue.clear();
|
||||||
this.activeNotifications.forEach(window => window.close());
|
this.activeNotifications.forEach((window) => window.close());
|
||||||
this.browserWindowPooler.closeAll();
|
this.browserWindowPooler.closeAll();
|
||||||
|
|
||||||
this.setup();
|
this.setup();
|
||||||
|
@ -161,7 +161,7 @@ export class NotifyWindowService {
|
||||||
this.firstPosition = { x: 0, y: 0 };
|
this.firstPosition = { x: 0, y: 0 };
|
||||||
this.activeNotifications = [];
|
this.activeNotifications = [];
|
||||||
this.browserWindowPooler = new BrowserWindowPooler(
|
this.browserWindowPooler = new BrowserWindowPooler(
|
||||||
this.getOptions().browserWindowPool.min
|
this.options.browserWindowPool.min
|
||||||
);
|
);
|
||||||
this.notificationQueue = [];
|
this.notificationQueue = [];
|
||||||
this.closedNotifications = new Map();
|
this.closedNotifications = new Map();
|
||||||
|
@ -169,7 +169,7 @@ export class NotifyWindowService {
|
||||||
|
|
||||||
this.animationQueue = new AnimationQueue();
|
this.animationQueue = new AnimationQueue();
|
||||||
|
|
||||||
const display = screen.getPrimaryDisplay();
|
const display = Electron.screen.getPrimaryDisplay();
|
||||||
|
|
||||||
this.lowerRightCornerPosition = {
|
this.lowerRightCornerPosition = {
|
||||||
x: display.bounds.x + display.workArea.x + display.workAreaSize.width,
|
x: display.bounds.x + display.workArea.x + display.workAreaSize.width,
|
||||||
|
@ -183,35 +183,45 @@ export class NotifyWindowService {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.maxVisibleNotifications =
|
this.maxVisibleNotifications =
|
||||||
this.getOptions().browserWindowPool.max < this.maxVisibleNotifications
|
this.options.browserWindowPool.max < this.maxVisibleNotifications
|
||||||
? this.getOptions().browserWindowPool.max
|
? this.options.browserWindowPool.max
|
||||||
: this.maxVisibleNotifications;
|
: this.maxVisibleNotifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupEvents(): void {
|
private setupEvents(): void {
|
||||||
const self = this;
|
const self = this;
|
||||||
ipcMain.on(
|
Electron.ipcMain.on(
|
||||||
Channel.close,
|
Channel.close,
|
||||||
(event: IpcMainEvent, windowId: number, notification: NotifyWindow) => {
|
(
|
||||||
|
event: Electron.IpcMainEvent,
|
||||||
|
windowId: number,
|
||||||
|
sNotification: string
|
||||||
|
) => {
|
||||||
|
const notification: NotifyWindow = JSON.parse(sNotification);
|
||||||
const onClose = self.buildCloseNotification(
|
const onClose = self.buildCloseNotification(
|
||||||
BrowserWindow.fromId(windowId),
|
Electron.BrowserWindow.fromId(windowId),
|
||||||
notification
|
notification
|
||||||
);
|
);
|
||||||
self.buildCloseNotificationSafely(onClose)('close');
|
self.buildCloseNotificationSafely(onClose)('close');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
ipcMain.on(
|
Electron.ipcMain.on(
|
||||||
Channel.click,
|
Channel.click,
|
||||||
(event: IpcMainEvent, windowId: number, notification: NotifyWindow) => {
|
(
|
||||||
|
event: Electron.IpcMainEvent,
|
||||||
|
windowId: number,
|
||||||
|
sNotification: string
|
||||||
|
) => {
|
||||||
|
const notification: NotifyWindow = JSON.parse(sNotification);
|
||||||
if (!!notification.url) {
|
if (!!notification.url) {
|
||||||
shell.openExternal(notification.url);
|
Electron.shell.openExternal(notification.url);
|
||||||
}
|
}
|
||||||
const notificationWindow = BrowserWindow.fromId(windowId);
|
const notificationWindow = Electron.BrowserWindow.fromId(windowId);
|
||||||
|
|
||||||
if (notificationWindow && notificationWindow[onClickNotifyWindow]) {
|
if (notificationWindow && notificationWindow[onClickNotifyWindow]) {
|
||||||
const onClose = self.buildCloseNotification(
|
const onClose = self.buildCloseNotification(
|
||||||
BrowserWindow.fromId(windowId),
|
Electron.BrowserWindow.fromId(windowId),
|
||||||
notification
|
notification
|
||||||
);
|
);
|
||||||
notificationWindow[onClickNotifyWindow]({
|
notificationWindow[onClickNotifyWindow]({
|
||||||
|
@ -275,7 +285,7 @@ export class NotifyWindowService {
|
||||||
const self = this;
|
const self = this;
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise<any>((resolve, reject) => {
|
||||||
if (this.activeNotifications.length < this.maxVisibleNotifications) {
|
if (this.activeNotifications.length < this.maxVisibleNotifications) {
|
||||||
self.getWindow().then(notificationWindow => {
|
self.getWindow().then((notificationWindow) => {
|
||||||
self.calcInsertPosition();
|
self.calcInsertPosition();
|
||||||
notificationWindow.setPosition(
|
notificationWindow.setPosition(
|
||||||
self.nextInsertPosition.x,
|
self.nextInsertPosition.x,
|
||||||
|
@ -324,7 +334,7 @@ export class NotifyWindowService {
|
||||||
|
|
||||||
notificationWindow.webContents.send(
|
notificationWindow.webContents.send(
|
||||||
Channel.browserWindowSetContents,
|
Channel.browserWindowSetContents,
|
||||||
notification
|
JSON.stringify(notification)
|
||||||
);
|
);
|
||||||
notificationWindow.showInactive();
|
notificationWindow.showInactive();
|
||||||
resolve(notificationWindow);
|
resolve(notificationWindow);
|
||||||
|
@ -337,7 +347,7 @@ export class NotifyWindowService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildCloseNotification(
|
private buildCloseNotification(
|
||||||
notificationWindow: BrowserWindow,
|
notificationWindow: Electron.BrowserWindow,
|
||||||
notification: NotifyWindow,
|
notification: NotifyWindow,
|
||||||
timeoutIdFunc?: () => number
|
timeoutIdFunc?: () => number
|
||||||
) {
|
) {
|
||||||
|
@ -349,7 +359,7 @@ export class NotifyWindowService {
|
||||||
|
|
||||||
if (self.closedNotifications.has(notification.id)) {
|
if (self.closedNotifications.has(notification.id)) {
|
||||||
self.closedNotifications.delete(notification.id);
|
self.closedNotifications.delete(notification.id);
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>((resolve) => {
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -409,24 +419,24 @@ export class NotifyWindowService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getWindow(): Promise<BrowserWindow> {
|
private getWindow(): Promise<Electron.BrowserWindow> {
|
||||||
const slef = this;
|
const slef = this;
|
||||||
return new Promise<BrowserWindow>((resolve, reject) => {
|
return new Promise<Electron.BrowserWindow>((resolve, reject) => {
|
||||||
if (0 < slef.browserWindowPooler.getLength()) {
|
if (0 < slef.browserWindowPooler.length) {
|
||||||
resolve(slef.browserWindowPooler.pop());
|
resolve(slef.browserWindowPooler.pop());
|
||||||
} else {
|
} else {
|
||||||
const windowProperties = slef.customOptions.defaultWindow;
|
const windowProperties = slef.customOptions.defaultWindow;
|
||||||
windowProperties.width = slef.customOptions.width;
|
windowProperties.width = slef.customOptions.width;
|
||||||
windowProperties.height = slef.customOptions.height;
|
windowProperties.height = slef.customOptions.height;
|
||||||
|
|
||||||
const notificationWindow = new BrowserWindow({
|
const notificationWindow = new Electron.BrowserWindow({
|
||||||
...windowProperties,
|
...windowProperties,
|
||||||
title: 'Notification'
|
title: 'Notification'
|
||||||
});
|
});
|
||||||
notificationWindow.setVisibleOnAllWorkspaces(true);
|
notificationWindow.setVisibleOnAllWorkspaces(true);
|
||||||
notificationWindow.loadURL(slef.getTemplatePath());
|
notificationWindow.loadURL(slef.templatePath);
|
||||||
notificationWindow.webContents.on(
|
notificationWindow.webContents.on(
|
||||||
WebContentsChannel.DidFinishLoad,
|
WebContentsChannel.didFinishLoad,
|
||||||
() => {
|
() => {
|
||||||
// Done
|
// Done
|
||||||
notificationWindow.webContents.send(
|
notificationWindow.webContents.send(
|
||||||
|
@ -437,7 +447,7 @@ export class NotifyWindowService {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
notificationWindow.webContents.on(
|
notificationWindow.webContents.on(
|
||||||
WebContentsChannel.DevtoolsOpened,
|
WebContentsChannel.devtoolsOpened,
|
||||||
() => {
|
() => {
|
||||||
notificationWindow.webContents.closeDevTools();
|
notificationWindow.webContents.closeDevTools();
|
||||||
}
|
}
|
||||||
|
@ -460,7 +470,7 @@ export class NotifyWindowService {
|
||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
aryNotificationPos.map(async index => {
|
aryNotificationPos.map(async (index) => {
|
||||||
await self.moveNotificationAnimation(index);
|
await self.moveNotificationAnimation(index);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,4 +2,12 @@
|
||||||
* Public API Surface of notification
|
* Public API Surface of notification
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from './lib/models/notify-window';
|
||||||
|
export * from './lib/models/notify-window-options';
|
||||||
|
|
||||||
|
export * from './lib/services/notify-window.service';
|
||||||
|
|
||||||
export * from './lib/types/channel.type';
|
export * from './lib/types/channel.type';
|
||||||
|
export * from './lib/types/event.type';
|
||||||
|
|
||||||
|
export * from './lib/utils/animation-queue';
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
"name": "@ucap/electron-updater-window",
|
"name": "@ucap/electron-updater-window",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {
|
"peerDependencies": {
|
||||||
"@ucap/electron-core": "~0.0.1",
|
"@ucap/electron-core": "~0.0.1",
|
||||||
"electron": "^8.0.0",
|
"electron": "^9.0.3",
|
||||||
"electron-log": "^4.1.0",
|
"electron-log": "^4.2.1",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^9.0.1",
|
||||||
"rxjs": "^6.5.4"
|
"rxjs": "^6.5.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {}
|
"devDependencies": {}
|
||||||
|
|
|
@ -19,6 +19,7 @@ async function buildForProduction(args) {
|
||||||
|
|
||||||
const distPath = path.join(projectPath, ucapPackageJson.dest);
|
const distPath = path.join(projectPath, ucapPackageJson.dest);
|
||||||
const docPath = path.join(projectPath, ucapPackageJson.docDest);
|
const docPath = path.join(projectPath, ucapPackageJson.docDest);
|
||||||
|
const packPath = path.join(rootPath, 'pack');
|
||||||
|
|
||||||
const webpackConfig = (overrideConfig, compilerOptions) => {
|
const webpackConfig = (overrideConfig, compilerOptions) => {
|
||||||
const commonConfig = {
|
const commonConfig = {
|
||||||
|
@ -150,7 +151,7 @@ async function buildForProduction(args) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}).catch(reason => {
|
}).catch((reason) => {
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -172,7 +173,7 @@ async function buildForProduction(args) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}).catch(reason => {
|
}).catch((reason) => {
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -211,7 +212,7 @@ async function buildForProduction(args) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}).catch(reason => {
|
}).catch((reason) => {
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -250,7 +251,7 @@ async function buildForProduction(args) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}).catch(reason => {
|
}).catch((reason) => {
|
||||||
reject(reason);
|
reject(reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -288,31 +289,28 @@ async function buildForProduction(args) {
|
||||||
const installPackage = () => {
|
const installPackage = () => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log(`${projectName}: installation for local started`);
|
console.log(`${projectName}: installation for local started`);
|
||||||
process.chdir(path.join(distPath));
|
|
||||||
execSync(`npm pack`, { stdio: 'inherit' });
|
|
||||||
process.chdir(path.join(rootPath));
|
|
||||||
const projectVersion = require(path.join(distPath, 'package.json'))
|
const projectVersion = require(path.join(distPath, 'package.json'))
|
||||||
.version;
|
.version;
|
||||||
|
|
||||||
execSync(
|
const packFileName = `ucap-electron-${projectName}-${projectVersion}.tgz`;
|
||||||
`npm install -D ${path.join(
|
|
||||||
distPath,
|
process.chdir(path.join(distPath));
|
||||||
`ucap-electron-${projectName}-${projectVersion}.tgz`
|
execSync(`npm pack`, { stdio: 'inherit' });
|
||||||
)}`,
|
|
||||||
|
fse.moveSync(
|
||||||
|
path.join(distPath, packFileName),
|
||||||
|
path.join(packPath, packFileName),
|
||||||
{
|
{
|
||||||
stdio: 'inherit'
|
overwrite: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
execSync(
|
process.chdir(path.join(rootPath));
|
||||||
`rimraf ${path.join(
|
|
||||||
distPath,
|
execSync(`npm install -D ${path.join(packPath, packFileName)}`, {
|
||||||
`ucap-electron-${projectName}-${projectVersion}.tgz`
|
|
||||||
)}`,
|
|
||||||
{
|
|
||||||
stdio: 'inherit'
|
stdio: 'inherit'
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`${projectName}: installation for local complete`);
|
console.log(`${projectName}: installation for local complete`);
|
||||||
resolve();
|
resolve();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user