This commit is contained in:
leejinho 2019-11-19 19:16:03 +09:00
commit 06a71351d3
23 changed files with 251 additions and 65 deletions

View File

@ -4,7 +4,7 @@ services:
nginx: nginx:
image: nginx:1.17.5-alpine image: nginx:1.17.5-alpine
volumes: volumes:
- ../dist/ucap-webmessenger-app-browser:/usr/share/nginx/html:ro - ../dist/web:/usr/share/nginx/html:ro
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
ports: ports:
- 8099:80 - 8099:80

View File

@ -21,7 +21,8 @@ import {
FileChannel, FileChannel,
IdleStateChannel, IdleStateChannel,
NotificationChannel, NotificationChannel,
ChatChannel ChatChannel,
MessengerChannel
} from '@ucap-webmessenger/native-electron'; } from '@ucap-webmessenger/native-electron';
import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification'; import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification';
@ -156,18 +157,7 @@ function createWindow() {
appWindow = window; appWindow = window;
} }
// This method will be called when Electron has finished function createTray() {
// 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();
appTray = new Tray(appIconPath); appTray = new Tray(appIconPath);
const contextMenu = Menu.buildFromTemplate([ const contextMenu = Menu.buildFromTemplate([
@ -176,22 +166,15 @@ app.on(ElectronAppChannel.Ready, () => {
// accelerator: 'Q', // accelerator: 'Q',
// selector: 'terminate:', // selector: 'terminate:',
click: () => { click: () => {
// 로그아웃 후 로그인화면. appWindow.browserWindow.webContents.send(MessengerChannel.Logout);
const options = { }
type: 'question', },
buttons: ['취소', '로그아웃'], {
defaultId: 2, label: '설정',
title: 'Question', // accelerator: 'Q',
message: '로그아웃', // selector: 'terminate:',
detail: '로그아웃 하시겠습니까?' click: () => {
// checkboxLabel: 'Remember my answer', appWindow.browserWindow.webContents.send(MessengerChannel.ShowSetting);
// checkboxChecked: true,
};
const choice = dialog.showMessageBoxSync(null, options);
if (1 === choice) {
// logout
appWindow.browserWindow.webContents.send(ChatChannel.OpenRoom);
}
} }
}, },
{ label: '버전', submenu: [{ label: 'Ver. ' + app.getVersion() }] }, { label: '버전', submenu: [{ label: 'Ver. ' + app.getVersion() }] },
@ -212,6 +195,21 @@ app.on(ElectronAppChannel.Ready, () => {
appTray.on('click', () => { appTray.on('click', () => {
appWindow.isVisible() ? appWindow.hide() : appWindow.show(); appWindow.isVisible() ? appWindow.hide() : appWindow.show();
}); });
}
// 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();
createTray();
notificationService = new ElectronNotificationService({ notificationService = new ElectronNotificationService({
width: 340, width: 340,

View File

@ -10,7 +10,7 @@
"start:browser": "cross-env UCAP_ENV_RUNTIME=BROWSER ng serve -c browser-development -o", "start:browser": "cross-env UCAP_ENV_RUNTIME=BROWSER ng serve -c browser-development -o",
"build": "npm-run-all -p build:renderer build:main:production", "build": "npm-run-all -p build:renderer build:main:production",
"build:renderer": "cross-env NODE_ENV=production ng build -c renderer-development --base-href ./", "build:renderer": "cross-env NODE_ENV=production ng build -c renderer-development --base-href ./",
"build:browser": "cross-env UCAP_ENV_RUNTIME=BROWSER ng build -c browser-development", "build:browser": "cross-env UCAP_ENV_RUNTIME=BROWSER ng build -c browser-development --base-href ./NextMessenger_POC",
"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: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", "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",
"electron:local": "electron .", "electron:local": "electron .",

View File

@ -5,6 +5,7 @@ import { DeviceType } from '@ucap-webmessenger/core';
import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types'; import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types';
import { AppNotificationService } from './notification.service'; import { AppNotificationService } from './notification.service';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { AppNativeService } from './native.service';
@Injectable() @Injectable()
export class AppService { export class AppService {
@ -12,6 +13,7 @@ export class AppService {
private enviromentUtilService: EnviromentUtilService, private enviromentUtilService: EnviromentUtilService,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
private appNotificationService: AppNotificationService, private appNotificationService: AppNotificationService,
private appNativeService: AppNativeService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService
) {} ) {}
@ -32,10 +34,11 @@ export class AppService {
this.sessionStorageService.set<EnvironmentsInfo>( this.sessionStorageService.set<EnvironmentsInfo>(
KEY_ENVIRONMENTS_INFO, KEY_ENVIRONMENTS_INFO,
{ {
deviceType, deviceType
} }
); );
this.appNativeService.subscribe();
this.appNotificationService.subscribe(); this.appNotificationService.subscribe();
this.nativeService.postAppInit(); this.nativeService.postAppInit();
resolve(); resolve();

View File

@ -2,10 +2,12 @@ import { AppService } from './app.service';
import { AppAuthenticationService } from './authentication.service'; import { AppAuthenticationService } from './authentication.service';
import { AppLoaderService } from './loader.service'; import { AppLoaderService } from './loader.service';
import { AppNotificationService } from './notification.service'; import { AppNotificationService } from './notification.service';
import { AppNativeService } from './native.service';
export const SERVICES = [ export const SERVICES = [
AppService, AppService,
AppAuthenticationService, AppAuthenticationService,
AppLoaderService, AppLoaderService,
AppNotificationService AppNotificationService,
AppNativeService
]; ];

View File

@ -0,0 +1,25 @@
import { Injectable, Inject } from '@angular/core';
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
import { Store } from '@ngrx/store';
import { NGXLogger } from 'ngx-logger';
import * as AuthenticationStore from '@app/store/account/authentication';
@Injectable({
providedIn: 'root'
})
export class AppNativeService {
constructor(
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private store: Store<any>,
private logger: NGXLogger
) {}
subscribe(): void {
this.nativeService.logout().subscribe(() => {
this.store.dispatch(AuthenticationStore.logout());
});
this.nativeService.changeStatus().subscribe(statusCode => {});
this.nativeService.showSetting().subscribe(() => {});
}
}

View File

@ -127,7 +127,7 @@ export class AppNotificationService {
default: default:
break; break;
} }
this.store.dispatch(AuthenticationStore.logout()); this.store.dispatch(AuthenticationStore.loginRedirect());
}) })
) )
.subscribe(); .subscribe();

View File

@ -66,6 +66,10 @@ export const logoutConfirmationDismiss = createAction(
'[Account::Authentication] Logout Confirmation Dismiss' '[Account::Authentication] Logout Confirmation Dismiss'
); );
export const logoutInitialize = createAction(
'[Account::Authentication] Logout Initialize'
);
export const postLogin = createAction( export const postLogin = createAction(
'[Account::Authentication] Post Login', '[Account::Authentication] Post Login',
props<{ props<{

View File

@ -1,4 +1,4 @@
import { Injectable, Inject } from '@angular/core'; import { Injectable, Inject, NgZone } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { of, Observable } from 'rxjs'; import { of, Observable } from 'rxjs';
@ -36,7 +36,8 @@ import {
changePasswordFailure, changePasswordFailure,
changePasswordSuccess, changePasswordSuccess,
increaseLoginFailCount, increaseLoginFailCount,
initialLoginFailCount initialLoginFailCount,
logoutInitialize
} from './actions'; } from './actions';
import { import {
LoginInfo, LoginInfo,
@ -52,6 +53,7 @@ import {
ServiceProtocolService, ServiceProtocolService,
UserPasswordSetResponse UserPasswordSetResponse
} from '@ucap-webmessenger/protocol-service'; } from '@ucap-webmessenger/protocol-service';
import { AuthenticationProtocolService } from '@ucap-webmessenger/protocol-authentication';
@Injectable() @Injectable()
export class Effects { export class Effects {
@ -122,21 +124,31 @@ export class Effects {
this.actions$.pipe( this.actions$.pipe(
ofType(loginRedirect), ofType(loginRedirect),
tap(authed => { tap(authed => {
this.router.navigate(['/account/login']); this.ngZone.run(() => {
this.appAuthenticationService.logout();
this.store.dispatch(logoutInitialize());
location.href = this.router.parseUrl('/account/login').toString();
});
}) })
), ),
{ dispatch: false } { dispatch: false }
); );
logout$ = createEffect(() => logout$ = createEffect(
this.actions$.pipe( () => {
ofType(logout), return this.actions$.pipe(
map(action => action), ofType(logout),
map(() => { switchMap(action => {
this.appAuthenticationService.logout(); return this.authenticationProtocolService.logout({}).pipe(
return loginRedirect(); map(res => {
}) this.store.dispatch(loginRedirect());
) }),
catchError(error => of(error))
);
})
);
},
{ dispatch: false }
); );
logoutConfirmation$ = createEffect(() => logoutConfirmation$ = createEffect(() =>
@ -288,11 +300,13 @@ export class Effects {
constructor( constructor(
private actions$: Actions, private actions$: Actions,
private ngZone: NgZone,
private router: Router, private router: Router,
private store: Store<any>, private store: Store<any>,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
private piService: PiService, private piService: PiService,
private appAuthenticationService: AppAuthenticationService, private appAuthenticationService: AppAuthenticationService,
private authenticationProtocolService: AuthenticationProtocolService,
private serviceProtocolService: ServiceProtocolService, private serviceProtocolService: ServiceProtocolService,
private dialogService: DialogService, private dialogService: DialogService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,

View File

@ -3,7 +3,9 @@ import { State, initialState } from './state';
import { import {
loginSuccess, loginSuccess,
increaseLoginFailCount, increaseLoginFailCount,
initialLoginFailCount initialLoginFailCount,
logout,
logoutInitialize
} from './actions'; } from './actions';
export const reducer = createReducer( export const reducer = createReducer(
@ -21,10 +23,17 @@ export const reducer = createReducer(
loginFailCount: state.loginFailCount + 1 loginFailCount: state.loginFailCount + 1
}; };
}), }),
on(initialLoginFailCount, (state, action) => { on(initialLoginFailCount, (state, action) => {
return { return {
...state, ...state,
loginFailCount: 0 loginFailCount: 0
}; };
}),
on(logoutInitialize, (state, action) => {
return {
...initialState
};
}) })
); );

View File

@ -11,6 +11,8 @@ import {
clearRightDrawer clearRightDrawer
} from './actions'; } from './actions';
import * as AuthenticationStore from '@app/store/account/authentication';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
on(selectedRoom, (state, action) => { on(selectedRoom, (state, action) => {
@ -64,5 +66,11 @@ export const reducer = createReducer(
...state, ...state,
selectedRightDrawer: null selectedRightDrawer: null
}; };
}),
on(AuthenticationStore.logoutInitialize, (state, action) => {
return {
...initialState
};
}) })
); );

View File

@ -128,12 +128,13 @@ export const reducer = createReducer(
}; };
}), }),
on(AuthenticationStore.logout, (state, action) => { on(ChatStore.clearSelectedRoom, (state, action) => {
return { return {
...initialState ...initialState
}; };
}), }),
on(ChatStore.clearSelectedRoom, (state, action) => {
on(AuthenticationStore.logoutInitialize, (state, action) => {
return { return {
...initialState ...initialState
}; };

View File

@ -17,7 +17,7 @@ export const reducer = createReducer(
reg: action.res reg: action.res
}; };
}), }),
on(AuthenticationStore.logout, (state, action) => { on(AuthenticationStore.logoutInitialize, (state, action) => {
return { return {
...initialState ...initialState
}; };

View File

@ -49,7 +49,7 @@ export const reducer = createReducer(
}; };
}), }),
on(AuthenticationStore.logout, (state, action) => { on(AuthenticationStore.logoutInitialize, (state, action) => {
return { return {
...initialState ...initialState
}; };

View File

@ -64,12 +64,13 @@ export const reducer = createReducer(
}; };
}), }),
on(AuthenticationStore.logout, (state, action) => { on(ChatStore.clearSelectedRoom, (state, action) => {
return { return {
...initialState ...initialState
}; };
}), }),
on(ChatStore.clearSelectedRoom, (state, action) => {
on(AuthenticationStore.logoutInitialize, (state, action) => {
return { return {
...initialState ...initialState
}; };

View File

@ -42,7 +42,7 @@ export const reducer = createReducer(
}; };
}), }),
on(AuthenticationStore.logout, (state, action) => { on(AuthenticationStore.logoutInitialize, (state, action) => {
return { return {
...initialState ...initialState
}; };

View File

@ -30,6 +30,8 @@ import * as RoomStore from '@app/store/messenger/room';
import { RoomInfo } from '@ucap-webmessenger/protocol-room'; import { RoomInfo } from '@ucap-webmessenger/protocol-room';
import { StringUtil } from '@ucap-webmessenger/ui'; import { StringUtil } from '@ucap-webmessenger/ui';
import * as AuthenticationStore from '@app/store/account/authentication';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
on(buddy2Success, (state, action) => { on(buddy2Success, (state, action) => {
@ -283,5 +285,11 @@ export const reducer = createReducer(
...state, ...state,
buddy2: adapterBuddy2.upsertOne(userInfo, { ...state.buddy2 }) buddy2: adapterBuddy2.upsertOne(userInfo, { ...state.buddy2 })
}; };
}),
on(AuthenticationStore.logoutInitialize, (state, action) => {
return {
...initialState
};
}) })
); );

View File

@ -1,4 +1,7 @@
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import * as AuthenticationStore from '@app/store/account/authentication';
import { initialState } from './state'; import { initialState } from './state';
import { companyListSuccess } from './actions'; import { companyListSuccess } from './actions';
@ -9,5 +12,11 @@ export const reducer = createReducer(
...state, ...state,
companyList: action.companyList companyList: action.companyList
}; };
}),
on(AuthenticationStore.logoutInitialize, (state, action) => {
return {
...initialState
};
}) })
); );

View File

@ -2,6 +2,8 @@ import { createReducer, on } from '@ngrx/store';
import { initialState } from './state'; import { initialState } from './state';
import { versionInfo2Success } from './actions'; import { versionInfo2Success } from './actions';
import * as AuthenticationStore from '@app/store/account/authentication';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
on(versionInfo2Success, (state, action) => { on(versionInfo2Success, (state, action) => {
@ -19,5 +21,11 @@ export const reducer = createReducer(
fileDownloadUrl: action.res.downloadUrl, fileDownloadUrl: action.res.downloadUrl,
serverIp: action.res.serverIp serverIp: action.res.serverIp
}; };
}),
on(AuthenticationStore.logoutInitialize, (state, action) => {
return {
...initialState
};
}) })
); );

View File

@ -4,7 +4,7 @@ import {
NativeService, NativeService,
WindowState, WindowState,
NotificationRequest, NotificationRequest,
WindowIdle, WindowIdle
} from '@ucap-webmessenger/native'; } from '@ucap-webmessenger/native';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { map, share } from 'rxjs/operators'; import { map, share } from 'rxjs/operators';
@ -12,10 +12,10 @@ import { TranslateLoader } from '@ngx-translate/core';
import { TranslateLoaderService } from '../translate/browser-loader'; import { TranslateLoaderService } from '../translate/browser-loader';
import { NotificationService } from '../notification/notification.service'; import { NotificationService } from '../notification/notification.service';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { FileUtil } from '@ucap-webmessenger/core'; import { FileUtil, StatusCode } from '@ucap-webmessenger/core';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root'
}) })
export class BrowserNativeService extends NativeService { export class BrowserNativeService extends NativeService {
private notificationService: NotificationService; private notificationService: NotificationService;
@ -27,6 +27,39 @@ export class BrowserNativeService extends NativeService {
this.notificationService.requestPermission(); this.notificationService.requestPermission();
} }
logout(): Observable<void> {
return new Observable<void>(subscriber => {
try {
} catch (error) {
subscriber.error(error);
} finally {
subscriber.complete();
}
});
}
changeStatus(): Observable<StatusCode> {
return new Observable<StatusCode>(subscriber => {
try {
} catch (error) {
subscriber.error(error);
} finally {
subscriber.complete();
}
});
}
showSetting(): Observable<void> {
return new Observable<void>(subscriber => {
try {
} catch (error) {
subscriber.error(error);
} finally {
subscriber.complete();
}
});
}
notify(noti: NotificationRequest): void { notify(noti: NotificationRequest): void {
this.notificationService.notify(noti, () => { this.notificationService.notify(noti, () => {
window.focus(); window.focus();

View File

@ -6,7 +6,7 @@ import {
NativeService, NativeService,
WindowState, WindowState,
NotificationRequest, NotificationRequest,
WindowIdle, WindowIdle
} from '@ucap-webmessenger/native'; } from '@ucap-webmessenger/native';
import { share } from 'rxjs/operators'; import { share } from 'rxjs/operators';
import { import {
@ -16,18 +16,29 @@ import {
WindowStateChannel, WindowStateChannel,
IdleStateChannel, IdleStateChannel,
ChatChannel, ChatChannel,
MessengerChannel
} from '../types/channel.type'; } from '../types/channel.type';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { TranslateLoaderService } from '../translate/electron-loader'; import { TranslateLoaderService } from '../translate/electron-loader';
import { TranslateLoader } from '@ngx-translate/core'; import { TranslateLoader } from '@ngx-translate/core';
import { StatusCode } from '@ucap-webmessenger/core';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root'
}) })
export class ElectronNativeService implements NativeService { export class ElectronNativeService implements NativeService {
private ipcRenderer: typeof ipcRenderer; private ipcRenderer: typeof ipcRenderer;
private remote: typeof remote; private remote: typeof remote;
private logoutSubject: Subject<void> | null = null;
private logout$: Observable<void> | null = null;
private changeStatusSubject: Subject<StatusCode> | null = null;
private changeStatus$: Observable<StatusCode> | null = null;
private showSettingSubject: Subject<void> | null = null;
private showSetting$: Observable<void> | null = null;
private windowStateChangedSubject: Subject<WindowState> | null = null; private windowStateChangedSubject: Subject<WindowState> | null = null;
private windowStateChanged$: Observable<WindowState> | null = null; private windowStateChanged$: Observable<WindowState> | null = null;
@ -39,6 +50,47 @@ export class ElectronNativeService implements NativeService {
postAppInit(): void {} postAppInit(): void {}
logout(): Observable<void> {
if (!this.logoutSubject) {
this.logoutSubject = new Subject<void>();
this.logout$ = this.logoutSubject.asObservable().pipe(share());
}
this.ipcRenderer.on(MessengerChannel.Logout, (event: any) => {
this.logoutSubject.next();
});
return this.logout$;
}
changeStatus(): Observable<StatusCode> {
if (!this.changeStatusSubject) {
this.changeStatusSubject = new Subject<StatusCode>();
this.changeStatus$ = this.changeStatusSubject
.asObservable()
.pipe(share());
}
this.ipcRenderer.on(
MessengerChannel.ChangeStatus,
(event: any, statusCode: StatusCode) => {
this.changeStatusSubject.next(statusCode);
}
);
return this.changeStatus$;
}
showSetting(): Observable<void> {
if (!this.showSettingSubject) {
this.showSettingSubject = new Subject<void>();
this.showSetting$ = this.showSettingSubject.asObservable().pipe(share());
}
this.ipcRenderer.on(MessengerChannel.ShowSetting, (event: any) => {
this.showSettingSubject.next();
});
return this.showSetting$;
}
notify(noti: NotificationRequest): void { notify(noti: NotificationRequest): void {
this.ipcRenderer.send(NotificationChannel.Notify, noti); this.ipcRenderer.send(NotificationChannel.Notify, noti);
} }

View File

@ -1,27 +1,33 @@
export enum MessengerChannel {
Logout = 'UCAP::messenger::logout',
ChangeStatus = 'UCAP::messenger::changeStatus',
ShowSetting = 'UCAP::messenger::showSetting'
}
export enum ChatChannel { export enum ChatChannel {
OpenRoom = 'UCAP::chat::openRoom', OpenRoom = 'UCAP::chat::openRoom'
} }
export enum NotificationChannel { export enum NotificationChannel {
Notify = 'UCAP::notification::notify', Notify = 'UCAP::notification::notify',
CloseAllNotify = 'UCAP::notification::closeAllNotify', CloseAllNotify = 'UCAP::notification::closeAllNotify'
} }
export enum UpdaterChannel { export enum UpdaterChannel {
Check = 'UCAP::updater::check', Check = 'UCAP::updater::check'
} }
export enum FileChannel { export enum FileChannel {
ShowImageViewer = 'UCAP::file::showImageViewer', ShowImageViewer = 'UCAP::file::showImageViewer',
SaveFile = 'UCAP::file::saveFile', SaveFile = 'UCAP::file::saveFile',
ReadFile = 'UCAP::file::readFile', ReadFile = 'UCAP::file::readFile'
} }
export enum WindowStateChannel { export enum WindowStateChannel {
Changed = 'UCAP::windowState::windowStateChanged', Changed = 'UCAP::windowState::windowStateChanged'
} }
export enum IdleStateChannel { export enum IdleStateChannel {
Changed = 'UCAP::idleState::changed', Changed = 'UCAP::idleState::changed',
StartCheck = 'UCAP::idleState::startCheck', StartCheck = 'UCAP::idleState::startCheck'
} }

View File

@ -4,10 +4,15 @@ import { WindowState } from '../types/window-state.type';
import { WindowIdle } from '../types/window-idle.type'; import { WindowIdle } from '../types/window-idle.type';
import { NotificationRequest } from '../models/notification'; import { NotificationRequest } from '../models/notification';
import { TranslateLoader } from '@ngx-translate/core'; import { TranslateLoader } from '@ngx-translate/core';
import { StatusCode } from '@ucap-webmessenger/core';
export abstract class NativeService { export abstract class NativeService {
abstract postAppInit(): void; abstract postAppInit(): void;
abstract logout(): Observable<void>;
abstract changeStatus(): Observable<StatusCode>;
abstract showSetting(): Observable<void>;
abstract notify(noti: NotificationRequest): void; abstract notify(noti: NotificationRequest): void;
abstract closeAllNotify(): void; abstract closeAllNotify(): void;