sync
This commit is contained in:
parent
e1d820434e
commit
5fade48f5e
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
|||
|
||||
# compiled output
|
||||
/dist
|
||||
/pack
|
||||
/docs
|
||||
/tmp
|
||||
/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,
|
||||
"scripts": {
|
||||
"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:common": "node ./scripts/build.js common",
|
||||
"build:logger": "node ./scripts/build.js logger",
|
||||
"build:notify-window": "node ./scripts/build.js notify-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:common": "cd ./dist/common && npm publish",
|
||||
"publish:notify-window": "cd ./dist/core && npm publish",
|
||||
"publish:updater-window": "cd ./dist/core && npm publish"
|
||||
"publish:logger": "cd ./dist/logger && 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": {},
|
||||
"devDependencies": {
|
||||
"@tsed/core": "^5.44.11",
|
||||
"@tsed/di": "^5.44.11",
|
||||
"@types/fs-extra": "^8.1.0",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"@types/node": "^12.11.1",
|
||||
"@ucap/electron-core": "file:dist/core/ucap-electron-core-0.0.1.tgz",
|
||||
"concurrently": "^5.1.0",
|
||||
"electron": "^8.1.1",
|
||||
"electron-log": "^4.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"@ucap/core": "~0.0.15",
|
||||
"@ucap/domain-common": "~0.0.1",
|
||||
"@ucap/domain-organization": "~0.0.1",
|
||||
"@ucap/domain-status": "~0.0.1",
|
||||
"@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",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^6.5.4",
|
||||
"terser-webpack-plugin": "^2.3.5",
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
{
|
||||
"name": "@ucap/electron-common",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.32",
|
||||
"publishConfig": {
|
||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"@tsed/core": "^5.44.11",
|
||||
"@tsed/di": "^5.44.11",
|
||||
"electron": "^8.0.0",
|
||||
"peerDependencies": {
|
||||
"electron": "^9.0.3",
|
||||
"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
|
||||
*/
|
||||
|
||||
export * from './lib/app/decorators/app-settings';
|
||||
export * from './lib/app/decorators/electron-app';
|
||||
import * as AppApi from './lib/api/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 * from './lib/app/app';
|
||||
export { AppApi, AutoUpdaterApi, BrowserWindowApi, IpcMainApi, PlatformApi };
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
"name": "@ucap/electron-core",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.16",
|
||||
"publishConfig": {
|
||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {},
|
||||
"devDependencies": {}
|
||||
"peerDependencies": {}
|
||||
}
|
||||
|
|
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
|
||||
*/
|
||||
|
||||
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",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.13",
|
||||
"publishConfig": {
|
||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"peerDependencies": {
|
||||
"@ucap/electron-core": "~0.0.1",
|
||||
"electron": "^8.0.0",
|
||||
"electron-log": "^4.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"electron": "^9.0.3",
|
||||
"electron-log": "^4.2.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"rxjs": "^6.5.4"
|
||||
},
|
||||
"devDependencies": {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as path from 'path';
|
||||
import { BrowserWindowConstructorOptions } from 'electron';
|
||||
import * as Electron from 'electron';
|
||||
|
||||
export interface NotifyWindowOptions {
|
||||
width?: number;
|
||||
|
@ -32,7 +32,7 @@ export interface NotifyWindowOptions {
|
|||
defaultStyleText?: {
|
||||
[attribute: string]: any;
|
||||
};
|
||||
defaultWindow?: BrowserWindowConstructorOptions;
|
||||
defaultWindow?: Electron.BrowserWindowConstructorOptions;
|
||||
templatePath?: string;
|
||||
htmlTemplate?: string;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import url from 'url';
|
||||
import fse from 'fs-extra';
|
||||
import * as Electron from 'electron';
|
||||
import log from 'electron-log';
|
||||
|
||||
import { WebContentsChannel } from '@ucap/electron-core';
|
||||
|
@ -9,7 +10,6 @@ import {
|
|||
NotifyWindowOptions,
|
||||
DefaultNotifyWindowOptions
|
||||
} from '../models/notify-window-options';
|
||||
import { screen, BrowserWindow, ipcMain, IpcMainEvent, shell } from 'electron';
|
||||
import { NotifyWindow } from '../models/notify-window';
|
||||
import { NotifyWindowEventType } from '../types/event.type';
|
||||
import { Channel } from '../types/channel.type';
|
||||
|
@ -28,18 +28,18 @@ interface ENDimension {
|
|||
}
|
||||
|
||||
class BrowserWindowPooler {
|
||||
private readonly inactiveWindows: BrowserWindow[];
|
||||
private readonly inactiveWindows: Electron.BrowserWindow[];
|
||||
|
||||
constructor(private readonly minSize: number) {
|
||||
this.minSize = 0 > this.minSize ? 0 : this.minSize;
|
||||
this.inactiveWindows = [];
|
||||
}
|
||||
|
||||
getLength() {
|
||||
get length() {
|
||||
return this.inactiveWindows.length;
|
||||
}
|
||||
|
||||
push(...items: BrowserWindow[]): number {
|
||||
push(...items: Electron.BrowserWindow[]): number {
|
||||
const length = this.inactiveWindows.push(...items);
|
||||
|
||||
if (this.minSize < length) {
|
||||
|
@ -48,7 +48,7 @@ class BrowserWindowPooler {
|
|||
return length;
|
||||
}
|
||||
|
||||
pop(): BrowserWindow {
|
||||
pop(): Electron.BrowserWindow {
|
||||
if (!this.inactiveWindows || 0 === this.inactiveWindows.length) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ export class NotifyWindowService {
|
|||
private firstPosition: ENPoint;
|
||||
private lowerRightCornerPosition: ENPoint;
|
||||
private maxVisibleNotifications: number;
|
||||
private activeNotifications: BrowserWindow[];
|
||||
private activeNotifications: Electron.BrowserWindow[];
|
||||
private browserWindowPooler: BrowserWindowPooler;
|
||||
|
||||
private notificationQueue: NotifyWindow[];
|
||||
|
@ -103,7 +103,7 @@ export class NotifyWindowService {
|
|||
this.setupEvents();
|
||||
}
|
||||
|
||||
setOptions(options: NotifyWindowOptions) {
|
||||
set options(options: NotifyWindowOptions) {
|
||||
if (!!options) {
|
||||
this.customOptions = {
|
||||
...this.customOptions,
|
||||
|
@ -113,18 +113,18 @@ export class NotifyWindowService {
|
|||
this.calcDimensions();
|
||||
}
|
||||
|
||||
getOptions(): NotifyWindowOptions {
|
||||
get options(): NotifyWindowOptions {
|
||||
return this.customOptions;
|
||||
}
|
||||
|
||||
setTemplatePath(templatePath: string) {
|
||||
set templatePath(templatePath: string) {
|
||||
if (!!templatePath) {
|
||||
this.customOptions.templatePath = templatePath;
|
||||
this.updateTemplatePath();
|
||||
}
|
||||
}
|
||||
|
||||
getTemplatePath(): string {
|
||||
get templatePath(): string {
|
||||
if (!this.templateUrl) {
|
||||
this.updateTemplatePath();
|
||||
}
|
||||
|
@ -143,13 +143,13 @@ export class NotifyWindowService {
|
|||
|
||||
dispose(): void {
|
||||
this.animationQueue.clear();
|
||||
this.activeNotifications.forEach(window => window.close());
|
||||
this.activeNotifications.forEach((window) => window.close());
|
||||
this.browserWindowPooler.closeAll();
|
||||
}
|
||||
|
||||
closeAll(): void {
|
||||
this.animationQueue.clear();
|
||||
this.activeNotifications.forEach(window => window.close());
|
||||
this.activeNotifications.forEach((window) => window.close());
|
||||
this.browserWindowPooler.closeAll();
|
||||
|
||||
this.setup();
|
||||
|
@ -161,7 +161,7 @@ export class NotifyWindowService {
|
|||
this.firstPosition = { x: 0, y: 0 };
|
||||
this.activeNotifications = [];
|
||||
this.browserWindowPooler = new BrowserWindowPooler(
|
||||
this.getOptions().browserWindowPool.min
|
||||
this.options.browserWindowPool.min
|
||||
);
|
||||
this.notificationQueue = [];
|
||||
this.closedNotifications = new Map();
|
||||
|
@ -169,7 +169,7 @@ export class NotifyWindowService {
|
|||
|
||||
this.animationQueue = new AnimationQueue();
|
||||
|
||||
const display = screen.getPrimaryDisplay();
|
||||
const display = Electron.screen.getPrimaryDisplay();
|
||||
|
||||
this.lowerRightCornerPosition = {
|
||||
x: display.bounds.x + display.workArea.x + display.workAreaSize.width,
|
||||
|
@ -183,35 +183,45 @@ export class NotifyWindowService {
|
|||
);
|
||||
|
||||
this.maxVisibleNotifications =
|
||||
this.getOptions().browserWindowPool.max < this.maxVisibleNotifications
|
||||
? this.getOptions().browserWindowPool.max
|
||||
this.options.browserWindowPool.max < this.maxVisibleNotifications
|
||||
? this.options.browserWindowPool.max
|
||||
: this.maxVisibleNotifications;
|
||||
}
|
||||
|
||||
private setupEvents(): void {
|
||||
const self = this;
|
||||
ipcMain.on(
|
||||
Electron.ipcMain.on(
|
||||
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(
|
||||
BrowserWindow.fromId(windowId),
|
||||
Electron.BrowserWindow.fromId(windowId),
|
||||
notification
|
||||
);
|
||||
self.buildCloseNotificationSafely(onClose)('close');
|
||||
}
|
||||
);
|
||||
|
||||
ipcMain.on(
|
||||
Electron.ipcMain.on(
|
||||
Channel.click,
|
||||
(event: IpcMainEvent, windowId: number, notification: NotifyWindow) => {
|
||||
(
|
||||
event: Electron.IpcMainEvent,
|
||||
windowId: number,
|
||||
sNotification: string
|
||||
) => {
|
||||
const notification: NotifyWindow = JSON.parse(sNotification);
|
||||
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]) {
|
||||
const onClose = self.buildCloseNotification(
|
||||
BrowserWindow.fromId(windowId),
|
||||
Electron.BrowserWindow.fromId(windowId),
|
||||
notification
|
||||
);
|
||||
notificationWindow[onClickNotifyWindow]({
|
||||
|
@ -275,7 +285,7 @@ export class NotifyWindowService {
|
|||
const self = this;
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
if (this.activeNotifications.length < this.maxVisibleNotifications) {
|
||||
self.getWindow().then(notificationWindow => {
|
||||
self.getWindow().then((notificationWindow) => {
|
||||
self.calcInsertPosition();
|
||||
notificationWindow.setPosition(
|
||||
self.nextInsertPosition.x,
|
||||
|
@ -324,7 +334,7 @@ export class NotifyWindowService {
|
|||
|
||||
notificationWindow.webContents.send(
|
||||
Channel.browserWindowSetContents,
|
||||
notification
|
||||
JSON.stringify(notification)
|
||||
);
|
||||
notificationWindow.showInactive();
|
||||
resolve(notificationWindow);
|
||||
|
@ -337,7 +347,7 @@ export class NotifyWindowService {
|
|||
}
|
||||
|
||||
private buildCloseNotification(
|
||||
notificationWindow: BrowserWindow,
|
||||
notificationWindow: Electron.BrowserWindow,
|
||||
notification: NotifyWindow,
|
||||
timeoutIdFunc?: () => number
|
||||
) {
|
||||
|
@ -349,7 +359,7 @@ export class NotifyWindowService {
|
|||
|
||||
if (self.closedNotifications.has(notification.id)) {
|
||||
self.closedNotifications.delete(notification.id);
|
||||
return new Promise<void>(resolve => {
|
||||
return new Promise<void>((resolve) => {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
|
@ -409,24 +419,24 @@ export class NotifyWindowService {
|
|||
}
|
||||
}
|
||||
|
||||
private getWindow(): Promise<BrowserWindow> {
|
||||
private getWindow(): Promise<Electron.BrowserWindow> {
|
||||
const slef = this;
|
||||
return new Promise<BrowserWindow>((resolve, reject) => {
|
||||
if (0 < slef.browserWindowPooler.getLength()) {
|
||||
return new Promise<Electron.BrowserWindow>((resolve, reject) => {
|
||||
if (0 < slef.browserWindowPooler.length) {
|
||||
resolve(slef.browserWindowPooler.pop());
|
||||
} else {
|
||||
const windowProperties = slef.customOptions.defaultWindow;
|
||||
windowProperties.width = slef.customOptions.width;
|
||||
windowProperties.height = slef.customOptions.height;
|
||||
|
||||
const notificationWindow = new BrowserWindow({
|
||||
const notificationWindow = new Electron.BrowserWindow({
|
||||
...windowProperties,
|
||||
title: 'Notification'
|
||||
});
|
||||
notificationWindow.setVisibleOnAllWorkspaces(true);
|
||||
notificationWindow.loadURL(slef.getTemplatePath());
|
||||
notificationWindow.loadURL(slef.templatePath);
|
||||
notificationWindow.webContents.on(
|
||||
WebContentsChannel.DidFinishLoad,
|
||||
WebContentsChannel.didFinishLoad,
|
||||
() => {
|
||||
// Done
|
||||
notificationWindow.webContents.send(
|
||||
|
@ -437,7 +447,7 @@ export class NotifyWindowService {
|
|||
}
|
||||
);
|
||||
notificationWindow.webContents.on(
|
||||
WebContentsChannel.DevtoolsOpened,
|
||||
WebContentsChannel.devtoolsOpened,
|
||||
() => {
|
||||
notificationWindow.webContents.closeDevTools();
|
||||
}
|
||||
|
@ -460,7 +470,7 @@ export class NotifyWindowService {
|
|||
}
|
||||
|
||||
await Promise.all(
|
||||
aryNotificationPos.map(async index => {
|
||||
aryNotificationPos.map(async (index) => {
|
||||
await self.moveNotificationAnimation(index);
|
||||
})
|
||||
);
|
||||
|
|
|
@ -2,4 +2,12 @@
|
|||
* 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/event.type';
|
||||
|
||||
export * from './lib/utils/animation-queue';
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
"name": "@ucap/electron-updater-window",
|
||||
"version": "0.0.1",
|
||||
"publishConfig": {
|
||||
"registry": "http://10.81.13.221:8081/nexus/repository/npm-ucap/"
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"peerDependencies": {
|
||||
"@ucap/electron-core": "~0.0.1",
|
||||
"electron": "^8.0.0",
|
||||
"electron-log": "^4.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"electron": "^9.0.3",
|
||||
"electron-log": "^4.2.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"rxjs": "^6.5.4"
|
||||
},
|
||||
"devDependencies": {}
|
||||
|
|
|
@ -19,6 +19,7 @@ async function buildForProduction(args) {
|
|||
|
||||
const distPath = path.join(projectPath, ucapPackageJson.dest);
|
||||
const docPath = path.join(projectPath, ucapPackageJson.docDest);
|
||||
const packPath = path.join(rootPath, 'pack');
|
||||
|
||||
const webpackConfig = (overrideConfig, compilerOptions) => {
|
||||
const commonConfig = {
|
||||
|
@ -150,7 +151,7 @@ async function buildForProduction(args) {
|
|||
resolve();
|
||||
}
|
||||
);
|
||||
}).catch(reason => {
|
||||
}).catch((reason) => {
|
||||
reject(reason);
|
||||
});
|
||||
|
||||
|
@ -172,7 +173,7 @@ async function buildForProduction(args) {
|
|||
resolve();
|
||||
}
|
||||
);
|
||||
}).catch(reason => {
|
||||
}).catch((reason) => {
|
||||
reject(reason);
|
||||
});
|
||||
|
||||
|
@ -211,7 +212,7 @@ async function buildForProduction(args) {
|
|||
resolve();
|
||||
}
|
||||
);
|
||||
}).catch(reason => {
|
||||
}).catch((reason) => {
|
||||
reject(reason);
|
||||
});
|
||||
|
||||
|
@ -250,7 +251,7 @@ async function buildForProduction(args) {
|
|||
resolve();
|
||||
}
|
||||
);
|
||||
}).catch(reason => {
|
||||
}).catch((reason) => {
|
||||
reject(reason);
|
||||
});
|
||||
|
||||
|
@ -288,31 +289,28 @@ async function buildForProduction(args) {
|
|||
const installPackage = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
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'))
|
||||
.version;
|
||||
|
||||
execSync(
|
||||
`npm install -D ${path.join(
|
||||
distPath,
|
||||
`ucap-electron-${projectName}-${projectVersion}.tgz`
|
||||
)}`,
|
||||
const packFileName = `ucap-electron-${projectName}-${projectVersion}.tgz`;
|
||||
|
||||
process.chdir(path.join(distPath));
|
||||
execSync(`npm pack`, { stdio: 'inherit' });
|
||||
|
||||
fse.moveSync(
|
||||
path.join(distPath, packFileName),
|
||||
path.join(packPath, packFileName),
|
||||
{
|
||||
stdio: 'inherit'
|
||||
overwrite: true
|
||||
}
|
||||
);
|
||||
|
||||
execSync(
|
||||
`rimraf ${path.join(
|
||||
distPath,
|
||||
`ucap-electron-${projectName}-${projectVersion}.tgz`
|
||||
)}`,
|
||||
{
|
||||
process.chdir(path.join(rootPath));
|
||||
|
||||
execSync(`npm install -D ${path.join(packPath, packFileName)}`, {
|
||||
stdio: 'inherit'
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
console.log(`${projectName}: installation for local complete`);
|
||||
resolve();
|
||||
|
|
Loading…
Reference in New Issue
Block a user