presence is modified

This commit is contained in:
Richard Park 2020-01-10 16:49:53 +09:00
parent ed00ae2a80
commit b7f1a16f28
21 changed files with 405 additions and 41 deletions

View File

@ -645,6 +645,16 @@ ipcMain.on(
}
);
ipcMain.on(
IdleStateChannel.ChangeLimitTime,
(event: IpcMainEvent, ...args: any[]) => {
const limitTime: number = args[0];
if (!!idle) {
idle.resetIdleTime(limitTime);
}
}
);
ipcMain.on(
NotificationChannel.Notify,
(event: IpcMainEvent, ...args: any[]) => {

View File

@ -150,6 +150,7 @@
<ucap-profile-my-profile-widget
[profileImageRoot]="sessionVerinfo.profileRoot"
[profileImageFile]="getMyProfileImageWidget()"
[presence]="myStatus"
*ngIf="!!loginRes"
class="myprofile-item"
[matMenuTriggerFor]="profileMenu"
@ -254,7 +255,19 @@
yPosition="below"
>
<div class="setting">
<button mat-menu-item [matMenuTriggerFor]="presenseMenu">대화 가능</button>
<button mat-menu-item [matMenuTriggerFor]="presenseMenu">
<ng-container [ngSwitch]="myStatus.pcStatus">
<ng-container *ngSwitchCase="StatusCode.OnLine">
{{ 'presence.online' | translate }}
</ng-container>
<ng-container *ngSwitchCase="StatusCode.Away">
{{ 'presence.away' | translate }}
</ng-container>
<ng-container *ngSwitchCase="StatusCode.Busy">
{{ myStatus.statusMessage }}
</ng-container>
</ng-container>
</button>
</div>
<div class="setting">
<button mat-menu-item (click)="onClickOpenProfile($event)">
@ -302,28 +315,83 @@
<mat-menu #presenseMenu="matMenu" class="status-pc-set">
<div class="setting">
<button mat-menu-item>
<span class="presence pcOn"> </span>{{ 'presence.online' | translate }}
<button mat-menu-item (click)="onClickStatusOnline($event)">
<span class="presence pcOn"> </span>
{{ 'presence.online' | translate }}
</button>
</div>
<div class="setting">
<button mat-menu-item>
<span class="presence pcOut"> </span
>{{ 'presence.offline' | translate }}</button
><button mat-menu-item class="clock"></button>
<button mat-menu-item (click)="onClickStatusAway($event)">
<span class="presence pcOut"> </span>
{{ 'presence.away' | translate }}
</button>
<button
mat-menu-item
class="clock"
[matMenuTriggerFor]="awayTimeMenu"
></button>
</div>
<div class="setting">
<button mat-menu-item>
<span class="presence pcOther"> </span>다른용무중</button
><button mat-menu-item class="edit"></button>
<button mat-menu-item (click)="onClickStatusBusy($event, 1)">
<span class="presence pcOther"> </span>
{{ loginRes?.statusMessage1 }}
</button>
<button
mat-menu-item
class="edit"
(click)="onClickChangeStatusBusy($event, 1)"
></button>
</div>
<div class="setting">
<button mat-menu-item><span class="presence pcOther"> </span>회의중</button
><button mat-menu-item class="edit"></button>
<button mat-menu-item (click)="onClickStatusBusy($event, 2)">
<span class="presence pcOther"> </span>
{{ loginRes?.statusMessage2 }}
</button>
<button
mat-menu-item
class="edit"
(click)="onClickChangeStatusBusy($event, 2)"
></button>
</div>
<div class="setting">
<button mat-menu-item>
<span class="presence pcOther"> </span>집중근무중</button
><button mat-menu-item class="edit"></button>
<button mat-menu-item (click)="onClickStatusBusy($event, 3)">
<span class="presence pcOther"> </span>
{{ loginRes?.statusMessage3 }}
</button>
<button
mat-menu-item
class="edit"
(click)="onClickChangeStatusBusy($event, 3)"
></button>
</div>
</mat-menu>
<mat-menu #awayTimeMenu="matMenu">
<div mat-menu-item (click)="$event.stopPropagation()">
{{ 'presence.settingOfAwayTime' | translate }}
</div>
<div mat-menu-item>
<mat-radio-button
value="10"
[checked]="myIdleCheckTime === 10"
(change)="onChangeAwayTime($event)"
>10{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div>
<div mat-menu-item>
<mat-radio-button
value="20"
[checked]="myIdleCheckTime === 20"
(change)="onChangeAwayTime($event)"
>20{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div>
<div mat-menu-item>
<mat-radio-button
value="30"
[checked]="myIdleCheckTime === 30"
(change)="onChangeAwayTime($event)"
>30{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div>
</mat-menu>

View File

@ -20,6 +20,7 @@ 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 * as StatusStore from '@app/store/messenger/status';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { tap, take, map, catchError } from 'rxjs/operators';
@ -55,7 +56,9 @@ import {
} from '@app/layouts/messenger/dialogs/profile/profile.dialog.component';
import { DialogService } from '@ucap-webmessenger/ui';
import { DOCUMENT } from '@angular/common';
import { MatMenu } from '@angular/material';
import { MatMenu, MatRadioChange } from '@angular/material';
import { StatusCode, StatusType } from '@ucap-webmessenger/core';
import { StatusInfo } from '@ucap-webmessenger/protocol-status';
@Component({
selector: 'app-layout-native-top-bar',
@ -72,12 +75,19 @@ export class TopBarComponent implements OnInit, OnDestroy {
updateInfo$: Observable<UpdateInfo>;
myStatus: StatusInfo;
myStatusSubscription: Subscription;
myIdleCheckTime: number;
myIdleCheckTimeSubscription: Subscription;
loginInfo: LoginInfo;
weblink: WebLink[] = [];
webLinkBadgeMail = 0;
webLinkBadgePayment = 0;
WebLinkType = WebLinkType;
StatusCode = StatusCode;
@ViewChild('profileMenu', { static: true })
profileMenu: MatMenu;
@ -118,11 +128,39 @@ export class TopBarComponent implements OnInit, OnDestroy {
)
.subscribe();
this.myStatusSubscription = this.store
.pipe(select(AppStore.MessengerSelector.StatusSelector.selectedMyStatus))
.subscribe(myStatus => {
this.myStatus = myStatus;
});
this.myIdleCheckTimeSubscription = this.store
.pipe(
select(
AppStore.MessengerSelector.StatusSelector.selectedMyIdleCheckTime
)
)
.subscribe(myIdleCheckTime => {
this.myIdleCheckTime = myIdleCheckTime;
});
this.updateInfo$ = this.store.pipe(
select(AppStore.SettingSelector.UpdateSelector.updateInfo)
);
}
ngOnDestroy(): void {
if (!!this.loginResSubscription) {
this.loginResSubscription.unsubscribe();
}
if (!!this.myStatusSubscription) {
this.myStatusSubscription.unsubscribe();
}
if (!!this.myIdleCheckTimeSubscription) {
this.myIdleCheckTimeSubscription.unsubscribe();
}
}
initWebLink(): void {
const loginRes = this.sessionStorageService.get<LoginResponse>(
KEY_LOGIN_RES_INFO
@ -207,12 +245,6 @@ export class TopBarComponent implements OnInit, OnDestroy {
}
}
ngOnDestroy(): void {
if (!!this.loginResSubscription) {
this.loginResSubscription.unsubscribe();
}
}
onClickClose() {
this.nativeService.windowClose();
}
@ -343,4 +375,61 @@ export class TopBarComponent implements OnInit, OnDestroy {
onClickRemoteSupport(event: Event) {
this.nativeService.executeProcess('AeroAdmin');
}
onClickStatusOnline(event: Event) {
this.store.dispatch(
StatusStore.status({
req: {
statusDivisionType: StatusType.Messenger,
statusType: StatusCode.OnLine
}
})
);
}
onClickStatusAway(event: Event) {
this.store.dispatch(
StatusStore.status({
req: {
statusDivisionType: StatusType.Messenger,
statusType: StatusCode.Away
}
})
);
}
onClickStatusBusy(event: Event, index: number) {
let statusMessage = '';
switch (index) {
case 1:
statusMessage = this.loginRes.statusMessage1;
break;
case 2:
statusMessage = this.loginRes.statusMessage2;
break;
case 3:
statusMessage = this.loginRes.statusMessage3;
break;
}
this.store.dispatch(
StatusStore.status({
req: {
statusDivisionType: StatusType.Messenger,
statusType: StatusCode.Busy,
statusMessage
}
})
);
}
onClickChangeStatusBusy(event: Event, index: number) {
event.stopPropagation();
}
onChangeAwayTime(event: MatRadioChange) {
this.store.dispatch(
StatusStore.changeMyIdleCheckTime({ checkTime: Number(event.value) })
);
}
}

View File

@ -6,6 +6,7 @@ import { FlexLayoutModule } from '@angular/flex-layout';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatRadioModule } from '@angular/material/radio';
import { MatToolbarModule } from '@angular/material/toolbar';
import { TranslateModule } from '@ngx-translate/core';
@ -25,6 +26,7 @@ import { MatTooltipModule, MatBadgeModule } from '@angular/material';
MatToolbarModule,
MatTooltipModule,
MatMenuModule,
MatRadioModule,
MatBadgeModule,
TranslateModule,

View File

@ -13,6 +13,7 @@ import { Store, select } from '@ngrx/store';
import * as AppStore from '@app/store';
import * as ChatStore from '@app/store/messenger/chat';
import * as MessageStore from '@app/store/messenger/message';
import * as StatusStore from '@app/store/messenger/status';
import { Observable, Subscription, of } from 'rxjs';
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
@ -59,6 +60,7 @@ export class MainPageComponent implements OnInit, OnDestroy {
idleStateChangedSubscription: Subscription;
chatOpenRoomSubscription: Subscription;
msgOpenMessageSubscription: Subscription;
myIdleCheckTimeSubscription: Subscription;
defaultLeftSideComponentWidth = 380;
leftSideComponentWidth = this.defaultLeftSideComponentWidth;
@ -122,11 +124,39 @@ export class MainPageComponent implements OnInit, OnDestroy {
statusType = StatusCode.OnLine;
}
this.statusProtocolService.status({
this.store.dispatch(
StatusStore.status({
req: {
statusDivisionType: StatusType.Messenger,
statusType,
statusMessage: ''
}
})
);
});
this.myIdleCheckTimeSubscription = this.store
.pipe(
select(
AppStore.MessengerSelector.StatusSelector.selectedMyIdleCheckTime
)
)
.subscribe(checkTime => {
this.nativeService.changeLimitOfIdleState(checkTime);
const appUserInfo = this.localStorageService.encGet<AppUserInfo>(
KEY_APP_USER_INFO,
environment.customConfig.appKey
);
this.localStorageService.encSet<AppUserInfo>(
KEY_APP_USER_INFO,
{
...appUserInfo,
idleCheckTime: checkTime
},
environment.customConfig.appKey
);
});
this.chatOpenRoomSubscription = this.nativeService
@ -186,6 +216,10 @@ export class MainPageComponent implements OnInit, OnDestroy {
this.loginResSubscription.unsubscribe();
}
if (!!this.myIdleCheckTimeSubscription) {
this.myIdleCheckTimeSubscription.unsubscribe();
}
this.logger.debug('-----------------------MainPageComponent ngOnDestroy');
}

View File

@ -17,7 +17,10 @@ import {
import { Store, select } from '@ngrx/store';
import { ProtocolService, ServerErrorCode } from '@ucap-webmessenger/protocol';
import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import {
SessionStorageService,
LocalStorageService
} from '@ucap-webmessenger/web-storage';
import {
PublicApiService,
VersionInfo2Response
@ -54,6 +57,7 @@ import * as VersionInfoStore from '@app/store/setting/version-info';
import * as OptionStore from '@app/store/messenger/option';
import * as QueryStore from '@app/store/messenger/query';
import * as SyncStore from '@app/store/messenger/sync';
import * as StatusStore from '@app/store/messenger/status';
import { KEY_LOGIN_RES_INFO, KEY_VER_INFO } from '@app/types';
import { environment } from '../../environments/environment';
@ -66,6 +70,7 @@ import {
} from '@ucap-webmessenger/api-external';
import { StatusCode } from '@ucap-webmessenger/api';
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
import { AppUserInfo, KEY_APP_USER_INFO } from '@app/types/app-user-info.type';
@Injectable()
export class AppMessengerResolver implements Resolve<void> {
@ -73,6 +78,7 @@ export class AppMessengerResolver implements Resolve<void> {
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private store: Store<any>,
private sessionStorageService: SessionStorageService,
private localStorageService: LocalStorageService,
private publicApiService: PublicApiService,
private externalApiService: ExternalApiService,
private protocolService: ProtocolService,
@ -264,6 +270,17 @@ export class AppMessengerResolver implements Resolve<void> {
);
this.store.dispatch(AuthenticationStore.postLogin({ loginRes }));
this.appNativeService.subscribeAfterLogin();
const appUserInfo = this.localStorageService.encGet<AppUserInfo>(
KEY_APP_USER_INFO,
environment.customConfig.appKey
);
this.store.dispatch(
StatusStore.changeMyIdleCheckTime({
checkTime: appUserInfo.idleCheckTime
})
);
resolve();
},
err => {

View File

@ -64,6 +64,7 @@ export class AppAuthenticationService {
if (!appUserInfo) {
appUserInfo = {
idleCheckTime: 10,
settings: {
...environment.productConfig.defaultSettings,
chat: {

View File

@ -2,7 +2,9 @@ import { createAction, props } from '@ngrx/store';
import {
BulkInfoRequest,
StatusBulkInfo,
StatusNotification
StatusNotification,
StatusRequest,
StatusResponse
} from '@ucap-webmessenger/protocol-status';
export const bulkInfo = createAction(
@ -24,3 +26,23 @@ export const statusNotification = createAction(
'[Messenger::Status] Status Notification',
props<{ noti: StatusNotification }>()
);
export const status = createAction(
'[Messenger::Status] Status',
props<{ req: StatusRequest }>()
);
export const statusSuccess = createAction(
'[Messenger::Status] Status Success',
props<{
res: StatusResponse;
}>()
);
export const statusFailure = createAction(
'[Messenger::Status] Status Failure',
props<{ error: any }>()
);
export const changeMyIdleCheckTime = createAction(
'[Messenger::Status] Change MyIdleCheckTime',
props<{ checkTime: number }>()
);

View File

@ -10,15 +10,18 @@ import {
bulkInfo,
bulkInfoSuccess,
bulkInfoFailure,
statusNotification
status,
statusFailure,
statusSuccess
} from './actions';
import { tap, switchMap, map, catchError } from 'rxjs/operators';
import { tap, switchMap, map, catchError, exhaustMap } from 'rxjs/operators';
import {
StatusProtocolService,
SSVC_TYPE_STATUS_BULK_INFO_DATA,
SSVC_TYPE_STATUS_BULK_INFO_RES,
BulkInfoData,
StatusBulkInfo
StatusBulkInfo,
StatusResponse
} from '@ucap-webmessenger/protocol-status';
import { of } from 'rxjs';
@ -84,6 +87,21 @@ export class Effects {
// { dispatch: false }
// );
status$ = createEffect(() =>
this.actions$.pipe(
ofType(status),
map(action => action.req),
exhaustMap(req => {
return this.statusProtocolService.status(req).pipe(
map((res: StatusResponse) => {
return statusSuccess({ res });
}),
catchError(error => of(statusFailure({ error })))
);
})
)
);
constructor(
private actions$: Actions,
private store: Store<any>,

View File

@ -1,6 +1,11 @@
import { createReducer, on } from '@ngrx/store';
import { initialState, State, adapterStatusBulkInfo } from './state';
import { bulkInfoSuccess, statusNotification } from './actions';
import {
bulkInfoSuccess,
statusNotification,
statusSuccess,
changeMyIdleCheckTime
} from './actions';
import * as AuthenticationStore from '@app/store/account/authentication';
import { StatusBulkInfo } from '@ucap-webmessenger/protocol-status';
@ -42,6 +47,24 @@ export const reducer = createReducer(
};
}),
on(statusSuccess, (state, action) => {
return {
...state,
myStatus: {
...state.myStatus,
pcStatus: action.res.statusType,
statusMessage: action.res.statusMessage
}
} as State;
}),
on(changeMyIdleCheckTime, (state, action) => {
return {
...state,
myIdleCheckTime: action.checkTime
} as State;
}),
on(AuthenticationStore.logoutInitialize, (state, action) => {
return {
...initialState

View File

@ -1,11 +1,14 @@
import { Selector, createSelector } from '@ngrx/store';
import { StatusBulkInfo } from '@ucap-webmessenger/protocol-status';
import { StatusBulkInfo, StatusInfo } from '@ucap-webmessenger/protocol-status';
import { EntityState, createEntityAdapter } from '@ngrx/entity';
import { StatusCode } from '@ucap-webmessenger/core';
export interface StatusBulkInfoState extends EntityState<StatusBulkInfo> {}
export interface State {
statusBulkInfo: StatusBulkInfoState;
myStatus: StatusInfo;
myIdleCheckTime: number;
}
export const adapterStatusBulkInfo = createEntityAdapter<StatusBulkInfo>({
@ -17,7 +20,18 @@ const statusBulkInfoInitialState: StatusBulkInfoState = adapterStatusBulkInfo.ge
);
export const initialState: State = {
statusBulkInfo: statusBulkInfoInitialState
statusBulkInfo: statusBulkInfoInitialState,
myStatus: {
userSeq: -1,
pcStatus: StatusCode.OnLine,
phoneStatus: StatusCode.Offline,
mobileStatus: StatusCode.Offline,
conferenceStatus: StatusCode.Offline,
statusMessage: '',
mobileConferenceStatus: StatusCode.Offline,
imessengerStatus: StatusCode.Offline
},
myIdleCheckTime: 10
};
const {
@ -47,6 +61,14 @@ export function selectors<S>(selector: Selector<any, State>) {
selectStatusBulkInfo,
ngeSelectEntitiesStatusBulkInfo,
(_, entities) => (!!entities ? entities[userSeq] : undefined)
),
selectedMyStatus: createSelector(
selector,
(state: State) => state.myStatus
),
selectedMyIdleCheckTime: createSelector(
selector,
(state: State) => state.myIdleCheckTime
)
};
}

View File

@ -10,6 +10,7 @@ export interface AppUserInfo {
companyCode?: string;
companyGroupType?: string;
localeCode?: LocaleCode;
idleCheckTime?: number;
settings?: Settings;
}

View File

@ -111,8 +111,12 @@
}
},
"presence": {
"settingOfAwayTime": "Setting of away time",
"online": "Online",
"offline": "Offline"
"away": "Away",
"statusMessage1": "Busy",
"statusMessage2": "In conference",
"statusMessage3": "In intensive work"
},
"group": {
"label": "Group",

View File

@ -111,8 +111,12 @@
}
},
"presence": {
"settingOfAwayTime": "부재 중 시간 설정",
"online": "온라인",
"offline": "부재중"
"away": "부재중",
"statusMessage1": "다른용무중",
"statusMessage2": "회의중",
"statusMessage3": "집중근무중"
},
"group": {
"label": "그룹",

View File

@ -226,6 +226,8 @@ export class BrowserNativeService extends NativeService {
});
}
changeLimitOfIdleState(limitTime: number): void {}
chatOpenRoom(): Observable<string> {
if (!this.chatOpenRoomSubject) {
this.chatOpenRoomSubject = new Subject<WindowIdle>();

View File

@ -396,6 +396,10 @@ export class ElectronNativeService implements NativeService {
return this.idleStateChanged$;
}
changeLimitOfIdleState(limitTime: number): void {
this.ipcRenderer.send(IdleStateChannel.ChangeLimitTime, limitTime);
}
chatOpenRoom(): Observable<string> {
if (!this.chatOpenRoomSubject) {
this.chatOpenRoomSubject = new Subject<WindowIdle>();

View File

@ -49,5 +49,6 @@ export enum WindowStateChannel {
export enum IdleStateChannel {
Changed = 'UCAP::idleState::changed',
StartCheck = 'UCAP::idleState::startCheck'
StartCheck = 'UCAP::idleState::startCheck',
ChangeLimitTime = 'UCAP::idleState::changeLimitTime'
}

View File

@ -68,6 +68,7 @@ export abstract class NativeService {
abstract windowMaximize(): void;
abstract idleStateChanged(): Observable<WindowIdle>;
abstract changeLimitOfIdleState(limitTime: number): void;
abstract chatOpenRoom(): Observable<string>;
abstract msgOpenMessage(): Observable<string>;

View File

@ -9,7 +9,7 @@
/>
</span>
<div class="btn-setting">
<span class="presence pcOff"></span>
<span class="presence" [ngClass]="getPresence(PresenceType.PC)"></span>
<button>
<svg
xmlns="http://www.w3.org/2000/svg"

View File

@ -1,5 +1,8 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { Component, OnInit, Input } from '@angular/core';
import { NGXLogger } from 'ngx-logger';
import { PresenceType } from '../types/presence-type.type';
import { StatusBulkInfo, StatusInfo } from '@ucap-webmessenger/protocol-status';
import { StatusCode } from '@ucap-webmessenger/core';
@Component({
selector: 'ucap-profile-my-profile-widget',
@ -11,8 +14,44 @@ export class MyProfileWidgetComponent implements OnInit {
profileImageRoot: string;
@Input()
profileImageFile: string;
@Input()
presence: StatusBulkInfo | StatusInfo;
PresenceType = PresenceType;
constructor(private logger: NGXLogger) {}
ngOnInit() {}
getPresence(type: PresenceType): string {
let status: string;
let rtnClass = '';
switch (type) {
case PresenceType.PC:
status = !!this.presence ? this.presence.pcStatus : undefined;
break;
case PresenceType.MOBILE:
status = !!this.presence ? this.presence.mobileStatus : undefined;
break;
}
if (!!status) {
switch (status) {
case StatusCode.OnLine:
rtnClass = type + 'On';
break;
case StatusCode.Away:
rtnClass = type + 'Out';
break;
case StatusCode.Busy:
rtnClass = type + 'Other';
break;
default:
rtnClass = type + 'Off';
break;
}
}
return rtnClass;
}
}

View File

@ -44,6 +44,8 @@ export class NotificationComponent implements OnInit, OnDestroy {
ngOnInit() {
this.logger.debug('setting', this.setting);
this.setNotificationMethodList();
this.langChangeSubscription = merge(
this.translateService.onLangChange,
this.translateService.onDefaultLangChange,