refactoring

This commit is contained in:
병준 박 2019-11-11 15:53:39 +09:00
parent 3fc5fa254a
commit 20c49b7cdd
97 changed files with 1886 additions and 274 deletions

View File

@ -30,16 +30,102 @@
"styles": ["projects/ucap-webmessenger-app/src/styles.scss"],
"scripts": [],
"customWebpackConfig": {
"path": "./config/renderer.webpack.config.js",
"mergeStrategies": { "externals": "replace" }
"path": "./config/angular.webpack.config.js"
}
},
"configurations": {
"production": {
"browser-development": {
"fileReplacements": [
{
"replace": "projects/ucap-webmessenger-app/src/environments/environment.ts",
"with": "projects/ucap-webmessenger-app/src/environments/environment.prod.ts"
"with": "projects/ucap-webmessenger-app/src/environments/environment-browser.dev.ts"
}
],
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
},
"browser-production": {
"fileReplacements": [
{
"replace": "projects/ucap-webmessenger-app/src/environments/environment.ts",
"with": "projects/ucap-webmessenger-app/src/environments/environment-browser.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
},
"renderer-development": {
"fileReplacements": [
{
"replace": "projects/ucap-webmessenger-app/src/environments/environment.ts",
"with": "projects/ucap-webmessenger-app/src/environments/environment-renderer.dev.ts"
}
],
"optimization": false,
"outputHashing": "all",
"sourceMap": true,
"extractCss": true,
"namedChunks": false,
"aot": false,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
},
"renderer-production": {
"fileReplacements": [
{
"replace": "projects/ucap-webmessenger-app/src/environments/environment.ts",
"with": "projects/ucap-webmessenger-app/src/environments/environment-renderer.prod.ts"
}
],
"optimization": true,
@ -72,8 +158,17 @@
"browserTarget": "ucap-webmessenger-app:build"
},
"configurations": {
"production": {
"browserTarget": "ucap-webmessenger-app:build:production"
"browser-development": {
"browserTarget": "ucap-webmessenger-app:build:browser-development"
},
"browser-production": {
"browserTarget": "ucap-webmessenger-app:build:browser-production"
},
"renderer-development": {
"browserTarget": "ucap-webmessenger-app:build:renderer-development"
},
"renderer-production": {
"browserTarget": "ucap-webmessenger-app:build:renderer-production"
}
}
},
@ -84,7 +179,7 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"builder": "@angular-builders/custom-webpack:karma",
"options": {
"main": "projects/ucap-webmessenger-app/src/test.ts",
"polyfills": "projects/ucap-webmessenger-app/src/polyfills.ts",
@ -95,7 +190,10 @@
"projects/ucap-webmessenger-app/src/assets"
],
"styles": ["projects/ucap-webmessenger-app/src/styles.scss"],
"scripts": []
"scripts": [],
"customWebpackConfig": {
"path": "./config/angular.webpack.config.js"
}
}
},
"lint": {
@ -1182,72 +1280,6 @@
}
}
},
"ucap-webmessenger-native": {
"projectType": "library",
"root": "projects/ucap-webmessenger-native",
"sourceRoot": "projects/ucap-webmessenger-native/src",
"prefix": "ucap-native",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ucap-webmessenger-native/tsconfig.lib.json",
"project": "projects/ucap-webmessenger-native/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ucap-webmessenger-native/src/test.ts",
"tsConfig": "projects/ucap-webmessenger-native/tsconfig.spec.json",
"karmaConfig": "projects/ucap-webmessenger-native/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ucap-webmessenger-native/tsconfig.lib.json",
"projects/ucap-webmessenger-native/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"ucap-webmessenger-native-electron": {
"projectType": "library",
"root": "projects/ucap-webmessenger-native-electron",
"sourceRoot": "projects/ucap-webmessenger-native-electron/src",
"prefix": "ucap-native-electron",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.lib.json",
"project": "projects/ucap-webmessenger-native-electron/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ucap-webmessenger-native-electron/src/test.ts",
"tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.spec.json",
"karmaConfig": "projects/ucap-webmessenger-native-electron/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ucap-webmessenger-native-electron/tsconfig.lib.json",
"projects/ucap-webmessenger-native-electron/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"ucap-webmessenger-web-socket": {
"projectType": "library",
"root": "projects/ucap-webmessenger-web-socket",
@ -1413,6 +1445,72 @@
}
}
},
"ucap-webmessenger-native": {
"projectType": "library",
"root": "projects/ucap-webmessenger-native",
"sourceRoot": "projects/ucap-webmessenger-native/src",
"prefix": "ucap-native",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ucap-webmessenger-native/tsconfig.lib.json",
"project": "projects/ucap-webmessenger-native/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ucap-webmessenger-native/src/test.ts",
"tsConfig": "projects/ucap-webmessenger-native/tsconfig.spec.json",
"karmaConfig": "projects/ucap-webmessenger-native/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ucap-webmessenger-native/tsconfig.lib.json",
"projects/ucap-webmessenger-native/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"ucap-webmessenger-native-electron": {
"projectType": "library",
"root": "projects/ucap-webmessenger-native-electron",
"sourceRoot": "projects/ucap-webmessenger-native-electron/src",
"prefix": "ucap-native-electron",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.lib.json",
"project": "projects/ucap-webmessenger-native-electron/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ucap-webmessenger-native-electron/src/test.ts",
"tsConfig": "projects/ucap-webmessenger-native-electron/tsconfig.spec.json",
"karmaConfig": "projects/ucap-webmessenger-native-electron/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ucap-webmessenger-native-electron/tsconfig.lib.json",
"projects/ucap-webmessenger-native-electron/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"ucap-webmessenger-native-browser": {
"projectType": "library",
"root": "projects/ucap-webmessenger-native-browser",
@ -1445,76 +1543,6 @@
}
}
}
},
"ucap-webmessenger-electron-notification": {
"projectType": "library",
"root": "projects/ucap-webmessenger-electron-notification",
"sourceRoot": "projects/ucap-webmessenger-electron-notification/src",
"prefix": "ucap-electron-notification",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ucap-webmessenger-electron-notification/tsconfig.lib.json",
"project": "projects/ucap-webmessenger-electron-notification/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ucap-webmessenger-electron-notification/src/test.ts",
"tsConfig": "projects/ucap-webmessenger-electron-notification/tsconfig.spec.json",
"karmaConfig": "projects/ucap-webmessenger-electron-notification/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ucap-webmessenger-electron-notification/tsconfig.lib.json",
"projects/ucap-webmessenger-electron-notification/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
},
"ucap-webmessenger-electron-core": {
"projectType": "library",
"root": "projects/ucap-webmessenger-electron-core",
"sourceRoot": "projects/ucap-webmessenger-electron-core/src",
"prefix": "ucap-electron-core",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ucap-webmessenger-electron-core/tsconfig.lib.json",
"project": "projects/ucap-webmessenger-electron-core/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ucap-webmessenger-electron-core/src/test.ts",
"tsConfig": "projects/ucap-webmessenger-electron-core/tsconfig.spec.json",
"karmaConfig": "projects/ucap-webmessenger-electron-core/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ucap-webmessenger-electron-core/tsconfig.lib.json",
"projects/ucap-webmessenger-electron-core/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
]
}
}
}
}
},
"defaultProject": "ucap-webmessenger-app"

View File

@ -2,8 +2,18 @@ const path = require('path');
module.exports = (config, options) => {
const PRODUCTION = process.env.NODE_ENV === 'production';
const BROWSER = process.env.UCAP_ENV_RUNTIME === 'BROWSER';
config.target = 'electron-renderer';
console.log('BROWSER', BROWSER, config.target);
if (!BROWSER) {
config.target = 'electron-renderer';
} else {
config.target = 'web';
config.node = {
fs: 'empty'
};
}
config.resolve.alias = {
...config.resolve.alias,

View File

@ -1,4 +1,3 @@
import * as fs from 'fs';
import * as Path from 'path';
const projectRoot = Path.dirname(__dirname);

View File

@ -12,10 +12,16 @@ export const externals = [nodeExternals()];
// externals.push('devtron');
// }
const outputDir = 'dist/main';
const outputDir = 'dist/ucap-webmessenger-electron';
const mainConfig: webpack.Configuration = {
entry: { main: path.resolve(__dirname, '..', 'main/src/index') },
entry: {
main: path.resolve(
__dirname,
'..',
'electron-projects/ucap-webmessenger-electron/src/index'
)
},
target: 'electron-main',
mode: enviroments.__DEV__ ? 'development' : 'production',
devtool: 'source-map',
@ -32,7 +38,12 @@ const mainConfig: webpack.Configuration = {
{
test: /\.tsx?$/,
include: [
path.resolve(__dirname, '..', 'main/src'),
path.resolve(
__dirname,
'..',
'electron-projects/ucap-webmessenger-electron/src'
),
path.resolve(__dirname, '..', 'electron-projects'),
path.resolve(__dirname, '..', 'projects')
],
use: [
@ -43,7 +54,7 @@ const mainConfig: webpack.Configuration = {
configFileName: path.resolve(
__dirname,
'..',
'main/tsconfig.main.json'
'electron-projects/ucap-webmessenger-electron/tsconfig.electron.json'
)
}
}
@ -71,8 +82,9 @@ const mainConfig: webpack.Configuration = {
),
new CopyWebpackPlugin([
{
from: 'main/resources/**/*',
to: path.resolve(__dirname, '..', 'dist')
from: 'ucap-webmessenger-electron/resources/**/*',
to: path.resolve(__dirname, '..', 'dist'),
context: 'electron-projects'
}
])
],
@ -82,12 +94,12 @@ const mainConfig: webpack.Configuration = {
'@ucap-webmessenger/electron-core': path.resolve(
__dirname,
'..',
'projects/ucap-webmessenger-electron-core/src/public-api'
'electron-projects/ucap-webmessenger-electron-core/src/public-api'
),
'@ucap-webmessenger/electron-notification': path.resolve(
__dirname,
'..',
'projects/ucap-webmessenger-electron-notification/src/public-api'
'electron-projects/ucap-webmessenger-electron-notification/src/public-api'
),
'@ucap-webmessenger/native': path.resolve(
__dirname,
@ -98,6 +110,11 @@ const mainConfig: webpack.Configuration = {
__dirname,
'..',
'projects/ucap-webmessenger-native-electron/src/public-api'
),
'@ucap-webmessenger/electron': path.resolve(
__dirname,
'..',
'electron-projects/ucap-webmessenger-electron/src/public-api'
)
},
modules: [path.resolve(__dirname, '..', 'node_modules/')]

View File

@ -1,22 +1,24 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
frameworks: ['jasmine'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
require('karma-coverage-istanbul-reporter')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-electron-core'),
dir: require('path').join(
__dirname,
'../../coverage/ucap-webmessenger-electron-core'
),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},

View File

@ -0,0 +1,5 @@
{
"name": "@ucap-webmessenger/electron-core",
"version": "0.0.1",
"peerDependencies": {}
}

View File

@ -0,0 +1,13 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": ["dom", "es2018"]
},
"exclude": ["src/test.ts", "**/*.spec.ts"]
}

View 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"]
}

View File

@ -1,22 +1,24 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
frameworks: ['jasmine'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
require('karma-coverage-istanbul-reporter')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-electron-notification'),
dir: require('path').join(
__dirname,
'../../coverage/ucap-webmessenger-electron-notification'
),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},

View File

@ -1,5 +1,6 @@
import * as path from 'path';
import * as url from 'url';
import * as fse from 'fs-extra';
import { AnimationQueue } from '../utils/animation-queue';
import {
@ -211,19 +212,13 @@ export class ElectronNotificationService {
private updateTemplatePath() {
try {
import('fs')
.then(fs => {
fs.statSync(this.customOptions.templatePath).isFile();
fse.statSync(this.customOptions.templatePath).isFile();
this.templateUrl = url.format({
pathname: this.customOptions.templatePath,
protocol: 'file:',
slashes: true
});
})
.catch(reason => {
throw reason;
});
this.templateUrl = url.format({
pathname: this.customOptions.templatePath,
protocol: 'file:',
slashes: true
});
} catch (e) {
console.log(
'electron-notify: Could not find template ("' +

View 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"]
}

View File

@ -0,0 +1,24 @@
# UcapWebmessengerElectron
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.11.
## Code scaffolding
Run `ng generate component component-name --project ucap-webmessenger-electron` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ucap-webmessenger-electron`.
> Note: Don't forget to add `--project ucap-webmessenger-electron` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build ucap-webmessenger-electron` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build ucap-webmessenger-electron`, go to the dist folder `cd dist/ucap-webmessenger-electron` and run `npm publish`.
## Running unit tests
Run `ng test ucap-webmessenger-electron` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@ -0,0 +1,34 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(
__dirname,
'../../coverage/ucap-webmessenger-electron'
),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@ -0,0 +1,5 @@
{
"name": "@ucap-webmessenger/electron",
"version": "0.0.1",
"peerDependencies": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 643 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -0,0 +1,143 @@
'use strict';
const electron = require('electron');
const ipc = electron.ipcRenderer;
const winId = electron.remote.getCurrentWindow().id;
function setStyle(config) {
// Style it
let notiDoc = global.window.document;
let container = notiDoc.getElementById('container');
let appIcon = notiDoc.getElementById('appIcon');
let image = notiDoc.getElementById('image');
let close = notiDoc.getElementById('close');
let message = notiDoc.getElementById('message');
// Default style
setStyleOnDomElement(config.defaultStyleContainer, container);
// Size and radius
let style = {
height:
config.height -
2 * config.borderRadius -
2 * config.defaultStyleContainer.padding,
width:
config.width -
2 * config.borderRadius -
2 * config.defaultStyleContainer.padding,
borderRadius: config.borderRadius + 'px'
};
setStyleOnDomElement(style, container);
// Style appIcon or hide
if (config.appIcon) {
setStyleOnDomElement(config.defaultStyleAppIcon, appIcon);
appIcon.src = config.appIcon;
} else {
setStyleOnDomElement(
{
display: 'none'
},
appIcon
);
}
// Style image
setStyleOnDomElement(config.defaultStyleImage, image);
// Style close button
setStyleOnDomElement(config.defaultStyleClose, close);
// Remove margin from text p
setStyleOnDomElement(config.defaultStyleText, message);
}
function setContents(event, notificationObj) {
// sound
if (notificationObj.sound) {
// Check if file is accessible
try {
// If it's a local file, check it's existence
// Won't check remote files e.g. http://
if (
notificationObj.sound.match(/^file\:/) !== null ||
notificationObj.sound.match(/^\//) !== null
) {
let audio = new global.window.Audio(notificationObj.sound);
audio.play();
}
} catch (e) {
log(
'electron-notify: ERROR could not find sound file: ' +
notificationObj.sound.replace('file://', ''),
e,
e.stack
);
}
}
let notiDoc = global.window.document;
// Title
let titleDoc = notiDoc.getElementById('title');
titleDoc.innerHTML = notificationObj.title || '';
// message
let messageDoc = notiDoc.getElementById('message');
messageDoc.innerHTML = notificationObj.text || '';
// Image
let imageDoc = notiDoc.getElementById('image');
if (notificationObj.image) {
imageDoc.src = notificationObj.image;
} else {
setStyleOnDomElement({ display: 'none' }, imageDoc);
}
// Close button
let closeButton = notiDoc.getElementById('close');
closeButton.addEventListener('click', function(event) {
event.stopPropagation();
ipc.send('UCAP::ElectronNotification::close', winId, notificationObj);
});
// URL
let container = notiDoc.getElementById('container');
container.addEventListener('click', function() {
ipc.send('UCAP::ElectronNotification::click', winId, notificationObj);
});
}
function setStyleOnDomElement(styleObj, domElement) {
try {
for (let styleAttr in styleObj) {
domElement.style[styleAttr] = styleObj[styleAttr];
}
} catch (e) {
throw new Error(
'electron-notify: Could not set style on domElement',
styleObj,
domElement
);
}
}
function loadConfig(event, conf) {
setStyle(conf || {});
}
function reset() {
let notiDoc = global.window.document;
let container = notiDoc.getElementById('container');
let closeButton = notiDoc.getElementById('close');
// Remove event listener
let newContainer = container.cloneNode(true);
container.parentNode.replaceChild(newContainer, container);
let newCloseButton = closeButton.cloneNode(true);
closeButton.parentNode.replaceChild(newCloseButton, closeButton);
}
ipc.on('UCAP::ElectronNotification::BrowserWindowSetContents', setContents);
ipc.on('UCAP::ElectronNotification::loadConfig', loadConfig);
ipc.on('UCAP::ElectronNotification::reset', reset);
function log() {
console.log.apply(console, arguments);
}
delete global.require;
delete global.exports;
delete global.module;

View File

@ -0,0 +1,130 @@
html {
height: 100%;
overflow-y: scroll;
}
body {
position: relative;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
color: #333;
font-family: '나눔고딕', Malgun Gothic, '맑은고딕', Arial, Dotum, '돋움',
Gulim, '굴림';
font-size: 12px;
line-height: 18px !important;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body * {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
ul,
ol {
list-style: none;
margin: 0;
padding: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
img {
border: none;
}
a:link,
a:visited,
a:hover,
a:active {
text-decoration: none;
}
.noti_messege {
width: 340px;
height: 100px;
border: 1px solid #666;
background-color: #fff;
box-shadow: 0px 0px 3px 0px #e7e7e7;
}
.info {
position: relative;
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 16px 14px;
color: #fff;
}
.btn_close {
position: absolute;
z-index: 1;
right: 6px;
top: 6px;
width: 20px;
height: 20px;
background: url(../image/btn_close_gray.png) no-repeat 50% 50%;
}
.btn_close:hover {
opacity: 0.7;
}
.photo {
position: relative;
top: 0px;
right: 0;
bottom: 0;
left: 0;
margin: 4px 0;
width: 54px;
height: 54px;
border-radius: 50%;
background: #5bc1ff url(../image/img_nophoto_50.png) no-repeat 50% 50%;
border: 2px solid #ddd;
}
.info .profile {
position: absolute;
width: 60px;
text-align: center;
}
.photo img {
overflow: hidden;
width: 50px;
height: 50px;
border-radius: 50px;
}
.noti_messege .info .profile + div {
padding-left: 70px;
position: relative;
line-height: 180%;
height: 100%;
}
.sender {
font-size: 14px;
font-weight: bold;
margin-bottom: 4px;
color: #333;
width: 94%;
}
.sender .name {
color: #2e7fb5;
}
.message {
color: #666;
}
.ellipsis {
display: block;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal;
overflow: hidden;
}
.ellipsis_row2 {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
word-wrap: break-word;
line-height: 1.6em;
height: 3.2em;
}

View File

@ -0,0 +1,50 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<title>[개발]M Messenger - 메시지 알림</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charset="UTF-8" />
<link
type="text/css"
rel="stylesheet"
href="styles/noti_messege.css"
/>
</HEAD>
<style>
html,
body {
overflow-y: hidden;
overflow-x: hidden;
}
</style>
<BODY>
<div class="noti_messege" id="container">
<div class="info">
<a class="btn_close" id="close"></a>
<div class="profile">
<div class="photo">
<img src="" id="appIcon" />
<img
src=""
id="image"
onerror="this.src='image/img_nophoto_50.png';"
/>
</div>
</div>
<div>
<ul id="text">
<li class="sender ellipsis" id="title">
<span class="name">김 수안무 거북이와 두루미</span>님이 메시지를
보냈습니다.
</li>
<li class="message ellipsis_row2" id="message">
홍길동 대리(솔루션사업팀)홍길동 대리(솔루션사업팀)홍길동
대리(솔루션사업팀)홍길동 대리(솔루션사업팀)
</li>
</ul>
</div>
</div>
</div>
</BODY>
</HTML>

View File

@ -0,0 +1,229 @@
import * as path from 'path';
import * as url from 'url';
import { app, BrowserWindow, screen, ipcMain, IpcMainEvent } from 'electron';
import windowStateKeeper from 'electron-window-state';
import { EventEmitter } from 'events';
import { now } from '../util/now';
import { registerWindowStateChangedEvents } from '../lib/window-state';
import {
ElectronAppChannel,
ElectronBrowserWindowChannel,
ElectronWebContentsChannel
} from '@ucap-webmessenger/electron-core';
export class AppWindow {
private window: BrowserWindow | null = null;
private eventEmitter = new EventEmitter();
// tslint:disable-next-line: variable-name
private _loadTime: number | null = null;
// tslint:disable-next-line: variable-name
private _rendererReadyTime: number | null = null;
private minWidth = 960;
private minHeight = 660;
public constructor() {
const savedWindowState = windowStateKeeper({
defaultWidth: this.minWidth,
defaultHeight: this.minHeight
});
const windowOptions: Electron.BrowserWindowConstructorOptions = {
x: savedWindowState.x,
y: savedWindowState.y,
width: savedWindowState.width,
height: savedWindowState.height,
minWidth: this.minWidth,
minHeight: this.minHeight,
center: true,
// This fixes subpixel aliasing on Windows
// See https://github.com/atom/atom/commit/683bef5b9d133cb194b476938c77cc07fd05b972
backgroundColor: '#fff',
webPreferences: {
// Disable auxclick event
// See https://developers.google.com/web/updates/2016/10/auxclick
disableBlinkFeatures: 'Auxclick',
// Enable, among other things, the ResizeObserver
experimentalFeatures: true,
nodeIntegration: true
},
acceptFirstMouse: true,
icon: path.join(__dirname, 'resources/image', 'ico_64_64.png')
};
if (__DARWIN__) {
windowOptions.titleBarStyle = 'hidden';
} else if (__WIN32__) {
windowOptions.frame = false;
} else if (__LINUX__) {
windowOptions.icon = path.join(__dirname, 'static', 'icon-logo.png');
}
this.window = new BrowserWindow(windowOptions);
savedWindowState.manage(this.window);
let quitting = false;
app.on(ElectronAppChannel.BeforeQuit, () => {
quitting = true;
});
ipcMain.on(ElectronAppChannel.WillQuit, (event: IpcMainEvent) => {
quitting = true;
event.returnValue = true;
});
// on macOS, when the user closes the window we really just hide it. This
// lets us activate quickly and keep all our interesting logic in the
// renderer.
if (__DARWIN__) {
this.window.on(ElectronBrowserWindowChannel.Close, e => {
if (!quitting) {
e.preventDefault();
}
});
}
if (__WIN32__) {
// workaround for known issue with fullscreen-ing the app and restoring
// is that some Chromium API reports the incorrect bounds, so that it
// will leave a small space at the top of the screen on every other
// maximize
//
// adapted from https://github.com/electron/electron/issues/12971#issuecomment-403956396
//
// can be tidied up once https://github.com/electron/electron/issues/12971
// has been confirmed as resolved
this.window.once(ElectronBrowserWindowChannel.ReadyToShow, () => {
this.window.on(ElectronBrowserWindowChannel.Unmaximize, () => {
setTimeout(() => {
const bounds = this.window.getBounds();
bounds.width += 1;
this.window.setBounds(bounds);
bounds.width -= 1;
this.window.setBounds(bounds);
}, 5);
});
});
}
}
public load(): void {
let startLoad = 0;
this.window.webContents.once(
ElectronWebContentsChannel.DidStartLoading,
() => {
this._rendererReadyTime = null;
this._loadTime = null;
startLoad = now();
}
);
this.window.webContents.once(
ElectronWebContentsChannel.DidFinishLoad,
() => {
this.window.webContents.setVisualZoomLevelLimits(1, 1);
if (process.env.NODE_ENV === 'development') {
this.window.webContents.openDevTools();
}
this._loadTime = now() - startLoad;
}
);
this.window.webContents.on(ElectronWebContentsChannel.DidFailLoad, () => {
this.window.webContents.openDevTools();
this.window.show();
});
registerWindowStateChangedEvents(this.window);
if (__DEV__) {
this.window.loadURL('http://localhost:4200');
} else {
this.window.loadURL(
url.format({
pathname: path.join(
__dirname,
'..',
'ucap-webmessenger-app/index.html'
),
protocol: 'file:',
slashes: true
})
);
}
}
/** Is the page loaded and has the renderer signalled it's ready? */
private get rendererLoaded(): boolean {
return !!this.loadTime && !!this.rendererReadyTime;
}
public onClose(fn: () => void) {
this.window.on(ElectronBrowserWindowChannel.Closed, fn);
}
/**
* Register a function to call when the window is done loading. At that point
* the page has loaded and the renderer has signalled that it is ready.
*/
public onDidLoad(fn: () => void): EventEmitter {
return this.eventEmitter.on('did-load', fn);
}
public isMinimized() {
return this.window.isMinimized();
}
/** Is the window currently visible? */
public isVisible() {
return this.window.isVisible();
}
public restore() {
this.window.restore();
}
public focus() {
this.window.focus();
}
/** Show the window. */
public show() {
this.window.show();
}
/**
* Get the time (in milliseconds) spent loading the page.
*
* This will be `null` until `onDidLoad` is called.
*/
public get loadTime(): number | null {
return this._loadTime;
}
/**
* Get the time (in milliseconds) elapsed from the renderer being loaded to it
* signaling it was ready.
*
* This will be `null` until `onDidLoad` is called.
*/
public get rendererReadyTime(): number | null {
return this._rendererReadyTime;
}
public destroy() {
this.window.destroy();
}
public get browserWindow(): BrowserWindow | null {
return this.window;
}
}

View File

@ -0,0 +1 @@
export function showUncaughtException(isLaunchError: boolean, error: Error) {}

View File

@ -0,0 +1,18 @@
/* eslint-disable @typescript-eslint/interface-name-prefix */
/** Is the app running in dev mode? */
declare const __DEV__: boolean;
/** Is the app being built to run on Darwin? */
declare const __DARWIN__: boolean;
/** Is the app being built to run on Win32? */
declare const __WIN32__: boolean;
/** Is the app being built to run on Linux? */
declare const __LINUX__: boolean;
/**
* The currently executing process kind, this is specific to desktop
* and identifies the processes that we have.
*/
declare const __PROCESS_KIND__: 'main' | 'ui';

View File

@ -0,0 +1,304 @@
import { app, ipcMain, IpcMainEvent, remote } from 'electron';
import * as path from 'path';
import * as url from 'url';
import * as fse from 'fs-extra';
import * as fs from 'fs';
import { AppWindow } from './app/AppWindow';
import { now } from './util/now';
import { showUncaughtException } from './crash/show-uncaught-exception';
import {
UpdaterChannel,
FileChannel,
IdleStateChannel,
NotificationChannel
} from '@ucap-webmessenger/native-electron';
import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification';
import { root } from './util/root';
import { DefaultFolder } from './lib/default-folder';
import { FileUtil } from './lib/file-util';
import { IdleChecker } from './lib/idle-checker';
import { NotificationRequest } from '@ucap-webmessenger/native';
import { ElectronAppChannel } from '@ucap-webmessenger/electron-core';
let appWindow: AppWindow | null = null;
const launchTime = now();
let readyTime: number | null = null;
type OnDidLoadFn = (window: AppWindow) => void;
let onDidLoadFns: Array<OnDidLoadFn> | null = [];
let preventQuit = false;
let notificationService: ElectronNotificationService | null;
function handleUncaughtException(error: Error) {
preventQuit = true;
// If we haven't got a window we'll assume it's because
// we've just launched and haven't created it yet.
// It could also be because we're encountering an unhandled
// exception on shutdown but that's less likely and since
// this only affects the presentation of the crash dialog
// it's a safe assumption to make.
const isLaunchError = appWindow === null;
if (appWindow) {
appWindow.destroy();
appWindow = null;
}
showUncaughtException(isLaunchError, error);
}
function getUptimeInSeconds() {
return (now() - launchTime) / 1000;
}
process.on('uncaughtException', (error: Error) => {
// error = withSourceMappedStack(error);
// reportError(error, getExtraErrorContext());
handleUncaughtException(error);
});
let isDuplicateInstance = false;
const gotSingleInstanceLock = app.requestSingleInstanceLock();
isDuplicateInstance = !gotSingleInstanceLock;
let idle: IdleChecker | null;
app.on(ElectronAppChannel.SecondInstance, (event, args, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
if (appWindow) {
if (appWindow.isMinimized()) {
appWindow.restore();
}
if (!appWindow.isVisible()) {
appWindow.show();
}
appWindow.focus();
}
});
if (isDuplicateInstance) {
app.quit();
}
function createWindow() {
const window = new AppWindow();
if (__DEV__) {
// const {
// default: installExtension,
// REDUX_DEVTOOLS
// } = require('electron-devtools-installer');
import('electron-debug').then(ed => {
ed.default({ showDevTools: true });
});
import('electron-devtools-installer').then(edi => {
const ChromeLens = {
id: 'idikgljglpfilbhaboonnpnnincjhjkd',
electron: '>=1.2.1'
};
const extensions = [edi.REDUX_DEVTOOLS, ChromeLens];
for (const extension of extensions) {
try {
edi.default(extension);
} catch (e) {
console.log(e);
}
}
});
}
window.onClose(() => {
appWindow = null;
if (!__DARWIN__ && !preventQuit) {
app.quit();
}
});
window.onDidLoad(() => {
window.show();
const fns = onDidLoadFns;
onDidLoadFns = null;
for (const fn of fns) {
fn(window);
}
});
window.load();
appWindow = window;
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on(ElectronAppChannel.Ready, () => {
if (isDuplicateInstance) {
return;
}
readyTime = now() - launchTime;
createWindow();
notificationService = new ElectronNotificationService({
width: 340,
height: 100,
padding: 0,
borderRadius: 0,
// appIcon: iconPath,
displayTime: 5000,
defaultStyleContainer: {},
defaultStyleAppIcon: { display: 'none' },
defaultStyleImage: {},
defaultStyleClose: {},
defaultStyleText: {}
});
notificationService.options.defaultWindow.webPreferences.preload = path.join(
__dirname,
'resources/notification/preload.js'
);
notificationService.templatePath = path.join(
__dirname,
'resources/notification/template.html'
);
ipcMain.on('uncaught-exception', (event: IpcMainEvent, error: Error) => {
handleUncaughtException(error);
});
ipcMain.on(
'send-error-report',
(
event: IpcMainEvent,
{ error, extra }: { error: Error; extra: { [key: string]: string } }
) => {}
);
});
// Quit when all windows are closed.
app.on(ElectronAppChannel.WindowAllClosed, () => {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on(ElectronAppChannel.Activate, () => {
onDidLoad(window => {
window.show();
});
});
function onDidLoad(fn: OnDidLoadFn) {
if (onDidLoadFns) {
onDidLoadFns.push(fn);
} else {
if (appWindow) {
fn(appWindow);
}
}
}
ipcMain.on(UpdaterChannel.Check, (event: IpcMainEvent, ...args: any[]) => {
event.returnValue = false;
});
ipcMain.on(FileChannel.ReadFile, (event: IpcMainEvent, ...args: any[]) => {
try {
fse.readFile(root(args[0]), (err, data) => {
if (!!err) {
event.returnValue = null;
} else {
event.returnValue = new Blob([data]);
}
});
} catch (error) {
event.returnValue = null;
}
});
ipcMain.on(
FileChannel.SaveFile,
async (event: IpcMainEvent, ...args: any[]) => {
try {
const buffer: Buffer = args[0];
const fileName: string = args[1];
let savePath: string = path.join(
!!args[2] ? args[2] : DefaultFolder.downloads(),
fileName
);
savePath = await FileUtil.uniqueFileName(savePath);
fse.writeFile(savePath, buffer, err => {
if (!err) {
event.returnValue = savePath;
} else {
event.returnValue = undefined;
}
});
} catch (error) {
event.returnValue = undefined;
}
}
);
ipcMain.on(
IdleStateChannel.StartCheck,
(event: IpcMainEvent, ...args: any[]) => {
if (!!idle) {
idle.destoryChecker();
idle = null;
}
idle = new IdleChecker(appWindow.browserWindow); // default 10min
idle.startChecker();
}
);
ipcMain.on(
NotificationChannel.Notify,
(event: IpcMainEvent, ...args: any[]) => {
const noti: NotificationRequest = args[0];
notificationService.notify({
title: noti.title,
text: noti.contents,
image:
noti.image ||
path.join(__dirname, 'resources/notification/image/img_nophoto_50.png'),
sound: noti.useSound
? path.join(
'file://',
__dirname,
'resources/notification/sound/messageAlarm.mp3'
)
: '',
onClick: () => {
console.log('onClick');
}
});
}
);
ipcMain.on(
NotificationChannel.CloseAllNotify,
(event: IpcMainEvent, ...args: any[]) => {
console.log('Channel.closeAllNotify', args);
}
);

View File

@ -0,0 +1,37 @@
import * as os from 'os';
import { execSync } from 'child_process';
import * as fse from 'fs-extra';
export class DefaultFolder {
static downloads(): string {
switch (os.platform()) {
case 'win32':
return `${process.env.USERPROFILE}/Downloads`;
case 'darwin':
return `${process.env.HOME}/Downloads`;
case 'linux': {
let dir: Buffer;
try {
dir = execSync('xdg-user-dir DOWNLOAD', { stdio: [0, 3, 3] });
} catch (_) {}
if (dir) {
return dir.toString('utf-8');
}
let stat: fse.Stats;
const homeDownloads = `${process.env.HOME}/Downloads`;
try {
stat = fse.statSync(homeDownloads);
} catch (_) {}
if (stat) {
return homeDownloads;
}
return '/tmp/';
}
default:
break;
}
}
}

View File

@ -0,0 +1,222 @@
import * as path from 'path';
import * as fse from 'fs-extra';
/**
*
* separator
* If the specified filename exists, the separator will be added before the incremental value such as: file{separator}2.jpg
* The default value is '-'.
*
* mode
* The mode allows you to specify which characters to use to generate the incremental value (the string after the separator)
* The default value is 'numeric'.
* 'numeric' Using the following characters: 1234567890
* 'alpha' Using the following characters: abcdefghijklmnopqrstuvwxyz
* 'ALPHA' Using the following characters: ABCDEFGHIJKLMNOPQRSTUVWXYZ
* 'alphanumeric' Using the following characters: 0123456789abcdefghijklmnopqrstuvwxyz
* 'ALPHANUMERIC' Using the following characters: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ
* 'charset' You must specify the characters you wish to use in the charset option
*
* paddingCharacter && paddingSize
* If you wish to left-pad the incremental values with a character, use this option. Here's an example :
* var uniquefilename = require('uniquefilename');
* options = {mode: 'alpha', paddingCharacter: '0', paddingSize: 3};
* uniquefilename.get('/path/to/dir/file.jpg', options, function(filename) {
* // filename might be "/path/to/dir/file.jpg",
* // "/path/to/dir/file-002.jpg", "/path/to/dir/file-045.jpg", etc...
* // depending on the files that exist on your filesystem
* });
*
* alwaysAppend
* If alwaysAppend is true filenames will include the separator and attachment from the first request.
* So instead of file.jpg, file-2.jpg you'd get file-1.jpg, file-2.jpg.
*/
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;
}
}

View File

@ -0,0 +1,72 @@
import { powerMonitor, BrowserWindow } from 'electron';
import { IdleStateChannel } from '@ucap-webmessenger/native-electron';
import { setInterval } from 'timers';
export enum IdleType {
ACTIVE = 'ACT',
IDLE = 'IDLE'
}
export class IdleChecker {
private limitSec: number;
private intervalObject: any;
private status: IdleType;
private window: BrowserWindow | null;
public constructor(window: BrowserWindow, limitedMin?: number) {
limitedMin = limitedMin || 10;
this.limitSec = limitedMin * 60;
this.intervalObject = null;
this.status = IdleType.ACTIVE;
this.window = window;
}
private doCheckIdle(): void {
const idle: number = powerMonitor.getSystemIdleTime();
if (idle > this.limitSec) {
if (this.status === IdleType.ACTIVE) {
this.status = IdleType.IDLE;
// TODO :: USER_STATUS change away
this.window.webContents.send(IdleStateChannel.Changed, this.status);
}
} else {
if (this.status === IdleType.IDLE) {
this.status = IdleType.ACTIVE;
// TODO :: USER_STATUS chage online
this.window.webContents.send(IdleStateChannel.Changed, this.status);
}
}
}
public resetIdleTime(limitedMin: number): void {
limitedMin = limitedMin || 10;
if (!!this.intervalObject) {
clearInterval(this.intervalObject);
}
this.limitSec = limitedMin * 60;
// storage.setIdleTimeLimit(limitedMin);
// global.opt_idleTimeLimit = limitedMin;
this.startChecker();
console.log('RESET IDLE TIMER in ' + limitedMin + 'm');
}
public startChecker() {
console.log('Idle Checker Start');
if (!this.intervalObject) {
this.intervalObject = setInterval(() => {
this.doCheckIdle();
}, 1000);
}
}
public destoryChecker() {
console.log('Idle Checker Destory');
if (!!this.intervalObject) {
clearInterval(this.intervalObject);
}
}
}

View File

@ -0,0 +1,99 @@
import Store from 'electron-store';
const STORE_KEY_AUTORUN = 'options.autoRun';
const STORE_KEY_AUTOLOGIN = 'options.autoLogin';
const STORE_KEY_STARTUPHIDEWINDOW = 'options.startupHideWindow';
const STORE_KEY_LOGINCOMPANY = 'login.loginCompany';
const STORE_KEY_LOGINID = 'login.loginId';
const STORE_KEY_LOGINPW = 'login.loginPw';
export class Storage extends Store<any> {
constructor() {
super({
schema: {
options: {
type: 'object',
properties: {
autoRun: {
type: 'boolean'
},
autoLogin: {
type: 'boolean'
},
startupHideWindow: {
type: 'boolean'
}
},
default: {
autoRun: false,
autoLogin: false,
startupHideWindow: false
}
},
login: {
type: 'object',
properties: {
loginCompany: {
type: 'string'
},
loginId: {
type: 'string'
},
loginPw: {
type: 'string'
}
},
default: {
loginCompany: '',
loginId: '',
loginPw: ''
}
}
},
encryptionKey: 'ucap',
fileExtension: 'dat'
});
}
get autoRun(): boolean {
return this.get(STORE_KEY_AUTORUN, false);
}
set autoRun(autoRun: boolean) {
this.set(STORE_KEY_AUTORUN, autoRun);
}
get autoLogin(): boolean {
return this.get(STORE_KEY_AUTOLOGIN, false);
}
set autoLogin(autoLogin: boolean) {
this.set(STORE_KEY_AUTOLOGIN, autoLogin);
}
get startupHideWindow(): boolean {
return this.get(STORE_KEY_STARTUPHIDEWINDOW, false);
}
set startupHideWindow(startupHideWindow: boolean) {
this.set(STORE_KEY_STARTUPHIDEWINDOW, startupHideWindow);
}
get loginCompany(): string {
return this.get(STORE_KEY_LOGINCOMPANY, false);
}
set loginCompany(loginCompany: string) {
this.set(STORE_KEY_LOGINCOMPANY, loginCompany);
}
get loginId(): string {
return this.get(STORE_KEY_LOGINID, false);
}
set loginId(loginId: string) {
this.set(STORE_KEY_LOGINID, loginId);
}
get loginPw(): string {
return this.get(STORE_KEY_LOGINPW, false);
}
set loginPw(loginPw: string) {
this.set(STORE_KEY_LOGINPW, loginPw);
}
}

View File

@ -0,0 +1,54 @@
import { BrowserWindow } from 'electron';
import { WindowState } from '@ucap-webmessenger/native';
import { WindowStateChannel } from '@ucap-webmessenger/native-electron';
import { ElectronBrowserWindowChannel } from '@ucap-webmessenger/electron-core';
export function getWindowState(window: Electron.BrowserWindow): WindowState {
if (window.isFullScreen()) {
return WindowState.FullScreen;
} else if (window.isMaximized()) {
return WindowState.Maximized;
} else if (window.isMinimized()) {
return WindowState.Minimized;
} else if (!window.isVisible()) {
return WindowState.Hidden;
} else {
return WindowState.Normal;
}
}
export function registerWindowStateChangedEvents(window: BrowserWindow) {
window.on(ElectronBrowserWindowChannel.EnterFullScreen, () =>
sendWindowStateEvent(window, WindowState.FullScreen)
);
window.on(ElectronBrowserWindowChannel.LeaveFullScreen, () =>
sendWindowStateEvent(window, WindowState.Normal)
);
window.on(ElectronBrowserWindowChannel.Maximize, () =>
sendWindowStateEvent(window, WindowState.Maximized)
);
window.on(ElectronBrowserWindowChannel.Minimize, () =>
sendWindowStateEvent(window, WindowState.Minimized)
);
window.on(ElectronBrowserWindowChannel.Unmaximize, () =>
sendWindowStateEvent(window, WindowState.Normal)
);
window.on(ElectronBrowserWindowChannel.Restore, () =>
sendWindowStateEvent(window, WindowState.Normal)
);
window.on(ElectronBrowserWindowChannel.Hide, () =>
sendWindowStateEvent(window, WindowState.Hidden)
);
window.on(ElectronBrowserWindowChannel.Show, () => {
// because the app can be maximized before being closed - which will restore it
// maximized on the next launch - this function should inspect the current state
// rather than always assume it is a 'normal' launch
sendWindowStateEvent(window, getWindowState(window));
});
}
function sendWindowStateEvent(window: BrowserWindow, windowState: WindowState) {
window.webContents.send(WindowStateChannel.Changed, windowState);
}

View File

@ -0,0 +1,4 @@
export function now(): number {
const time = process.hrtime();
return time[0] * 1000 + time[1] / 1000000;
}

View File

@ -0,0 +1,11 @@
import * as path from 'path';
// tslint:disable-next-line: variable-name
const _root = __DEV__
? path.resolve(__dirname, '..', '..')
: path.resolve(__dirname);
export function root(...paths: string[]) {
const args = Array.prototype.slice.call(paths, 0);
return path.join.apply(path, [_root].concat(args));
}

View File

@ -0,0 +1,34 @@
{
"compilerOptions": {
"baseUrl": "./",
"outDir": "../../dist/main",
"sourceMap": true,
"declaration": false,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"esModuleInterop": true,
"target": "es5",
"types": ["node"],
"lib": ["es2017", "es2016", "es2015", "dom"],
"paths": {
"@ucap-webmessenger/electron-core": [
"../ucap-webmessenger-electron-core/src/public-api"
],
"@ucap-webmessenger/electron-notification": [
"../ucap-webmessenger-electron-notification/src/public-api"
],
"@ucap-webmessenger/native": [
"../../projects/ucap-webmessenger-native/src/public-api"
],
"@ucap-webmessenger/native-electron": [
"../../projects/ucap-webmessenger-native-electron/src/public-api"
],
"@ucap-webmessenger/electron": [
"../../projects/ucap-webmessenger-electron/src/public-api"
]
}
},
"exclude": ["../../node_modules", "**/*.spec.ts"]
}

View File

@ -0,0 +1,7 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [true, "attribute", "ucapElectron", "camelCase"],
"component-selector": [true, "element", "ucap-electron", "kebab-case"]
}
}

View File

@ -24,6 +24,9 @@
],
"@ucap-webmessenger/native-electron": [
"../projects/ucap-webmessenger-native-electron/src/public-api"
],
"@ucap-webmessenger/electron": [
"../projects/ucap-webmessenger-electron/src/public-api"
]
}
},

View File

@ -4,13 +4,12 @@
"scripts": {
"ng": "ng",
"start": "npm-run-all -p start:renderer start:main",
"start:main": "wait-on http-get://localhost:4200/ && npm run build:main:dev && electron --nolazy --inspect-brk=9229 .",
"start:renderer": "ng serve",
"start:web": "cross-env UCAP_ENV=WEB ng serve",
"start:production": "npm run build:renderer && npm run build:main:prod && electron --nolazy --inspect-brk=9229 .",
"start:main": "wait-on http-get://localhost:4200/ && npm run build:main:development && electron --nolazy --inspect-brk=9229 .",
"start:renderer": "cross-env UCAP_ENV_RUNTIME=ELECTRON ng serve -c renderer-development",
"start:browser": "cross-env UCAP_ENV_RUNTIME=BROWSER ng serve -c browser-development -o",
"build:renderer": "cross-env NODE_ENV=production ng build --base-href ./",
"build:main:dev": "cross-env NODE_ENV=development TS_NODE_PROJECT='./config/tsconfig.webpack.json' parallel-webpack --config=config/main.webpack.config.ts",
"build:main:prod": "cross-env NODE_ENV=production TS_NODE_PROJECT='./config/tsconfig.webpack.json' NODE_OPTIONS='--max_old_space_size=4096' parallel-webpack --config=config/main.webpack.config.ts",
"build:main:development": "cross-env NODE_ENV=development TS_NODE_PROJECT='./config/tsconfig.webpack.json' parallel-webpack --config=config/main.webpack.config.ts",
"build:main:production": "cross-env NODE_ENV=production TS_NODE_PROJECT='./config/tsconfig.webpack.json' NODE_OPTIONS='--max_old_space_size=4096' parallel-webpack --config=config/main.webpack.config.ts",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
@ -112,5 +111,5 @@
"webpack-node-externals": "^1.7.2",
"zone.js": "~0.9.1"
},
"main": "./dist/main/main.js"
"main": "./dist/ucap-webmessenger-electron/main.js"
}

View File

@ -1,11 +1,11 @@
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { ElectronNativeService } from '@ucap-webmessenger/native-electron';
import { RESOLVERS } from './resolvers';
import { SERVICES } from './services';
import { AppService } from './services/app.service';
import { HttpClient } from '@angular/common/http';
import { UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { environment } from '../environments/environment';
export function initializeApp(appService: AppService) {
return (): Promise<any> => {
@ -19,13 +19,17 @@ export function initializeApp(appService: AppService) {
providers: [
...SERVICES,
...RESOLVERS,
{
provide: UCAP_NATIVE_SERVICE,
useClass: environment.modules.native.serviceClass,
deps: [HttpClient]
},
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [AppService],
multi: true
},
{ provide: UCAP_NATIVE_SERVICE, useClass: ElectronNativeService }
}
]
})
export class AppProviderModule {}

View File

@ -1,9 +1,9 @@
import { Injectable } from '@angular/core';
import { AppNotificationService } from './notification.service';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { EnviromentUtilService } from '@ucap-webmessenger/util';
import { DeviceType } from '@ucap-webmessenger/core';
import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types';
import { AppNotificationService } from './notification.service';
@Injectable()
export class AppService {
@ -13,8 +13,8 @@ export class AppService {
private appNotificationService: AppNotificationService
) {}
public postInit(): Promise<void> {
return new Promise<void>((resolve, reject) => {
public postInit(): Promise<any> {
const initPromise = new Promise<void>((resolve, reject) => {
try {
let deviceType: DeviceType;
if (this.enviromentUtilService.nodeWebkit()) {
@ -40,5 +40,7 @@ export class AppService {
reject();
}
});
return Promise.all([initPromise]);
}
}

View File

@ -0,0 +1,18 @@
import { Environment, build } from './environment.type';
import { BrowserNativeService } from '@ucap-webmessenger/native-browser';
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment: Environment = build(false);
environment.modules.native.serviceClass = BrowserNativeService;
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

View File

@ -0,0 +1,5 @@
import { Environment, build } from './environment.type';
import { BrowserNativeService } from '@ucap-webmessenger/native-browser';
export const environment: Environment = build(true);
environment.modules.native.serviceClass = BrowserNativeService;

View File

@ -0,0 +1,18 @@
import { Environment, build } from './environment.type';
import { ElectronNativeService } from '@ucap-webmessenger/native-electron';
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.
export const environment: Environment = build(false);
environment.modules.native.serviceClass = ElectronNativeService;
/*
* For easier debugging in development mode, you can import the following file
* to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
*
* This import should be commented out in production mode because it will have a negative impact
* on performance if an error is thrown.
*/
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.

View File

@ -0,0 +1,5 @@
import { Environment, build } from './environment.type';
import { ElectronNativeService } from '@ucap-webmessenger/native-electron';
export const environment: Environment = build(true);
environment.modules.native.serviceClass = ElectronNativeService;

View File

@ -1,3 +0,0 @@
import { Environment, build } from './environment.type';
export const environment: Environment = build(true);

View File

@ -1,3 +1,6 @@
import { Type } from '@angular/core';
import { NativeService } from '@ucap-webmessenger/native';
export abstract class UrlConfig {
constructor(
protected useSsl: boolean,
@ -334,6 +337,9 @@ export interface Environment {
};
};
modules: {
native: {
serviceClass?: Type<NativeService>;
};
event: {
acceptableFileExtensions: string[];
};
@ -381,6 +387,7 @@ export function build(production: boolean): Environment {
}
},
modules: {
native: {},
event: {
acceptableFileExtensions: [
// 문서1

View File

@ -1,7 +0,0 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/ucap-webmessenger-electron-core",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@ -1,8 +0,0 @@
{
"name": "@ucap-webmessenger/electron-core",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^8.2.11",
"@angular/core": "^8.2.11"
}
}

View File

@ -1,26 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -1,7 +0,0 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/ucap-webmessenger-electron-notification",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@ -1,26 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -8,7 +8,11 @@ import {
} from '@ucap-webmessenger/native';
import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class BrowserNativeService implements NativeService {
notify(noti: NotificationRequest): void {}
closeAllNotify(): void {}

View File

@ -1,12 +1,16 @@
import { TranslateLoader } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { NativeService } from '@ucap-webmessenger/native';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { take, map } from 'rxjs/operators';
import { FileUtil } from '@ucap-webmessenger/core';
import { Injectable, Inject } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TranslateBrowserLoader implements TranslateLoader {
constructor(
private nativeService: NativeService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private prefix: string = '/assets/i18n/',
private suffix: string = '.json'
) {}

View File

@ -0,0 +1,19 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { BrowserNativeService } from './services/browser-native.service';
const SERVICES = [BrowserNativeService];
@NgModule({
declarations: [],
imports: [],
exports: []
})
export class UCapBrowserNativeModule {
public static forRoot(): ModuleWithProviders<UCapBrowserNativeModule> {
return {
ngModule: UCapBrowserNativeModule,
providers: [...SERVICES]
};
}
}

View File

@ -5,3 +5,5 @@
export * from './lib/services/browser-native.service';
export * from './lib/translate/browser-loader';
export * from './lib/ucap-browser-native.module';

View File

@ -16,7 +16,11 @@ import {
WindowStateChannel,
IdleStateChannel
} from '../types/channel.type';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class ElectronNativeService implements NativeService {
private windowStateChangedSubject: Subject<WindowState> | null = null;
private windowStateChanged$: Observable<WindowState> | null = null;

View File

@ -1,11 +1,12 @@
import { TranslateLoader } from '@ngx-translate/core';
import { Observable } from 'rxjs';
import { NativeService } from '@ucap-webmessenger/native';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { take, map } from 'rxjs/operators';
import { Inject } from '@angular/core';
export class TranslateElectronLoader implements TranslateLoader {
constructor(
private nativeService: NativeService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private prefix: string = '/assets/i18n/',
private suffix: string = '.json'
) {}

View File

@ -0,0 +1,19 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { ElectronNativeService } from './services/electron-native.service';
const SERVICES = [ElectronNativeService];
@NgModule({
declarations: [],
imports: [],
exports: []
})
export class UCapElectronNativeModule {
public static forRoot(): ModuleWithProviders<UCapElectronNativeModule> {
return {
ngModule: UCapElectronNativeModule,
providers: [...SERVICES]
};
}
}

View File

@ -7,3 +7,5 @@ export * from './lib/services/electron-native.service';
export * from './lib/translate/electron-loader';
export * from './lib/types/channel.type';
export * from './lib/ucap-electron-native.module';

View File

@ -127,10 +127,13 @@
"projects/ucap-webmessenger-util/src/public-api"
],
"@ucap-webmessenger/electron-core": [
"projects/ucap-webmessenger-electron-core/src/public-api"
"electron-projects/ucap-webmessenger-electron-core/src/public-api"
],
"@ucap-webmessenger/electron-notification": [
"projects/ucap-webmessenger-electron-notification/src/public-api"
"electron-projects/ucap-webmessenger-electron-notification/src/public-api"
],
"@ucap-webmessenger/electron": [
"electron-projects/ucap-webmessenger-electron/src/public-api"
]
}
},