renderer update is implemented

This commit is contained in:
병준 박 2019-12-12 17:15:09 +09:00
parent bc037c6d52
commit e84d47540d
28 changed files with 509 additions and 65 deletions

View File

@ -27,9 +27,9 @@
"icon": "./dist/ucap-webmessenger-electron/resources/linuxicon"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"perMachine": true,
"oneClick": true,
"allowToChangeInstallationDirectory": false,
"perMachine": false,
"differentialPackage": true
},
"directories": {

View File

@ -1,17 +1,6 @@
import {
app,
ipcMain,
IpcMainEvent,
remote,
Tray,
Menu,
dialog,
shell
} from 'electron';
import * as path from 'path';
import * as url from 'url';
import * as fse from 'fs-extra';
import * as fs from 'fs';
import { app, ipcMain, IpcMainEvent, Tray, Menu, shell } from 'electron';
import path from 'path';
import fse from 'fs-extra';
import { AppWindow } from './app/AppWindow';
import { now } from './util/now';
@ -32,12 +21,16 @@ 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 {
NotificationRequest,
UpdateCheckConfig
} from '@ucap-webmessenger/native';
import { ElectronAppChannel } from '@ucap-webmessenger/electron-core';
import { RendererUpdater } from './lib/renderer-updater';
const appIconPath = __LINUX__
? path.join(__dirname, 'static', 'icon-logo.png')
: path.join(__dirname, 'resources/image', 'ico_64_64.png');
: path.join(__dirname, 'resources/image', '64_64.png');
let appWindow: AppWindow | null = null;
let appTray: Tray | null = null;
@ -85,6 +78,7 @@ let isDuplicateInstance = false;
const gotSingleInstanceLock = app.requestSingleInstanceLock();
isDuplicateInstance = !gotSingleInstanceLock;
let idle: IdleChecker | null;
let rendererUpdater: RendererUpdater | undefined;
app.on(ElectronAppChannel.SecondInstance, (event, args, workingDirectory) => {
// Someone tried to run a second instance, we should focus our window.
@ -280,6 +274,38 @@ ipcMain.on(UpdaterChannel.Check, (event: IpcMainEvent, ...args: any[]) => {
event.returnValue = false;
});
ipcMain.on(
UpdaterChannel.StartCheckInstant,
(event: IpcMainEvent, ...args: any[]) => {
const config = args[0] as UpdateCheckConfig;
if (!!rendererUpdater) {
rendererUpdater.stopCheck();
rendererUpdater = null;
}
rendererUpdater = new RendererUpdater(appWindow.browserWindow, config); // default 10min
rendererUpdater.startCheck();
}
);
ipcMain.on(
UpdaterChannel.StopCheckInstant,
(event: IpcMainEvent, ...args: any[]) => {
if (!!rendererUpdater) {
rendererUpdater.stopCheck();
rendererUpdater = null;
}
}
);
ipcMain.on(
UpdaterChannel.ApplyInstant,
(event: IpcMainEvent, ...args: any[]) => {
if (!!rendererUpdater) {
rendererUpdater.apply();
}
}
);
ipcMain.on(FileChannel.ReadFile, (event: IpcMainEvent, ...args: any[]) => {
try {
fse.readFile(root(args[0]), (err, data) => {

View File

@ -0,0 +1,122 @@
import { interval, Subscription } from 'rxjs';
import {
UpdateCheckConfig,
UpdateInfo,
UpdateType
} from '@ucap-webmessenger/native';
import axios from 'axios';
import semver from 'semver';
import path from 'path';
import fse from 'fs-extra';
import zlib from 'zlib';
import { BrowserWindow, app } from 'electron';
import { UpdaterChannel } from '@ucap-webmessenger/native-electron';
import { startWith } from 'rxjs/operators';
export class RendererUpdater {
private checkSubscription: Subscription | undefined;
private readonly appPath: string;
private readonly downloadPath: string;
private readonly unzipPath: string;
constructor(
private window: BrowserWindow,
private config: UpdateCheckConfig
) {
this.appPath = app.getAppPath() + '/';
const appPathFolder = this.appPath.slice(
0,
this.appPath.indexOf('app.asar')
);
this.downloadPath = path.resolve(appPathFolder, 'update.zip');
this.unzipPath = path.resolve(appPathFolder, '_app.asar');
}
startCheck() {
if (!semver.valid(this.config.currentVersion)) {
console.log(
`RendererUpdater::error currentVersion[${this.config.currentVersion}] is not valid`
);
return;
}
this.checkSubscription = interval(this.config.intervalHour * 60 * 60 * 1000)
.pipe(startWith(0))
.subscribe(i => {
axios
.post(this.config.feed)
.then(res => {
const appVersion = res.data.appVersion;
if (!semver.valid(appVersion)) {
console.log(
`RendererUpdater::error appVersion[${appVersion}] is not valid`
);
return;
}
if (semver.lt(this.config.currentVersion, appVersion)) {
this.download(appVersion, res.data.installUrl);
}
})
.catch(reason => {
console.log('RendererUpdater', reason);
});
});
}
stopCheck() {
if (!!this.checkSubscription) {
this.checkSubscription.unsubscribe();
}
}
apply() {
const downloadStream = fse.createReadStream(this.downloadPath);
const unzipStream = fse.createWriteStream(this.unzipPath);
const unzip = zlib.createGunzip();
downloadStream
.pipe(unzip)
.pipe(unzipStream)
.end(chunk => {
try {
fse.unlinkSync(this.appPath.slice(0, -1));
} catch (error) {
console.log('RendererUpdater::apply', error);
return;
}
try {
fse.renameSync(this.unzipPath, this.appPath.slice(0, -1));
} catch (error) {
console.log('RendererUpdater::apply', error);
return;
}
app.relaunch();
});
}
private download(appVersion: string, installUrl: string) {
axios
.get(installUrl, { responseType: 'blob' })
.then(res => {
fse.writeFile(this.downloadPath, res.data, err => {
if (!!err) {
console.log('RendererUpdater::download failed', err);
return;
}
const updateInfo: UpdateInfo = {
type: UpdateType.Differencial,
version: appVersion,
description: ''
};
this.window.webContents.send(UpdaterChannel.ExistInstant, updateInfo);
});
})
.catch(reason => {
console.log('RendererUpdater::download failed', reason);
});
}
}

51
package-lock.json generated
View File

@ -2270,6 +2270,15 @@
"integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==",
"dev": true
},
"@types/axios": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
"integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=",
"dev": true,
"requires": {
"axios": "*"
}
},
"@types/copy-webpack-plugin": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/copy-webpack-plugin/-/copy-webpack-plugin-5.0.0.tgz",
@ -3224,6 +3233,48 @@
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
"dev": true
},
"axios": {
"version": "0.19.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
"integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
"dev": true,
"requires": {
"follow-redirects": "1.5.10",
"is-buffer": "^2.0.2"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"follow-redirects": {
"version": "1.5.10",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
"dev": true,
"requires": {
"debug": "=3.1.0"
}
},
"is-buffer": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
"integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
"dev": true
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
}
}
},
"axobject-query": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",

View File

@ -53,6 +53,7 @@
"@ngrx/store": "^8.4.0",
"@ngrx/store-devtools": "^8.4.0",
"@ngx-translate/core": "^11.0.1",
"@types/axios": "^0.14.0",
"@types/copy-webpack-plugin": "^5.0.0",
"@types/crypto-js": "^3.1.43",
"@types/detect-browser": "^4.0.0",
@ -71,6 +72,7 @@
"@types/webpack-node-externals": "^1.6.3",
"angular-split": "^3.0.2",
"autolinker": "^3.11.1",
"axios": "^0.19.0",
"awesome-node-loader": "^1.1.1",
"awesome-typescript-loader": "^5.2.1",
"classlist.js": "^1.1.20150312",

View File

@ -1,5 +1,5 @@
import { Injectable, Inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { HttpClient, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@ -8,13 +8,13 @@ import {
VersionInfo2Request,
VersionInfo2Response,
encodeVersionInfo2,
decodeVersionInfo2,
decodeVersionInfo2
} from '../apis/version-info2';
import {
UpdateInfoRequest,
UpdateInfoResponse,
encodeUpdateInfo,
decodeUpdateInfo,
decodeUpdateInfo
} from '../apis/update-info';
import { _MODULE_CONFIG } from '../config/token';
@ -23,7 +23,7 @@ import { UrlConfig } from '@ucap-webmessenger/core';
import { Urls } from '../config/urls';
@Injectable({
providedIn: 'root',
providedIn: 'root'
})
export class PublicApiService {
readonly urls: Urls;
@ -46,19 +46,32 @@ export class PublicApiService {
this.urls.versionInfo2,
{},
{
params: encodeVersionInfo2(req),
params: encodeVersionInfo2(req)
}
)
.pipe(map((res: any) => decodeVersionInfo2(res)));
}
public urlForVersionInfo2(req: VersionInfo2Request): string {
const httpReq = new HttpRequest(
'GET',
this.urls.versionInfo2,
{},
{
params: encodeVersionInfo2(req)
}
);
return httpReq.urlWithParams;
}
public updateInfo(req: UpdateInfoRequest): Observable<UpdateInfoResponse> {
return this.httpClient
.post<any>(
this.urls.updateInfo,
{},
{
params: encodeUpdateInfo(req),
params: encodeUpdateInfo(req)
}
)
.pipe(map(res => decodeUpdateInfo(res)));

View File

@ -21,18 +21,6 @@ export function initializeApp(
};
}
// export function nativeServiceFactory(httpClient: HttpClient) {
// if ('browser' === environment.runtime) {
// return import('@ucap-webmessenger/native-browser').then(
// m => new m.BrowserNativeService(httpClient)
// );
// } else {
// return import('@ucap-webmessenger/native-electron').then(
// m => new m.ElectronNativeService()
// );
// }
// }
@NgModule({
imports: [],
exports: [],
@ -44,7 +32,7 @@ export function initializeApp(
'browser' === environment.runtime
? BrowserNativeService
: ElectronNativeService,
deps: [HttpClient],
deps: [HttpClient]
},
...SERVICES,
...RESOLVERS,
@ -52,8 +40,8 @@ export function initializeApp(
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [AppService, UCAP_NATIVE_SERVICE],
multi: true,
},
],
multi: true
}
]
})
export class AppProviderModule {}

View File

@ -2,7 +2,7 @@
<div class="app-layout-native-title-bar-logo">
<img src="assets/images/logo/64_64.png" />
</div>
<div class="app-layout-native-title-bar-title">UCAP M Messenger</div>
<div class="app-layout-native-title-bar-title">UCAP M Messenger!!</div>
<div class="app-layout-native-title-bar-spacer"></div>
<div *ngIf="!!loginRes" class="app-layout-native-title-bar-link">
<button
@ -32,6 +32,32 @@
<span class="stroke-bar"></span>
<button
*ngIf="updateInfo$ | async as updateInfo"
mat-icon-button
class="button app-layout-native-title-bar-setting"
matTooltip="업데이트"
(click)="onClickUpdate()"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="1.5"
stroke-linecap="butt"
stroke-linejoin="round"
alt="업데이트"
>
<circle cx="12" cy="12" r="3"></circle>
<path
d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"
></path>
</svg>
</button>
<button
mat-icon-button
class="button app-layout-native-title-bar-setting"

View File

@ -2,7 +2,8 @@ import { Component, OnInit, Inject, OnDestroy, ViewChild } from '@angular/core';
import {
UCAP_NATIVE_SERVICE,
NativeService,
WindowState
WindowState,
UpdateInfo
} from '@ucap-webmessenger/native';
import { Observable, Subscription } from 'rxjs';
import { Store, select } from '@ngrx/store';
@ -11,6 +12,7 @@ import * as AppStore from '@app/store';
import * as ChatStore from '@app/store/messenger/chat';
import * as AuthenticationStore from '@app/store/account/authentication';
import * as SettingsStore from '@app/store/messenger/settings';
import * as UpdateStore from '@app/store/setting/update';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { tap } from 'rxjs/operators';
@ -23,11 +25,13 @@ import { RightDrawer } from '@app/types';
})
export class TopBarComponent implements OnInit, OnDestroy {
windowStateChanged$: Observable<WindowState>;
WindowState = WindowState;
loginRes: LoginResponse;
loginResSubscription: Subscription;
updateInfo$: Observable<UpdateInfo>;
showWeblink = false;
weblink: {
title: string;
@ -159,6 +163,10 @@ export class TopBarComponent implements OnInit, OnDestroy {
})
)
.subscribe();
this.updateInfo$ = this.store.pipe(
select(AppStore.SettingSelector.UpdateSelector.updateInfo)
);
}
ngOnDestroy(): void {}
@ -194,4 +202,8 @@ export class TopBarComponent implements OnInit, OnDestroy {
onToggleWebLinkSelector(): void {
this.showWeblink = !this.showWeblink;
}
onClickUpdate() {
this.store.dispatch(UpdateStore.applyInstantUpdate());
}
}

View File

@ -52,6 +52,7 @@ import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import { environment } from '../../environments/environment';
import { SnackBarService } from '@ucap-webmessenger/ui';
import { AppNativeService } from '@app/services/native.service';
@Injectable()
export class AppMessengerResolver implements Resolve<void> {
@ -64,6 +65,7 @@ export class AppMessengerResolver implements Resolve<void> {
private optionProtocolService: OptionProtocolService,
private authenticationProtocolService: AuthenticationProtocolService,
private innerProtocolService: InnerProtocolService,
private appNativeService: AppNativeService,
private snackBarService: SnackBarService,
private logger: NGXLogger
) {}
@ -205,6 +207,7 @@ export class AppMessengerResolver implements Resolve<void> {
})
);
this.store.dispatch(AuthenticationStore.postLogin({ loginRes }));
this.appNativeService.subscribeAfterLogin();
resolve();
},
err => {

View File

@ -1,10 +1,25 @@
import { Injectable, Inject } from '@angular/core';
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
import { DOCUMENT } from '@angular/common';
import {
UCAP_NATIVE_SERVICE,
NativeService,
UpdateCheckConfig
} from '@ucap-webmessenger/native';
import { Store } from '@ngrx/store';
import { NGXLogger } from 'ngx-logger';
import * as AuthenticationStore from '@app/store/account/authentication';
import * as SettingsStore from '@app/store/messenger/settings';
import * as UpdateStore from '@app/store/setting/update';
import { PublicApiService } from '@ucap-webmessenger/api-public';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import {
LoginInfo,
KEY_LOGIN_INFO,
EnvironmentsInfo,
KEY_ENVIRONMENTS_INFO
} from '@app/types';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root'
@ -12,6 +27,9 @@ import * as SettingsStore from '@app/store/messenger/settings';
export class AppNativeService {
constructor(
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private publicApiService: PublicApiService,
private sessionStorageService: SessionStorageService,
@Inject(DOCUMENT) private document: Document,
private store: Store<any>,
private logger: NGXLogger
) {}
@ -25,4 +43,32 @@ export class AppNativeService {
this.store.dispatch(SettingsStore.showDialog());
});
}
subscribeAfterLogin(): void {
const loginInfo = this.sessionStorageService.get<LoginInfo>(KEY_LOGIN_INFO);
const environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
KEY_ENVIRONMENTS_INFO
);
const els = this.document.getElementsByTagName('app-root');
const currentVersion = els.item(0).getAttribute('app-version');
const updateCheckConfig: UpdateCheckConfig = {
feed: this.publicApiService.urlForVersionInfo2({
deviceType: environment.productConfig.updateCheckConfig.deviceType,
companyGroupType: loginInfo.companyGroupType,
companyCode: loginInfo.companyCode,
loginId: loginInfo.loginId
}),
currentVersion,
intervalHour: environment.productConfig.updateCheckConfig.intervalHour
};
this.nativeService
.checkForInstantUpdates(updateCheckConfig)
.subscribe(updateInfo => {
this.store.dispatch(UpdateStore.existInstantUpdate({ updateInfo }));
});
}
}

View File

@ -4,46 +4,44 @@ import { Action, combineReducers, Selector, createSelector } from '@ngrx/store';
import * as CompanyStore from './company';
import * as InitStore from './init';
import * as VersionInfoStore from './version-info';
import * as UpdateStore from './update';
export interface State {
company: CompanyStore.State;
init: InitStore.State;
versionInfo: VersionInfoStore.State;
update: UpdateStore.State;
}
export const effects: Type<any>[] = [
CompanyStore.Effects,
InitStore.Effects,
VersionInfoStore.Effects
VersionInfoStore.Effects,
UpdateStore.Effects
];
export function reducers(state: State | undefined, action: Action) {
return combineReducers({
company: CompanyStore.reducer,
init: InitStore.reducer,
versionInfo: VersionInfoStore.reducer
versionInfo: VersionInfoStore.reducer,
update: UpdateStore.reducer
})(state, action);
}
export function selectors<S>(selector: Selector<any, State>) {
return {
CompanySelector: CompanyStore.selectors(
createSelector(
selector,
(state: State) => state.company
)
createSelector(selector, (state: State) => state.company)
),
InitSelector: InitStore.selectors(
createSelector(
selector,
(state: State) => state.init
)
createSelector(selector, (state: State) => state.init)
),
VersionInfoSelector: VersionInfoStore.selectors(
createSelector(
selector,
(state: State) => state.versionInfo
)
createSelector(selector, (state: State) => state.versionInfo)
),
UpdateSelector: UpdateStore.selectors(
createSelector(selector, (state: State) => state.update)
)
};
}

View File

@ -0,0 +1,11 @@
import { createAction, props } from '@ngrx/store';
import { UpdateInfo } from '@ucap-webmessenger/native';
export const existInstantUpdate = createAction(
'[Setting::Update] existInstantUpdate',
props<{ updateInfo: UpdateInfo }>()
);
export const applyInstantUpdate = createAction(
'[Setting::Update] applyInstantUpdate'
);

View File

@ -0,0 +1,25 @@
import { Injectable, Inject } from '@angular/core';
import { createEffect, Actions, ofType } from '@ngrx/effects';
import { applyInstantUpdate } from './actions';
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
import { tap } from 'rxjs/operators';
@Injectable()
export class Effects {
applyInstantUpdate$ = createEffect(
() => {
return this.actions$.pipe(
ofType(applyInstantUpdate),
tap(() => {
this.nativeService.applyInstantUpdates();
})
);
},
{ dispatch: false }
);
constructor(
private actions$: Actions,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService
) {}
}

View File

@ -0,0 +1,4 @@
export * from './actions';
export * from './effects';
export * from './reducers';
export * from './state';

View File

@ -0,0 +1,13 @@
import { createReducer, on } from '@ngrx/store';
import { initialState } from './state';
import { existInstantUpdate } from './actions';
export const reducer = createReducer(
initialState,
on(existInstantUpdate, (state, action) => {
return {
...state,
updateInfo: action.updateInfo
};
})
);

View File

@ -0,0 +1,16 @@
import { Selector, createSelector } from '@ngrx/store';
import { UpdateInfo } from '@ucap-webmessenger/native';
export interface State {
updateInfo: UpdateInfo;
}
export const initialState: State = {
updateInfo: null
};
export function selectors<S>(selector: Selector<any, State>) {
return {
updateInfo: createSelector(selector, (state: State) => state.updateInfo)
};
}

View File

@ -8,6 +8,7 @@ import {
protocolUrls,
messageApiUrls
} from './environment.type';
import { DeviceType } from '@ucap-webmessenger/core';
export const environment: Environment = {
production: false,
@ -22,6 +23,10 @@ export const environment: Environment = {
productName: 'EZMessenger',
authentication: {
usePrivateInformationAgree: false
},
updateCheckConfig: {
deviceType: DeviceType.Renderer,
intervalHour: 1
}
},

View File

@ -8,6 +8,7 @@ import {
protocolUrls,
messageApiUrls
} from './environment.type';
import { DeviceType } from '@ucap-webmessenger/core';
export const environment: Environment = {
production: true,
@ -22,6 +23,10 @@ export const environment: Environment = {
productName: 'EZMessenger',
authentication: {
usePrivateInformationAgree: false
},
updateCheckConfig: {
deviceType: DeviceType.Renderer,
intervalHour: 1
}
},

View File

@ -22,7 +22,11 @@ import {
ModuleConfig as ProtocolModuleConfig,
Urls as ProtocolUrls
} from '@ucap-webmessenger/protocol';
import { ModuleConfig as NativeModuleConfig } from '@ucap-webmessenger/native';
import {
ModuleConfig as NativeModuleConfig,
UpdateCheckConfig
} from '@ucap-webmessenger/native';
import { DeviceType } from '@ucap-webmessenger/core';
export type UCapRuntime = 'browser' | 'electron';
@ -41,6 +45,10 @@ export interface Environment {
authentication: {
usePrivateInformationAgree: boolean;
};
updateCheckConfig: {
deviceType: DeviceType;
intervalHour: number;
};
};
commonApiModuleConfig: CommonApiModuleConfig;

View File

@ -10,7 +10,7 @@
<link href="assets/scss/partials/splash.css" rel="stylesheet" />
</head>
<body class="theme-default">
<app-root></app-root>
<app-root app-version="0.0.1"></app-root>
<div class="wrapper-splash" id="splash-screen">
<svg

View File

@ -5,5 +5,6 @@ export enum DeviceType {
iOS = 'I',
iOSCall = 'O',
PC = 'P',
iMessenger = 'H'
iMessenger = 'H',
Renderer = 'R'
}

View File

@ -4,7 +4,9 @@ import {
NativeService,
WindowState,
NotificationRequest,
WindowIdle
WindowIdle,
UpdateInfo,
UpdateCheckConfig
} from '@ucap-webmessenger/native';
import { HttpClient } from '@angular/common/http';
import { map, share } from 'rxjs/operators';
@ -74,6 +76,19 @@ export class BrowserNativeService extends NativeService {
});
}
checkForInstantUpdates(config: UpdateCheckConfig): Observable<UpdateInfo> {
return new Observable<UpdateInfo>(subscriber => {
try {
} catch (error) {
subscriber.error(error);
} finally {
subscriber.complete();
}
});
}
applyInstantUpdates(): void {}
showImageViewer(): void {}
readFile(path: string): Promise<Buffer> {

View File

@ -6,7 +6,9 @@ import {
NativeService,
WindowState,
NotificationRequest,
WindowIdle
WindowIdle,
UpdateInfo,
UpdateCheckConfig
} from '@ucap-webmessenger/native';
import { share } from 'rxjs/operators';
import {
@ -49,6 +51,9 @@ export class ElectronNativeService implements NativeService {
private chatOpenRoomSubject: Subject<string> | null = null;
private chatOpenRoom$: Observable<string> | null = null;
private backgroundCheckForUpdatesSubject: Subject<UpdateInfo> | null = null;
private backgroundCheckForUpdates$: Observable<UpdateInfo> | null = null;
postAppInit(): void {}
logout(): Observable<void> {
@ -110,6 +115,29 @@ export class ElectronNativeService implements NativeService {
});
}
checkForInstantUpdates(config: UpdateCheckConfig): Observable<UpdateInfo> {
if (!this.backgroundCheckForUpdatesSubject) {
this.backgroundCheckForUpdatesSubject = new Subject<UpdateInfo>();
this.backgroundCheckForUpdates$ = this.backgroundCheckForUpdatesSubject
.asObservable()
.pipe(share());
}
this.ipcRenderer.send(UpdaterChannel.StartCheckInstant, config);
this.ipcRenderer.on(
UpdaterChannel.ExistInstant,
(event: any, updateInfo: UpdateInfo) => {
this.backgroundCheckForUpdatesSubject.next(updateInfo);
}
);
return this.backgroundCheckForUpdates$;
}
applyInstantUpdates(): void {
this.ipcRenderer.send(UpdaterChannel.ApplyInstant);
}
showImageViewer(): void {
this.ipcRenderer.send(FileChannel.ShowImageViewer);
}

View File

@ -14,7 +14,11 @@ export enum NotificationChannel {
}
export enum UpdaterChannel {
Check = 'UCAP::updater::check'
Check = 'UCAP::updater::check',
StartCheckInstant = 'UCAP::updater::startCheckInstant',
ExistInstant = 'UCAP::updater::existInstant',
ApplyInstant = 'UCAP::updater::applyInstant',
StopCheckInstant = 'UCAP::updater::stopCheckInstant'
}
export enum FileChannel {

View File

@ -0,0 +1,16 @@
export enum UpdateType {
Full = 'Full',
Differencial = 'Differencial'
}
export interface UpdateInfo {
type: UpdateType;
version: string;
description: string;
}
export interface UpdateCheckConfig {
feed: string;
currentVersion: string;
intervalHour: number;
}

View File

@ -5,6 +5,7 @@ import { WindowIdle } from '../types/window-idle.type';
import { NotificationRequest } from '../models/notification';
import { TranslateLoader } from '@ngx-translate/core';
import { StatusCode } from '@ucap-webmessenger/core';
import { UpdateInfo, UpdateCheckConfig } from '../models/update-info';
export abstract class NativeService {
abstract postAppInit(): void;
@ -17,6 +18,10 @@ export abstract class NativeService {
abstract closeAllNotify(): void;
abstract checkForUpdates(): Promise<boolean>;
abstract checkForInstantUpdates(
config: UpdateCheckConfig
): Observable<UpdateInfo>;
abstract applyInstantUpdates(): void;
abstract showImageViewer(): void;

View File

@ -5,6 +5,7 @@
export * from './lib/types/token';
export * from './lib/models/notification';
export * from './lib/models/update-info';
export * from './lib/services/native.service';