This commit is contained in:
khk 2020-01-23 12:26:45 +09:00
commit ddb52c3fb4
16 changed files with 534 additions and 335 deletions

View File

@ -28,7 +28,8 @@ import {
NotificationChannel, NotificationChannel,
ChatChannel, ChatChannel,
MessengerChannel, MessengerChannel,
MessageChannel MessageChannel,
AppChannel
} from '@ucap-webmessenger/native-electron'; } from '@ucap-webmessenger/native-electron';
import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification'; import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification';
import { ElectronUpdateWindowService } from '@ucap-webmessenger/electron-update-window'; import { ElectronUpdateWindowService } from '@ucap-webmessenger/electron-update-window';
@ -231,8 +232,7 @@ function createTray() {
// selector: 'terminate:', // selector: 'terminate:',
click: () => { click: () => {
// 메신저에 로그아웃 후 종료 // 메신저에 로그아웃 후 종료
appWindow = null; appExit();
app.exit();
} }
} }
]); ]);
@ -715,6 +715,10 @@ ipcMain.on(
} }
); );
ipcMain.on(AppChannel.Exit, (event: IpcMainEvent, ...args: any[]) => {
appExit();
});
autoUpdater.on('checking-for-update', () => { autoUpdater.on('checking-for-update', () => {
log.info('Checking for update...'); log.info('Checking for update...');
}); });
@ -748,3 +752,8 @@ autoUpdater.on('update-downloaded', info => {
updateWindowService.setDownloadComplete(); updateWindowService.setDownloadComplete();
autoUpdater.quitAndInstall(true, true); autoUpdater.quitAndInstall(true, true);
}); });
function appExit() {
appWindow = null;
app.exit();
}

View File

@ -563,7 +563,8 @@ export class GroupComponent implements OnInit, OnDestroy {
>(SelectGroupDialogComponent, { >(SelectGroupDialogComponent, {
width: '600px', width: '600px',
data: { data: {
title: this.translateService.instant('group.selectTargetGroup') title: this.translateService.instant('group.selectTargetGroup'),
ignoreGroup: [group]
} }
}); });

View File

@ -88,10 +88,10 @@
</mat-tab-group> </mat-tab-group>
</div> </div>
<div [style.display]="isSearch ? 'block' : 'none'"> <div class="message-section" [style.display]="isSearch ? 'block' : 'none'">
<div class="search-sub"> <div class="search-sub">
<form [formGroup]="fgSearchType" class="w-100-p"> <form [formGroup]="fgSearchType" class="w-100-p">
<mat-form-field> <mat-form-field style="width: 100px;">
<mat-select <mat-select
formControlName="searchMessageType" formControlName="searchMessageType"
(selectionChange)="onChangeSelection($event)" (selectionChange)="onChangeSelection($event)"
@ -127,13 +127,15 @@
</mat-radio-group> </mat-radio-group>
</form> </form>
</div> </div>
<div> <div style="height: calc(100% - 65.5px)">
<ucap-message-list-item <perfect-scrollbar fxFlex="1 1 auto">
*ngFor="let message of messageSearchList$ | async" <ucap-message-list-item
[message]="message" *ngFor="let message of messageSearchList$ | async"
(click)="onClickDetail(message)" [message]="message"
class="message-item" (click)="onClickDetail(message)"
></ucap-message-list-item> class="message-item"
></ucap-message-list-item>
</perfect-scrollbar>
</div> </div>
</div> </div>
</div> </div>

View File

@ -81,6 +81,7 @@
(click)="onToggleUser(userInfo)" (click)="onToggleUser(userInfo)"
(contextmenu)="onContextMenuOrgUser($event, userInfo)" (contextmenu)="onContextMenuOrgUser($event, userInfo)"
[matTooltip]="getTooltip(userInfo)" [matTooltip]="getTooltip(userInfo)"
matTooltipShowDelay="500"
matTooltipPosition="after" matTooltipPosition="after"
> >
</ucap-profile-user-list-item> </ucap-profile-user-list-item>
@ -105,6 +106,7 @@
(click)="onToggleUser(userInfo)" (click)="onToggleUser(userInfo)"
(contextmenu)="onContextMenuOrgUser($event, userInfo)" (contextmenu)="onContextMenuOrgUser($event, userInfo)"
[matTooltip]="getTooltip(userInfo)" [matTooltip]="getTooltip(userInfo)"
matTooltipShowDelay="500"
matTooltipPosition="after" matTooltipPosition="after"
> >
</ucap-profile-user-list-item> </ucap-profile-user-list-item>

View File

@ -638,170 +638,190 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
if (!!this.isShowTranslation && this.destLocale.trim().length > 0) { if (!!this.isShowTranslation && this.destLocale.trim().length > 0) {
/** CASE : Translation */ /** CASE : Translation */
const destLocale = this.destLocale; // 번역할 대화 없이 스티커만 전송할 경우.
const original = message; if (!message || message.trim().length === 0) {
const roomSeq = this.roomInfoSubject.value.roomSeq; this.sendMessageOfSticker(message);
} else {
if (!!this.isTranslationProcess) { this.sendMessageOfTranslate(message);
return;
} }
this.isTranslationProcess = true;
this.commonApiService
.translationSave({
userSeq: this.loginResSubject.value.userSeq,
deviceType: this.environmentsInfo.deviceType,
token: this.loginResSubject.value.tokenString,
roomSeq,
original,
srcLocale: '',
destLocale
} as TranslationSaveRequest)
.pipe(
take(1),
map(res => {
if (res.statusCode === StatusCode.Success) {
let sentMessage = '';
let eventType = EventType.Translation;
let previewObject:
| TranslationEventJson
| MassTranslationEventJson;
if (res.translationSeq > 0) {
// Mass Text Translation
previewObject = res;
sentMessage = res.returnJson;
eventType = EventType.MassTranslation;
} else {
// Normal Text Translation
previewObject = {
locale: destLocale,
original,
translation: res.translation,
stickername: '',
stickerfile: !!this.selectedSticker
? this.selectedSticker.index
: ''
};
sentMessage = JSON.stringify(previewObject);
eventType = EventType.Translation;
}
if (!!this.translationPreview) {
// preview
this.translationPreviewInfo = {
previewInfo: res,
translationType: eventType
};
} else {
// direct send
this.store.dispatch(
EventStore.send({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq,
eventType,
sentMessage
}
})
);
if (!!this.translationPreviewInfo) {
this.translationPreviewInfo = null;
}
}
if (!!this.selectedSticker) {
this.isShowStickerSelector = false;
this.setStickerHistory(this.selectedSticker);
this.selectedSticker = null;
}
} else {
this.logger.error(res);
}
}),
catchError(error => of(this.logger.error(error)))
)
.subscribe(() => {
this.isTranslationProcess = false;
});
} else if (!!this.selectedSticker) { } else if (!!this.selectedSticker) {
/** CASE : Sticker */ /** CASE : Sticker */
if ( this.sendMessageOfSticker(message);
!!message &&
message.trim().length >
environment.productConfig.CommonSetting.masstextLength
) {
const result = await this.dialogService.open<
AlertDialogComponent,
AlertDialogData,
AlertDialogResult
>(AlertDialogComponent, {
width: '360px',
data: {
title: this.translateService.instant('chat.errors.label'),
message: this.translateService.instant(
'chat.errors.maxLengthOfMassText',
{
maxLength:
environment.productConfig.CommonSetting.masstextLength
}
)
}
});
return;
}
const stickerJson: StickerEventJson = {
name: '스티커',
file: this.selectedSticker.index,
chat: !!message ? message.trim() : ''
};
this.store.dispatch(
EventStore.send({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq: this.roomInfoSubject.value.roomSeq,
eventType: EventType.Sticker,
sentMessage: JSON.stringify(stickerJson)
}
})
);
this.isShowStickerSelector = false;
this.setStickerHistory(this.selectedSticker);
this.selectedSticker = null;
} else if ( } else if (
message.trim().length > message.trim().length >
environment.productConfig.CommonSetting.masstextLength environment.productConfig.CommonSetting.masstextLength
) { ) {
/** CASE : MASS TEXT */ /** CASE : MASS TEXT */
this.store.dispatch( this.sendMessageOfMassText(message);
EventStore.sendMass({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq: this.roomInfoSubject.value.roomSeq,
eventType: EventType.MassText,
// sentMessage: message.replace(/\n/gi, '\r\n')
sentMessage: message
}
})
);
} else { } else {
/** CASE : Normal Text */ /** CASE : Normal Text */
this.store.dispatch( this.sendMessageOfNormal(message);
EventStore.send({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq: this.roomInfoSubject.value.roomSeq,
eventType: EventType.Character,
sentMessage: message
}
})
);
} }
} }
/** Send Normal message */
sendMessageOfNormal(message: string) {
this.store.dispatch(
EventStore.send({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq: this.roomInfoSubject.value.roomSeq,
eventType: EventType.Character,
sentMessage: message
}
})
);
}
/** Send Sticker message */
async sendMessageOfSticker(message: string, isCheck: boolean = true) {
if (
!!isCheck &&
!!message &&
message.trim().length >
environment.productConfig.CommonSetting.masstextLength
) {
const result = await this.dialogService.open<
AlertDialogComponent,
AlertDialogData,
AlertDialogResult
>(AlertDialogComponent, {
width: '360px',
data: {
title: this.translateService.instant('chat.errors.label'),
message: this.translateService.instant(
'chat.errors.maxLengthOfMassText',
{
maxLength: environment.productConfig.CommonSetting.masstextLength
}
)
}
});
return;
}
const stickerJson: StickerEventJson = {
name: '스티커',
file: this.selectedSticker.index,
chat: !!message ? message.trim() : ''
};
this.store.dispatch(
EventStore.send({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq: this.roomInfoSubject.value.roomSeq,
eventType: EventType.Sticker,
sentMessage: JSON.stringify(stickerJson)
}
})
);
this.isShowStickerSelector = false;
this.setStickerHistory(this.selectedSticker);
this.selectedSticker = null;
}
/** Send Masstext message */
sendMessageOfMassText(message: string) {
this.store.dispatch(
EventStore.sendMass({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq: this.roomInfoSubject.value.roomSeq,
eventType: EventType.MassText,
// sentMessage: message.replace(/\n/gi, '\r\n')
sentMessage: message
}
})
);
}
/** Send Translation message */
sendMessageOfTranslate(message: string) {
const destLocale = this.destLocale;
const original = message;
const roomSeq = this.roomInfoSubject.value.roomSeq;
if (!!this.isTranslationProcess) {
return;
}
this.isTranslationProcess = true;
this.commonApiService
.translationSave({
userSeq: this.loginResSubject.value.userSeq,
deviceType: this.environmentsInfo.deviceType,
token: this.loginResSubject.value.tokenString,
roomSeq,
original,
srcLocale: '',
destLocale
} as TranslationSaveRequest)
.pipe(
take(1),
map(res => {
if (res.statusCode === StatusCode.Success) {
let sentMessage = '';
let eventType = EventType.Translation;
let previewObject: TranslationEventJson | MassTranslationEventJson;
if (res.translationSeq > 0) {
// Mass Text Translation
previewObject = res;
sentMessage = res.returnJson;
eventType = EventType.MassTranslation;
} else {
// Normal Text Translation
previewObject = {
locale: destLocale,
original,
translation: res.translation,
stickername: '',
stickerfile: !!this.selectedSticker
? this.selectedSticker.index
: ''
};
sentMessage = JSON.stringify(previewObject);
eventType = EventType.Translation;
}
if (!!this.translationPreview) {
// preview
this.translationPreviewInfo = {
previewInfo: res,
translationType: eventType
};
} else {
// direct send
this.store.dispatch(
EventStore.send({
senderSeq: this.loginResSubject.value.userSeq,
req: {
roomSeq,
eventType,
sentMessage
}
})
);
if (!!this.translationPreviewInfo) {
this.translationPreviewInfo = null;
}
}
if (!!this.selectedSticker) {
this.isShowStickerSelector = false;
this.setStickerHistory(this.selectedSticker);
this.selectedSticker = null;
}
} else {
this.logger.error(res);
}
}),
catchError(error => of(this.logger.error(error)))
)
.subscribe(() => {
this.isTranslationProcess = false;
});
}
onClickReceiveAlarm() { onClickReceiveAlarm() {
this.store.dispatch( this.store.dispatch(
RoomStore.updateOnlyAlarm({ roomInfo: this.roomInfoSubject.value }) RoomStore.updateOnlyAlarm({ roomInfo: this.roomInfoSubject.value })
@ -1592,6 +1612,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
const trgtUserSeq: number[] = []; const trgtUserSeq: number[] = [];
result.group.userSeqs.map(seq => trgtUserSeq.push(seq)); result.group.userSeqs.map(seq => trgtUserSeq.push(seq));
this.userInfoListSubject.value this.userInfoListSubject.value
.filter(v => v.isJoinRoom)
.filter(v => result.group.userSeqs.indexOf(v.seq) < 0) .filter(v => result.group.userSeqs.indexOf(v.seq) < 0)
.forEach(user => { .forEach(user => {
trgtUserSeq.push(user.seq); trgtUserSeq.push(user.seq);

View File

@ -63,6 +63,7 @@
<mat-list-option <mat-list-option
*ngFor="let groupBuddy of groupBuddyList$ | async" *ngFor="let groupBuddy of groupBuddyList$ | async"
[value]="groupBuddy.group" [value]="groupBuddy.group"
[disabled]="getDisabled(groupBuddy)"
class="group-name" class="group-name"
> >
<span class="title-name ellipsis"> {{ groupBuddy.group.name }} </span> <span class="title-name ellipsis"> {{ groupBuddy.group.name }} </span>

View File

@ -24,6 +24,7 @@ import { TranslateService } from '@ngx-translate/core';
export interface SelectGroupDialogData { export interface SelectGroupDialogData {
title: string; title: string;
ignoreGroup?: GroupDetailData[];
} }
export interface SelectGroupDialogResult { export interface SelectGroupDialogResult {
@ -71,6 +72,44 @@ export class SelectGroupDialogComponent implements OnInit {
) )
]).pipe( ]).pipe(
map(([buddyList, groupList]) => { map(([buddyList, groupList]) => {
// sort..
if (!!groupList && groupList.length > 0) {
const tempOrderArr: GroupDetailData[] = [];
let myDeptGroup: GroupDetailData;
let defaultGroup: GroupDetailData;
const normalGroup: GroupDetailData[] = [];
groupList.forEach(group => {
if (
!!environment.productConfig.CommonSetting.useMyDeptGroup &&
environment.productConfig.CommonSetting.myDeptGroupSeq ===
group.seq
) {
myDeptGroup = group;
} else if (0 === group.seq) {
defaultGroup = group;
} else {
normalGroup.push(group);
}
});
if (!!myDeptGroup) {
tempOrderArr.push(myDeptGroup);
}
tempOrderArr.push(
...normalGroup.sort((a, b) =>
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
)
);
if (!!defaultGroup) {
tempOrderArr.push(defaultGroup);
}
groupList = tempOrderArr;
}
/** 수정불가 그룹 */ /** 수정불가 그룹 */
let fixedGroupSeqs: number[]; let fixedGroupSeqs: number[];
if (!!environment.productConfig.CommonSetting.fixedGroupSeqs) { if (!!environment.productConfig.CommonSetting.fixedGroupSeqs) {
@ -90,6 +129,17 @@ export class SelectGroupDialogComponent implements OnInit {
} }
} }
/** 선택 무시 그룹 필터링. */
if (!!this.data.ignoreGroup && this.data.ignoreGroup.length > 0) {
if (
this.data.ignoreGroup.filter(
groupDetail => groupDetail.seq === group.seq
).length > 0
) {
continue;
}
}
groupBuddyList.push({ groupBuddyList.push({
group, group,
buddyList: buddyList.filter(buddy => { buddyList: buddyList.filter(buddy => {
@ -140,4 +190,18 @@ export class SelectGroupDialogComponent implements OnInit {
group: this.selectedGroup group: this.selectedGroup
}); });
} }
getDisabled(groupBuddyList: {
group: GroupDetailData;
buddyList: UserInfo[];
}): boolean {
if (!!this.data.ignoreGroup && this.data.ignoreGroup.length > 0) {
return (
this.data.ignoreGroup.filter(
groupDetail => groupDetail.seq === groupBuddyList.group.seq
).length > 0
);
}
return false;
}
} }

View File

@ -257,27 +257,28 @@
yPosition="below" yPosition="below"
(closed)="onClosedProfileMenu($event)" (closed)="onClosedProfileMenu($event)"
> >
<div class="setting"> <ng-template matMenuContent>
<button mat-menu-item [matMenuTriggerFor]="presenseMenu"> <div class="setting" style="width: 200px;">
<ng-container [ngSwitch]="myStatus.pcStatus"> <button mat-menu-item [matMenuTriggerFor]="presenseMenu">
<ng-container *ngSwitchCase="StatusCode.OnLine"> <ng-container [ngSwitch]="myStatus.pcStatus">
{{ 'presence.online' | translate }} <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> </ng-container>
<ng-container *ngSwitchCase="StatusCode.Away"> </button>
{{ 'presence.away' | translate }} </div>
</ng-container> <div class="setting">
<ng-container *ngSwitchCase="StatusCode.Busy"> <button mat-menu-item (click)="onClickOpenProfile($event)">
{{ myStatus.statusMessage }} {{ 'profile.open' | translate }}
</ng-container> </button>
</ng-container> </div>
</button> <!-- <div class="setting">
</div>
<div class="setting">
<button mat-menu-item (click)="onClickOpenProfile($event)">
{{ 'profile.open' | translate }}
</button>
</div>
<!-- <div class="setting">
<button <button
mat-menu-item mat-menu-item
class="zoom minus-square" class="zoom minus-square"
@ -293,148 +294,163 @@
확대 확대
</button> </button>
</div> --> </div> -->
<div class="setting"> <div class="setting">
<button mat-menu-item (click)="onClickNotice()"> <button mat-menu-item (click)="onClickNotice()">
{{ 'notice.label' | translate }} {{ 'notice.label' | translate }}
</button> </button>
</div> </div>
<div class="setting"> <div class="setting">
<button mat-menu-item (click)="onClickRemoteSupport($event)"> <button mat-menu-item (click)="onClickRemoteSupport($event)">
{{ 'profile.remoteSupport' | translate }} {{ 'profile.remoteSupport' | translate }}
</button> </button>
</div> </div>
<div class="setting"> <div class="setting">
<button mat-menu-item (click)="onClickSettings()"> <button mat-menu-item (click)="onClickSettings()">
{{ 'settings.label' | translate }} {{ 'settings.label' | translate }}
</button> </button>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div class="setting"> <div class="setting">
<button <button
mat-menu-item mat-menu-item
[matMenuTriggerFor]="informationMenu" [matMenuTriggerFor]="informationMenu"
(menuOpened)="onMenuOpenedinformationMenu()" (menuOpened)="onMenuOpenedinformationMenu()"
> >
{{ 'information.checkForUpdates' | translate }} {{ 'information.checkForUpdates' | translate }}
</button> </button>
</div> </div>
<mat-divider></mat-divider> <mat-divider></mat-divider>
<div class="setting"> <div class="setting">
<button mat-menu-item (click)="onClickLogout()"> <button mat-menu-item (click)="onClickLogout()">
{{ 'accounts.logout' | translate }} {{ 'accounts.logout' | translate }}
</button> </button>
</div> </div>
<div class="setting">
<button mat-menu-item (click)="onClickQuit()">
{{ 'common.messages.quit' | translate }}
</button>
</div>
</ng-template>
</mat-menu> </mat-menu>
<mat-menu #presenseMenu="matMenu" class="status-pc-set"> <mat-menu #presenseMenu="matMenu" class="status-pc-set">
<div class="setting"> <ng-template matMenuContent>
<button mat-menu-item (click)="onClickStatusOnline($event)"> <div class="setting">
<span class="presence pcOn"> </span> <button mat-menu-item (click)="onClickStatusOnline($event)">
{{ 'presence.online' | translate }} <span class="presence pcOn"> </span>
</button> {{ 'presence.online' | translate }}
</div> </button>
<div class="setting"> </div>
<button mat-menu-item (click)="onClickStatusAway($event)"> <div class="setting">
<span class="presence pcOut"> </span> <button mat-menu-item (click)="onClickStatusAway($event)">
{{ 'presence.away' | translate }} <span class="presence pcOut"> </span>
</button> {{ 'presence.away' | translate }}
<button </button>
mat-menu-item <button
class="clock" mat-menu-item
[matMenuTriggerFor]="awayTimeMenu" class="clock"
></button> [matMenuTriggerFor]="awayTimeMenu"
</div> ></button>
<div class="setting"> </div>
<button mat-menu-item (click)="onClickStatusBusy($event, 1)"> <div class="setting">
<span class="presence pcOther"> </span> <button mat-menu-item (click)="onClickStatusBusy($event, 1)">
<ucap-inline-edit-input <span class="presence pcOther"> </span>
(apply)=" <ucap-inline-edit-input
$event.stopPropagation(); (apply)="
onApplyStatusMessage(1, statusMessage1.value) $event.stopPropagation();
" onApplyStatusMessage(1, statusMessage1.value)
(edit)="$event.stopPropagation()" "
(cancel)="$event.stopPropagation()" (edit)="$event.stopPropagation()"
class="form-eidt" (cancel)="$event.stopPropagation()"
> class="form-eidt"
<span ucapInlineEditInput="view">{{ loginRes?.statusMessage1 }}</span> >
<span ucapInlineEditInput="edit" <span ucapInlineEditInput="view">{{ loginRes?.statusMessage1 }}</span>
><input <span ucapInlineEditInput="edit"
matInput ><input
#statusMessage1 matInput
type="text" #statusMessage1
maxlength="5" type="text"
[value]="loginRes?.statusMessage1" maxlength="5"
(click)="$event.stopPropagation()" [value]="loginRes?.statusMessage1"
/></span> (click)="$event.stopPropagation()"
</ucap-inline-edit-input> /></span>
</button> </ucap-inline-edit-input>
</div> </button>
<div class="setting"> </div>
<button mat-menu-item (click)="onClickStatusBusy($event, 2)"> <div class="setting">
<span class="presence pcOther"> </span> <button mat-menu-item (click)="onClickStatusBusy($event, 2)">
<ucap-inline-edit-input <span class="presence pcOther"> </span>
(apply)=" <ucap-inline-edit-input
$event.stopPropagation(); (apply)="
onApplyStatusMessage(2, statusMessage2.value) $event.stopPropagation();
" onApplyStatusMessage(2, statusMessage2.value)
(edit)="$event.stopPropagation()" "
(cancel)="$event.stopPropagation()" (edit)="$event.stopPropagation()"
class="form-eidt" (cancel)="$event.stopPropagation()"
> class="form-eidt"
<span ucapInlineEditInput="view">{{ loginRes?.statusMessage2 }}</span> >
<span ucapInlineEditInput="edit" <span ucapInlineEditInput="view">{{ loginRes?.statusMessage2 }}</span>
><input <span ucapInlineEditInput="edit"
matInput ><input
#statusMessage2 matInput
type="text" #statusMessage2
maxlength="5" type="text"
[value]="loginRes?.statusMessage2" maxlength="5"
(click)="$event.stopPropagation()" [value]="loginRes?.statusMessage2"
/></span> (click)="$event.stopPropagation()"
</ucap-inline-edit-input> /></span>
</button> </ucap-inline-edit-input>
</div> </button>
<div class="setting"> </div>
<button mat-menu-item (click)="onClickStatusBusy($event, 3)"> <div class="setting">
<span class="presence pcOther"> </span> <button mat-menu-item (click)="onClickStatusBusy($event, 3)">
<ucap-inline-edit-input <span class="presence pcOther"> </span>
(apply)=" <ucap-inline-edit-input
$event.stopPropagation(); (apply)="
onApplyStatusMessage(3, statusMessage3.value) $event.stopPropagation();
" onApplyStatusMessage(3, statusMessage3.value)
(edit)="$event.stopPropagation()" "
(cancel)="$event.stopPropagation()" (edit)="$event.stopPropagation()"
class="form-eidt" (cancel)="$event.stopPropagation()"
> class="form-eidt"
<span ucapInlineEditInput="view">{{ loginRes?.statusMessage3 }}</span> >
<span ucapInlineEditInput="edit" <span ucapInlineEditInput="view">{{ loginRes?.statusMessage3 }}</span>
><input <span ucapInlineEditInput="edit"
matInput ><input
#statusMessage3 matInput
type="text" #statusMessage3
maxlength="5" type="text"
[value]="loginRes?.statusMessage3" maxlength="5"
(click)="$event.stopPropagation()" [value]="loginRes?.statusMessage3"
/></span> (click)="$event.stopPropagation()"
</ucap-inline-edit-input> /></span>
</button> </ucap-inline-edit-input>
</div> </button>
</div>
</ng-template>
</mat-menu> </mat-menu>
<mat-menu #awayTimeMenu="matMenu"> <mat-menu #awayTimeMenu="matMenu">
<div mat-menu-item (click)="$event.stopPropagation()"> <ng-template matMenuContent>
{{ 'presence.settingOfAwayTime' | translate }} <div mat-menu-item (click)="$event.stopPropagation()">
</div> {{ 'presence.settingOfAwayTime' | translate }}
<mat-radio-group aria-label="Select an option" [value]="myIdleCheckTime">
<div mat-menu-item *ngFor="let awayTime of awayTimeList">
<mat-radio-button [value]="awayTime" (change)="onChangeAwayTime($event)">
{{ awayTime }}{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div> </div>
</mat-radio-group> <mat-radio-group aria-label="Select an option" [value]="myIdleCheckTime">
<div mat-menu-item *ngFor="let awayTime of awayTimeList">
<mat-radio-button
[value]="awayTime"
(change)="onChangeAwayTime($event)"
>
{{ awayTime
}}{{ 'common.units.minute' | translate }}</mat-radio-button
>
</div>
</mat-radio-group>
</ng-template>
</mat-menu> </mat-menu>
<mat-menu #informationMenu="matMenu"> <mat-menu #informationMenu="matMenu">
<<<<<<< HEAD
<div <div
class="version-info-container menu-item" class="version-info-container menu-item"
(click)="$event.stopPropagation()" (click)="$event.stopPropagation()"
@ -461,8 +477,45 @@
<span class="version-info-button"> <span class="version-info-button">
<button *ngIf="checkingUpdateIsExist" mat-flat-button <button *ngIf="checkingUpdateIsExist" mat-flat-button
class="mat-primary" (click)="onClickApplyUpdate($event)">{{ 'information.applyUpdates' | translate }}</button> class="mat-primary" (click)="onClickApplyUpdate($event)">{{ 'information.applyUpdates' | translate }}</button>
=======
<ng-template matMenuContent>
<div
class="version-info-container menu-item"
(click)="$event.stopPropagation()"
>
<div>
<span>
{{ 'information.installedVersion' | translate }}:{{ appVersion }}
>>>>>>> 86c2a7cb2a2195023425b1653f1cc8472b09a8d9
</span> </span>
</div> </div>
<div *ngIf="checkingUpdate">
<div *ngIf="checkingUpdateIsProcessing" style="display: flex;">
<mat-spinner
*ngIf="checkingUpdate && checkingUpdateIsProcessing"
diameter="20"
strokeWidth="1"
></mat-spinner>
{{ 'information.checkForUpdatesInProgress' | translate }}
</div>
<div *ngIf="!checkingUpdateIsProcessing" style="display: flex;">
<span>
{{ 'information.latestVersion' | translate }}:{{
checkingUpdateAppVersion
}}
</span>
<span>
<button
*ngIf="checkingUpdateIsExist"
mat-button
(click)="onClickApplyUpdate($event)"
>
{{ 'information.applyUpdates' | translate }}
</button>
</span>
</div>
</div>
</div> </div>
</div> </ng-template>
</mat-menu> </mat-menu>

View File

@ -309,6 +309,10 @@ export class TopBarComponent implements OnInit, OnDestroy {
this.store.dispatch(AuthenticationStore.logoutConfirmation()); this.store.dispatch(AuthenticationStore.logoutConfirmation());
} }
onClickQuit(): void {
this.nativeService.appExit();
}
getMyProfileImageWidget(): string { getMyProfileImageWidget(): string {
if (!!this.loginRes) { if (!!this.loginRes) {
return this.loginRes.userInfo.profileImageFile; return this.loginRes.userInfo.profileImageFile;

View File

@ -406,6 +406,7 @@
} }
}, },
"messages": { "messages": {
"quit": "Quit",
"yes": "Yes", "yes": "Yes",
"no": "No", "no": "No",
"confirm": "Confirm", "confirm": "Confirm",

View File

@ -406,6 +406,7 @@
} }
}, },
"messages": { "messages": {
"quit": "종료",
"yes": "예", "yes": "예",
"no": "아니오", "no": "아니오",
"confirm": "확인", "confirm": "확인",

View File

@ -221,6 +221,8 @@ export class BrowserNativeService extends NativeService {
windowMaximize(): void {} windowMaximize(): void {}
appExit(): void {}
zoomTo(factor: number): Promise<number> { zoomTo(factor: number): Promise<number> {
return new Promise<number>((resolve, reject) => { return new Promise<number>((resolve, reject) => {
resolve(-1); resolve(-1);

View File

@ -22,7 +22,8 @@ import {
ChatChannel, ChatChannel,
MessengerChannel, MessengerChannel,
MessageChannel, MessageChannel,
ProcessChannel ProcessChannel,
AppChannel
} 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';
@ -388,6 +389,10 @@ export class ElectronNativeService implements NativeService {
} }
} }
appExit(): void {
this.ipcRenderer.send(AppChannel.Exit);
}
zoomTo(factor: number): Promise<number> { zoomTo(factor: number): Promise<number> {
return new Promise<number>((resolve, reject) => { return new Promise<number>((resolve, reject) => {
try { try {

View File

@ -53,3 +53,7 @@ export enum IdleStateChannel {
StartCheck = 'UCAP::idleState::startCheck', StartCheck = 'UCAP::idleState::startCheck',
ChangeLimitTime = 'UCAP::idleState::changeLimitTime' ChangeLimitTime = 'UCAP::idleState::changeLimitTime'
} }
export enum AppChannel {
Exit = 'UCAP::app::exit'
}

View File

@ -68,6 +68,7 @@ export abstract class NativeService {
abstract windowMinimize(): void; abstract windowMinimize(): void;
abstract windowMaximize(): void; abstract windowMaximize(): void;
abstract zoomTo(factor: number): Promise<number>; abstract zoomTo(factor: number): Promise<number>;
abstract appExit(): void;
abstract idleStateChanged(): Observable<WindowIdle>; abstract idleStateChanged(): Observable<WindowIdle>;
abstract changeLimitOfIdleState(limitTime: number): void; abstract changeLimitOfIdleState(limitTime: number): void;

View File

@ -142,6 +142,7 @@ export class MessagesComponent implements OnInit, OnDestroy {
readToHereEvent: Info<EventJson>; readToHereEvent: Info<EventJson>;
swapped = false; swapped = false;
hidden = false;
constructor( constructor(
private logger: NGXLogger, private logger: NGXLogger,
@ -384,9 +385,10 @@ export class MessagesComponent implements OnInit, OnDestroy {
to: Info<EventJson>, to: Info<EventJson>,
preCallback: () => void, preCallback: () => void,
postCallback: () => void, postCallback: () => void,
useSwap: boolean = true useHide: boolean,
useSwap: boolean
) { ) {
this.preSwapScroll(useSwap); this.preSwapScroll(useHide, useSwap);
if (!!preCallback) { if (!!preCallback) {
preCallback(); preCallback();
} }
@ -395,12 +397,12 @@ export class MessagesComponent implements OnInit, OnDestroy {
if (!!postCallback) { if (!!postCallback) {
postCallback(); postCallback();
} }
this.postSwapScroll(useSwap); this.postSwapScroll(useHide, useSwap);
}, 100); }, 100);
}); });
} }
preSwapScroll(useSwap: boolean = true) { preSwapScroll(useHide: boolean, useSwap: boolean) {
if (useSwap && !this.swapped) { if (useSwap && !this.swapped) {
this.chatMessagesBuffer.nativeElement.innerHTML = this.chatMessagesContainer.nativeElement.innerHTML; this.chatMessagesBuffer.nativeElement.innerHTML = this.chatMessagesContainer.nativeElement.innerHTML;
this.chatMessagesBuffer.nativeElement.scrollTop = this.chatMessagesContainer.nativeElement.scrollTop; this.chatMessagesBuffer.nativeElement.scrollTop = this.chatMessagesContainer.nativeElement.scrollTop;
@ -408,10 +410,13 @@ export class MessagesComponent implements OnInit, OnDestroy {
this.swapped = true; this.swapped = true;
} }
this.chatMessagesContainer.nativeElement.classList.add('hide'); if (useHide && !this.hidden) {
this.chatMessagesContainer.nativeElement.classList.add('hide');
this.hidden = true;
}
} }
postSwapScroll(useSwap: boolean = true) { postSwapScroll(useHide: boolean, useSwap: boolean) {
if (useSwap && this.swapped) { if (useSwap && this.swapped) {
this.chatMessagesBuffer.nativeElement.innerHTML = ''; this.chatMessagesBuffer.nativeElement.innerHTML = '';
this.chatMessagesBuffer.nativeElement.scrollTop = 0; this.chatMessagesBuffer.nativeElement.scrollTop = 0;
@ -419,7 +424,10 @@ export class MessagesComponent implements OnInit, OnDestroy {
this.swapped = false; this.swapped = false;
} }
this.chatMessagesContainer.nativeElement.classList.remove('hide'); if (useHide && this.hidden) {
this.chatMessagesContainer.nativeElement.classList.remove('hide');
this.hidden = false;
}
} }
ready(): void { ready(): void {
@ -434,7 +442,9 @@ export class MessagesComponent implements OnInit, OnDestroy {
() => {}, () => {},
() => { () => {
this.firstCheckReadHere = false; this.firstCheckReadHere = false;
} },
true,
true
); );
} else { } else {
this.swapScroll( this.swapScroll(
@ -442,7 +452,9 @@ export class MessagesComponent implements OnInit, OnDestroy {
() => {}, () => {},
() => { () => {
this.storedScrollItem = undefined; this.storedScrollItem = undefined;
} },
true,
true
); );
} }
} else if (this.scrollUpInitalized) { } else if (this.scrollUpInitalized) {
@ -460,15 +472,31 @@ export class MessagesComponent implements OnInit, OnDestroy {
() => {}, () => {},
() => { () => {
this.firstCheckReadHere = false; this.firstCheckReadHere = false;
} },
true,
true
); );
} else { } else {
this.swapScroll( if (
this.eventList[this.eventList.length - 1], this.virtualScroller.viewPortInfo.endIndex ===
() => {}, this.eventList.length - 2
() => {}, ) {
false this.swapScroll(
); this.eventList[this.eventList.length - 1],
() => {},
() => {},
false,
false
);
} else {
this.swapScroll(
this.eventList[this.eventList.length - 1],
() => {},
() => {},
true,
false
);
}
} }
} }
} }
@ -499,7 +527,7 @@ export class MessagesComponent implements OnInit, OnDestroy {
if (this.scrollUpInitalized && this.eventRemained) { if (this.scrollUpInitalized && this.eventRemained) {
this.storeScrollPosition(); this.storeScrollPosition();
this.preSwapScroll(); this.preSwapScroll(true, true);
this.moreEvent.emit(this.eventList[0].seq); this.moreEvent.emit(this.eventList[0].seq);