renderer update is implemented
This commit is contained in:
parent
bc037c6d52
commit
e84d47540d
|
@ -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": {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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
51
package-lock.json
generated
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)));
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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 }));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'
|
||||
);
|
|
@ -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
|
||||
) {}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export * from './actions';
|
||||
export * from './effects';
|
||||
export * from './reducers';
|
||||
export * from './state';
|
|
@ -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
|
||||
};
|
||||
})
|
||||
);
|
|
@ -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)
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -5,5 +5,6 @@ export enum DeviceType {
|
|||
iOS = 'I',
|
||||
iOSCall = 'O',
|
||||
PC = 'P',
|
||||
iMessenger = 'H'
|
||||
iMessenger = 'H',
|
||||
Renderer = 'R'
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user