This commit is contained in:
병준 박 2019-11-01 15:06:20 +09:00
commit bb1c74823d
44 changed files with 737 additions and 439 deletions

View File

@ -194,3 +194,7 @@ ipcMain.on(Channel.saveFile, (event: IpcMainEvent, ...args: any[]) => {
event.returnValue = false; event.returnValue = false;
} }
}); });
ipcMain.on(Channel.showNotify, (event: IpcMainEvent, ...args: any[]) => {
console.log('Channel.showNotify', args);
});

View File

@ -11,7 +11,7 @@
<ng-template mat-tab-label> <ng-template mat-tab-label>
<mat-icon>group</mat-icon> <mat-icon>group</mat-icon>
</ng-template> </ng-template>
<app-layout-chat-left-sidenav-group></app-layout-chat-left-sidenav-group> <app-layout-chat-left-sidenav-group class="left-group-side"></app-layout-chat-left-sidenav-group>
</mat-tab> </mat-tab>
<mat-tab> <mat-tab>
<ng-template mat-tab-label> <ng-template mat-tab-label>

View File

@ -49,3 +49,7 @@
} }
} }
} }
.left-group-side{
position: relative;
height:100%;
}

View File

@ -1,83 +1,66 @@
<!--<div class="app-layout-chat-left-sidenav-chat-header list-search"> <div>
<form [formGroup]="fgSearch"> <div class="current-head">
<mat-form-field class="w-100-p searchbox" floatLabel="never"> <h3>대화</h3>
<input <div class="btn-box">
matInput <!-- <button mat-icon-button>
#inputSearch <mat-icon>timer</mat-icon>
type="text"
maxlength="20"
placeholder="대화방 이름 검색"
value=""
formControlName="searchInput"
[matAutocomplete]="auto"
(keydown.enter)="onKeyDownEnter($event, inputSearch.value)"
/>
<mat-autocomplete #auto="matAutocomplete">
<mat-option
*ngFor="let filteredRecommendedWord of filteredRecommendedWordList"
[value]="filteredRecommendedWord"
>
{{ filteredRecommendedWord }}
</mat-option>
</mat-autocomplete>
<button
mat-button
matSuffix
mat-icon-button
aria-label="Clear"
(click)="inputSearch.value = ''; onClickSearchCancel()"
>
<mat-icon>close</mat-icon>
</button> </button>
</mat-form-field> <button mat-icon-button>
</form>--> <mat-icon> add_comment</mat-icon>
</button> -->
<div class="list-search"> </div>
<div class="searchbox"> </div>
<form [formGroup]="fgSearch" class="w-100-p"> <div class="list-search">
<mat-form-field floatLabel="never"> <div class="searchbox">
<input <form [formGroup]="fgSearch" class="w-100-p">
matInput <mat-form-field floatLabel="never">
#inputSearch <input
type="text" matInput
maxlength="20" #inputSearch
placeholder="대화방 이름 검색" type="text"
value="" maxlength="20"
formControlName="searchInput" placeholder="대화방 이름 검색"
[matAutocomplete]="auto" value=""
(keydown.enter)="onKeyDownEnter($event, inputSearch.value)" formControlName="searchInput"
/> [matAutocomplete]="auto"
<button (keydown.enter)="onKeyDownEnter($event, inputSearch.value)"
mat-button />
matSuffix <mat-autocomplete #auto="matAutocomplete">
mat-icon-button <mat-option
aria-label="Clear" *ngFor="
(click)="inputSearch.value = ''; onClickSearchCancel()" let filteredRecommendedWord of filteredRecommendedWordList
> "
<mat-icon>close</mat-icon> [value]="filteredRecommendedWord"
</button> >
</mat-form-field> {{ filteredRecommendedWord }}
</form> </mat-option>
</mat-autocomplete>
<button
mat-button
matSuffix
mat-icon-button
aria-label="Clear"
(click)="inputSearch.value = ''; onClickSearchCancel()"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</form>
</div>
</div> </div>
</div>
<div <div *ngIf="!isSearch" class="app-layout-chat-left-sidenav-chat-list">
*ngIf="!isSearch" <ucap-room-list-item
class="app-layout-chat-left-sidenav-chat-list" *ngFor="let room of roomList"
perfectScrollbar [loginRes]="loginRes"
> [roomInfo]="room"
<ucap-room-list-item [roomUserInfo]="getRoomUserList(room)"
*ngFor="let room of roomList" [sessionVerinfo]="sessionVerinfo"
[loginRes]="loginRes" (click)="onSelectedRoom(room)"
[roomInfo]="room" (contextmenu)="onContextMenuChat($event, room)"
[roomUserInfo]="getRoomUserList(room)" >
[sessionVerinfo]="sessionVerinfo" </ucap-room-list-item>
(click)="onSelectedRoom(room)" <!-- <cdk-virtual-scroll-viewport
(contextmenu)="onContextMenuChat($event, room)"
>
</ucap-room-list-item>
<!-- <cdk-virtual-scroll-viewport
itemSize="20" itemSize="20"
class="app-layout-chat-left-sidenav-chat-list-viewport" class="app-layout-chat-left-sidenav-chat-list-viewport"
> >
@ -92,23 +75,19 @@
> >
</ucap-room-list-item> </ucap-room-list-item>
</cdk-virtual-scroll-viewport> --> </cdk-virtual-scroll-viewport> -->
</div> </div>
<div <div *ngIf="!!isSearch" class="app-layout-chat-left-sidenav-chat-list search">
*ngIf="!!isSearch" <ucap-room-list-item
class="app-layout-chat-left-sidenav-chat-list search" *ngFor="let room of searchRoomList"
perfectScrollbar [loginRes]="loginRes"
> [roomInfo]="room"
<ucap-room-list-item [roomUserInfo]="getRoomUserList(room)"
*ngFor="let room of searchRoomList" [sessionVerinfo]="sessionVerinfo"
[loginRes]="loginRes" (click)="onSelectedRoom(room)"
[roomInfo]="room" (contextmenu)="onContextMenuChat($event, room)"
[roomUserInfo]="getRoomUserList(room)" >
[sessionVerinfo]="sessionVerinfo" </ucap-room-list-item>
(click)="onSelectedRoom(room)" <!-- <cdk-virtual-scroll-viewport
(contextmenu)="onContextMenuChat($event, room)"
>
</ucap-room-list-item>
<!-- <cdk-virtual-scroll-viewport
itemSize="20" itemSize="20"
class="app-layout-chat-left-sidenav-chat-list-viewport" class="app-layout-chat-left-sidenav-chat-list-viewport"
> >
@ -123,29 +102,30 @@
> >
</ucap-room-list-item> </ucap-room-list-item>
</cdk-virtual-scroll-viewport> --> </cdk-virtual-scroll-viewport> -->
</div> </div>
<div <div
style="visibility: hidden; position: fixed" style="visibility: hidden; position: fixed"
[style.left]="chatContextMenuPosition.x" [style.left]="chatContextMenuPosition.x"
[style.top]="chatContextMenuPosition.y" [style.top]="chatContextMenuPosition.y"
#chatContextMenuTrigger="matMenuTrigger" #chatContextMenuTrigger="matMenuTrigger"
[matMenuTriggerFor]="chatContextMenu" [matMenuTriggerFor]="chatContextMenu"
></div> ></div>
<mat-menu <mat-menu
#chatContextMenu="matMenu" #chatContextMenu="matMenu"
[hasBackdrop]="false" [hasBackdrop]="false"
(ucapUiClickOutside)="chatContextMenuTrigger.closeMenu()" (ucapUiClickOutside)="chatContextMenuTrigger.closeMenu()"
> >
<ng-template matMenuContent let-roomInfo="roomInfo"> <ng-template matMenuContent let-roomInfo="roomInfo">
<button mat-menu-item (click)="onSelectedRoom(roomInfo)"> <button mat-menu-item (click)="onSelectedRoom(roomInfo)">
대화방 열기 대화방 열기
</button> </button>
<button mat-menu-item (click)="onClickToggleAlarm(roomInfo)"> <button mat-menu-item (click)="onClickToggleAlarm(roomInfo)">
대화방 알람 {{ roomInfo.receiveAlarm ? '끄기' : '켜기' }} 대화방 알람 {{ roomInfo.receiveAlarm ? '끄기' : '켜기' }}
</button> </button>
<button mat-menu-item (click)="onClickExit(roomInfo)"> <button mat-menu-item (click)="onClickExit(roomInfo)">
대화방 나가기 대화방 나가기
</button> </button>
</ng-template> </ng-template>
</mat-menu> </mat-menu>
</div>

View File

@ -1,3 +1,69 @@
.current-head{
display: flex;
justify-content: center;
padding: 0 10px;
height: 60px;
h3{
display: inline-flex;
padding-left: 10px;
align-items: center;
}
.btn-box{
height: 100%;
margin-left: auto;
display: inline-flex;
align-items: center;
}
}
.list-search {
display: flex;
flex-direction: row;
height: 60px;
align-items: center;
padding: 0;
font-size: 14px;
border-bottom: 1px solid #dddddd;
.searchbox{
width:100%;
height:100%;
}
}
::ng-deep .searchbox{
.mat-form-field{
display:block;
.mat-form-field-wrapper{
padding: 0;
padding-bottom:0 !important;
height: 100%;
.mat-form-field-flex{
height: 59px;
padding:0 20px;
align-items: center;
.mat-form-field-infix{
width:100%;
font-size:14px;
border:none;
}
.mat-form-field-suffix{
.mat-icon{
line-height:24px;
}
}
}
}
}
.mat-form-field-appearance-legacy{
.mat-form-field-wrapper{
padding: 0;
}
.mat-form-field-underline{
bottom:0;
background-color: unset !important;
}
}
}
.app-layout-chat-left-sidenav-chat-header { .app-layout-chat-left-sidenav-chat-header {
width: 100%; width: 100%;
height: 50px; height: 50px;
@ -10,60 +76,4 @@
.app-layout-chat-left-sidenav-chat-list-viewport { .app-layout-chat-left-sidenav-chat-list-viewport {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.room-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.list-search {
display: flex;
flex-direction: row;
height: 60px;
align-items: center;
padding: 0;
font-size: 14px;
border-bottom: 1px solid #dddddd;
.searchbox{
width:100%;
.mat-form-field{
display:block;
.mat-form-field-wrapper{
padding-bottom:0 !important;
height: 60px;
}
}
}
}
::ng-deep .searchbox .mat-form-field-flex{
height: 60px;
padding:0 10px;
}
::ng-deep .searchbox .mat-form-field-appearance-legacy .mat-form-field-wrapper{
padding: 0;
}
::ng-deep .searchbox .mat-form-field-appearance-legacy .mat-form-field-underline{
bottom:0;
background-color: #cccccc !important;
}
/*.searchbox {
width: 100%;
display: flex;
input {
display: inline-flex;
width: 100%;
font-size: 14px;
padding-left: 0;
}
.btn-search {
color: #777777;
font-size: 12px;
display: inline-flex;
margin-left: auto;
flex: none;
}
}*/

View File

@ -1,4 +1,4 @@
<div>
<div> <div>
<div class="current-head"> <div class="current-head">
<h3>그룹</h3> <h3>그룹</h3>
@ -52,7 +52,7 @@
</button> </button>
</mat-menu> </mat-menu>
</div> </div>
<div *ngIf="!isShowSearch"> <div *ngIf="!isShowSearch" class="search-result" style="overflow: auto;">
<ucap-group-expansion-panel <ucap-group-expansion-panel
#groupExpansionPanel #groupExpansionPanel
[groupBuddyList]="groupBuddyList$ | async" [groupBuddyList]="groupBuddyList$ | async"
@ -71,11 +71,11 @@
</ucap-profile-user-list-item> </ucap-profile-user-list-item>
</ucap-group-expansion-panel> </ucap-group-expansion-panel>
</div> </div>
<div *ngIf="isShowSearch"> <div *ngIf="isShowSearch" class="search-result">
<div *ngIf="searchProcessing"> <div *ngIf="searchProcessing">
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div> </div>
<div>검색결과({{ searchUserInfos.length }}명)</div> <div class="result-num">검색결과<span class="text-accent-color">({{ searchUserInfos.length }}명)</span></div>
<ucap-profile-user-list-item <ucap-profile-user-list-item
*ngFor="let userInfo of searchUserInfos" *ngFor="let userInfo of searchUserInfos"
[userInfo]="userInfo" [userInfo]="userInfo"
@ -85,7 +85,7 @@
> >
</ucap-profile-user-list-item> </ucap-profile-user-list-item>
</div> </div>
</div>
<div <div
style="visibility: hidden; position: fixed" style="visibility: hidden; position: fixed"

View File

@ -18,3 +18,16 @@
} }
} }
} }
.search-result {
height: calc(100% - 120px);
overflow: auto;
.result-num {
padding: 10px;
display: flex;
height: 40px;
}
}
::ng-deep .mat-tab-body-content {
height: 100%;
overflow: unset;
}

View File

@ -170,7 +170,6 @@ export class GroupComponent implements OnInit, OnDestroy {
CreateChatDialogResult CreateChatDialogResult
>(CreateChatDialogComponent, { >(CreateChatDialogComponent, {
width: '600px', width: '600px',
height: '700px',
data: { data: {
type: UserSelectDialogType.NewGroup, type: UserSelectDialogType.NewGroup,
title: 'New Group' title: 'New Group'

View File

@ -10,8 +10,7 @@
></ucap-organization-tree> ></ucap-organization-tree>
</div> </div>
<div class="select-list "> <div class="select-list ">
<div class="select-dept text-accent-color"> <dl class="select-dept text-accent-color">
<dl>
<dt> <dt>
{{ getSelectedDepartmentName() }} {{ getSelectedDepartmentName() }}
</dt> </dt>
@ -25,7 +24,6 @@
</mat-checkbox> </mat-checkbox>
</dd> </dd>
</dl> </dl>
</div>
<div *ngIf="selectedDepartmentProcessing"> <div *ngIf="selectedDepartmentProcessing">
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div> </div>

View File

@ -16,16 +16,17 @@
} }
.oraganization-tab { .oraganization-tab {
height: 100%; height: 100%;
flex-direction: inherit; flex-direction: inherit;
display: flex; display: flex;
.oraganization-tab-tree { .oraganization-tab-tree {
height:40%; height:40%;
overflow-y: auto; overflow-y: auto;
}
} }
}
//팝업에 있는 조직도
.mat-card-content{ .mat-card-content{
.mat-tab-body-content{ .mat-tab-body-content{
.oraganization-box{ .oraganization-box{
@ -37,34 +38,24 @@
} }
} }
.btn-box{
height:100px;
position: absolute;
bottom:0;
border-top:1px solid #ddd;
align-items: center;
width:100%;
background-color:#ffffff;
}
.select-list{ .select-list{
height:60%; height:60%;
border-top:1px solid #dddddd; border-top:1px solid #dddddd;
.select-dept{ .select-dept{
padding:0 10px; padding:0 20px;
height:40px; height:40px;
line-height:40px; line-height:40px;
.dept-name{ display:flex;
border-top:1px solid #dddddd; background-color: #f9f9f9;
height:40px; dt{
width:100%;
display:inline-flex; }
align-items: center; dd{
padding:0 10px; margin-left:auto;
} }
} }
.search-list{ .search-list{
height: calc(100% - 140px); height: calc(100% - 40px);
overflow: auto; overflow: auto;
.list-item{ .list-item{
height:70px; height:70px;

View File

@ -8,19 +8,33 @@
<button <button
mat-icon-button mat-icon-button
aria-label="chats button" aria-label="chats button"
fxHide.gt-md
class="responsive-chats-button" class="responsive-chats-button"
> >
<mat-icon>chat</mat-icon> <mat-icon>chat</mat-icon>
</button> </button>
<!-- / RESPONSIVE CHATS BUTTON--> <!-- / RESPONSIVE CHATS BUTTON-->
<button
mat-icon-button
aria-label="chats button"
class="responsive-chats-button"
*ngIf="!!roomInfo && roomInfo.isTimeRoom">
<mat-icon>timer</mat-icon>
</button>
</div> </div>
<div class="room-name"> <div class="room-info">
{{ getRoomName() }} <h3 class="room-name">
</div> {{ getRoomName() }}
<div *ngIf="!!roomInfo && roomInfo.isTimeRoom"> </h3>
<mat-icon>timer</mat-icon> {{ getConvertTimer(roomInfo.timeRoomInterval) }} <!-- Timer Room Info -->
<div *ngIf="roomInfo && roomInfo.isTimeRoom" class="room-type text-accent-color ">
<span class="bg-accent-light">{{ getConvertTimer(roomInfo.timeRoomInterval) }} </span>비밀 대화방입니다.
</div>
<!-- Timer Room Info -->
</div> </div>
<!--<div *ngIf="!!roomInfo && roomInfo.isTimeRoom">
<mat-icon>timer</mat-icon>
{{ getConvertTimer(roomInfo.timeRoomInterval) }}
</div>-->
<div class="room-option"> <div class="room-option">
<button <button
*ngIf="!!roomInfo" *ngIf="!!roomInfo"
@ -68,18 +82,22 @@
(fileDragOver)="onFileDragOver()" (fileDragOver)="onFileDragOver()"
(fileDragLeave)="onFileDragLeave()" (fileDragLeave)="onFileDragLeave()"
> >
<!-- Timer Room Info -->
<span *ngIf="roomInfo && roomInfo.isTimeRoom">비밀 대화방입니다.</span>
<!-- Timer Room Info -->
<!-- CHAT MESSAGES --> <!-- CHAT MESSAGES -->
<perfect-scrollbar fxFlex="1 1 auto" #psChatContent> <perfect-scrollbar
fxFlex="1 1 auto"
#psChatContent
(psYReachStart)="onScrollup($event)"
>
<ucap-chat-messages <ucap-chat-messages
[messages]="eventList$ | async" [messages]="eventList$ | async"
[eventInfoStatus]="eventInfoStatus$ | async" [eventInfoStatus]="eventInfoStatus$ | async"
[eventRemain]="eventRemain$ | async"
[userInfos]="userInfoList" [userInfos]="userInfoList"
[loginRes]="loginRes" [loginRes]="loginRes"
[sessionVerInfo]="sessionVerInfo" [sessionVerInfo]="sessionVerInfo"
(moreEvent)="onMoreEvent($event)"
(massDetail)="onMassDetail($event)" (massDetail)="onMassDetail($event)"
(save)="onSave($event)" (save)="onSave($event)"
(imageViewer)="onImageViewer($event)" (imageViewer)="onImageViewer($event)"

View File

@ -1,5 +1,9 @@
@charset 'utf-8'; @charset 'utf-s';
$line-basic: 1px solid #dddddd; :host {
display: flex;
width: 100%;
height: 100%;
}
@mixin ellipsis($row) { @mixin ellipsis($row) {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -14,24 +18,29 @@ $line-basic: 1px solid #dddddd;
word-wrap: break-word; word-wrap: break-word;
} }
} }
:host { .container{
display: flex; position: relative;
width: 100%; width:100%;
height: 100%; }
flex: 1; .chat-toolbar {
width:100%;
height: 70px;
min-height: 70px;
align-items: center;
background-color: #ffffff !important;
border: 1px solid #dddddd;
.container { .chat-header {
width: 100%; width: 100%;
height: 100%; align-items: center;
display: flex;
.chat-toolbar { justify-content: space-between;
height: 70px; .profile-img {
min-height: 70px; margin-right: 20px;
border-bottom: $line-basic; .responsive-chats-button {
background-color: #ffffff; display: none;
.chat-header { &:last-child {
width: 100%; display: block;
.responsive-chats-button {
padding: 0; padding: 0;
width: 40px; width: 40px;
height: 40px; height: 40px;
@ -39,44 +48,38 @@ $line-basic: 1px solid #dddddd;
background-color: #252525; background-color: #252525;
color: #efefef; color: #efefef;
font-size: 16px; font-size: 16px;
font-weight: 100;
line-height: 40px; line-height: 40px;
} }
.room-name {
font-size: 16px;
padding: 0 10px;
@include ellipsis(1);
}
.room-option {
width: 100px;
margin-left: auto;
}
} }
} }
.room-info {
.chat-content { display:flex;
position: relative; flex-flow: column;
background: transparent; overflow: hidden;
overflow: auto; .room-name {
-webkit-overflow-scrolling: touch; font-size: 16px;
line-height: normal;
.file-drop-zone-container { @include ellipsis(1);
position: absolute; }
top: 0; .room-type {
left: 0; font-size: 14px;
width: 100%; line-height: normal;
height: 100%; margin-top: 6px;
background-color: rgba(255, 255, 255, 0.95); height:20px;
span{
.file-drop-zone { border-radius:10px;
position: absolute; padding:1px 10px;
margin-right:6px;
top: 10%; font-size:13px;
left: 10%;
width: 80%;
height: 80%;
} }
} }
} }
.room-option {
wdith: 100%;
margin-left: auto;
}
} }
} }
.chat-content {
overflow: auto;
}

View File

@ -58,7 +58,7 @@ import {
ImageViewerDialogData, ImageViewerDialogData,
ImageViewerDialogResult ImageViewerDialogResult
} from '@app/layouts/common/dialogs/image-viewer.dialog.component'; } from '@app/layouts/common/dialogs/image-viewer.dialog.component';
import { Maximum_Range } from '@ucap-webmessenger/core'; import { CONST } from '@ucap-webmessenger/core';
import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar'; import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar';
@Component({ @Component({
@ -83,12 +83,15 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
loginRes: LoginResponse; loginRes: LoginResponse;
loginResSubscription: Subscription; loginResSubscription: Subscription;
eventList$: Observable<Info[]>; eventList$: Observable<Info[]>;
baseEventSeq = 0;
roomInfo: RoomInfo; roomInfo: RoomInfo;
roomInfoSubscription: Subscription; roomInfoSubscription: Subscription;
userInfoList: UserInfo[]; userInfoList: UserInfo[];
userInfoListSubscription: Subscription; userInfoListSubscription: Subscription;
eventListProcessing$: Observable<boolean>; eventListProcessing$: Observable<boolean>;
eventInfoStatus$: Observable<InfoResponse>; eventInfoStatus$: Observable<InfoResponse>;
eventRemain$: Observable<boolean>;
eventRemain = false;
sessionVerInfo: VersionInfo2Response; sessionVerInfo: VersionInfo2Response;
isRecalledMessage = isRecalled; isRecalledMessage = isRecalled;
@ -99,6 +102,9 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
files: File[]; files: File[];
fileItems: DataTransferItemList; fileItems: DataTransferItemList;
/** Timer 대화방의 대화 삭제를 위한 interval */
interval: any;
constructor( constructor(
private store: Store<any>, private store: Store<any>,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
@ -149,8 +155,20 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
select(AppStore.MessengerSelector.EventSelector.infoListProcessing) select(AppStore.MessengerSelector.EventSelector.infoListProcessing)
); );
this.eventRemain$ = this.store.pipe(
select(AppStore.MessengerSelector.EventSelector.remainInfo),
tap(remainInfo => {
this.eventRemain = remainInfo;
})
);
this.eventList$ = this.store.pipe( this.eventList$ = this.store.pipe(
select(AppStore.MessengerSelector.EventSelector.selectAllInfoList) select(AppStore.MessengerSelector.EventSelector.selectAllInfoList),
tap(infoList => {
if (!!infoList && infoList.length > 0) {
this.baseEventSeq = infoList[0].seq;
}
})
); );
this.eventInfoStatus$ = this.store.pipe( this.eventInfoStatus$ = this.store.pipe(
@ -158,6 +176,12 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
); );
this.psChatContent.directiveRef.scrollToBottom(0, 0); this.psChatContent.directiveRef.scrollToBottom(0, 0);
this.interval = setInterval(() => {
if (!!this.roomInfo.isTimeRoom) {
this.store.dispatch(EventStore.infoIntervalClear({}));
}
}, 1000);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -170,6 +194,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
if (!!this.userInfoListSubscription) { if (!!this.userInfoListSubscription) {
this.userInfoListSubscription.unsubscribe(); this.userInfoListSubscription.unsubscribe();
} }
clearInterval(this.interval);
} }
ngAfterViewChecked(): void { ngAfterViewChecked(): void {
@ -230,7 +256,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
return; return;
} }
if (message.trim().length > Maximum_Range.MassText) { if (message.trim().length > CONST.MASSTEXT_LEN) {
// MASS TEXT // MASS TEXT
this.store.dispatch( this.store.dispatch(
EventStore.sendMass({ EventStore.sendMass({
@ -261,6 +287,22 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
this.store.dispatch(RoomStore.updateOnlyAlarm({ roomInfo: this.roomInfo })); this.store.dispatch(RoomStore.updateOnlyAlarm({ roomInfo: this.roomInfo }));
} }
onScrollup(event: any) {
this.onMoreEvent(this.baseEventSeq);
}
/** More Event */
onMoreEvent(seq: number) {
if (this.eventRemain) {
this.store.dispatch(
EventStore.info({
roomSeq: this.roomInfo.roomSeq,
baseSeq: seq,
requestCount: CONST.EVENT_INFO_READ_COUNT
})
);
}
}
/** MassText Detail View */ /** MassText Detail View */
onMassDetail(value: number) { onMassDetail(value: number) {
this.store.dispatch( this.store.dispatch(

View File

@ -55,6 +55,7 @@
[selectedUserList]="selectedUserList" [selectedUserList]="selectedUserList"
[checkable]="true" [checkable]="true"
(checkGroup)="onCheckGroup($event)" (checkGroup)="onCheckGroup($event)"
class="group-expansion"
> >
<ucap-profile-user-list-item <ucap-profile-user-list-item
*ucapGroupExpansionPanelItem="let userInfo" *ucapGroupExpansionPanelItem="let userInfo"
@ -68,11 +69,11 @@
</ucap-profile-user-list-item> </ucap-profile-user-list-item>
</ucap-group-expansion-panel> </ucap-group-expansion-panel>
</div> </div>
<div *ngIf="isShowSearch"> <div *ngIf="isShowSearch" class="search-result">
<div *ngIf="searchProcessing"> <div *ngIf="searchProcessing">
<mat-progress-bar mode="indeterminate"></mat-progress-bar> <mat-progress-bar mode="indeterminate"></mat-progress-bar>
</div> </div>
<div>검색결과({{ searchUserInfos.length }}명)</div> <div class="result-num">검색결과 <span class="text-accent-color">({{ searchUserInfos.length }}명)</span></div>
<ucap-profile-user-list-item <ucap-profile-user-list-item
*ngFor="let userInfo of searchUserInfos" *ngFor="let userInfo of searchUserInfos"
[userInfo]="userInfo" [userInfo]="userInfo"

View File

@ -2,6 +2,7 @@
margin: 0; margin: 0;
} }
//조직도 레이아웃 변경
::ng-deep .dialog-org { ::ng-deep .dialog-org {
.oraganization-tab { .oraganization-tab {
width: 100%; width: 100%;
@ -43,7 +44,7 @@
.confirm-card { .confirm-card {
min-width: 500px; min-width: 500px;
.mat-card-header { .mat-card-header {
margin-bottom: 20px; margin-bottom: 10px;
.mat-card-header-text { .mat-card-header-text {
.mat-card-title { .mat-card-title {
margin: 0 -16px; margin: 0 -16px;
@ -67,6 +68,11 @@
height: 380px; height: 380px;
} }
.list-panel{ .list-panel{
overflow: auto; overflow: auto;
height: calc(100% - 60px); height: calc(100% - 60px);
} .group-expansion{
.list-item{
height:70px;
}
}
}

View File

@ -1,5 +1,5 @@
import { delGroupSuccess, buddy2 } from './../store/messenger/sync/actions'; import { delGroupSuccess, buddy2 } from './../store/messenger/sync/actions';
import { Injectable } from '@angular/core'; import { Injectable, Inject } from '@angular/core';
import { tap, withLatestFrom } from 'rxjs/operators'; import { tap, withLatestFrom } from 'rxjs/operators';
@ -79,6 +79,12 @@ import * as EventStore from '@app/store/messenger/event';
import * as SyncStore from '@app/store/messenger/sync'; import * as SyncStore from '@app/store/messenger/sync';
import * as RoomStore from '@app/store/messenger/room'; import * as RoomStore from '@app/store/messenger/room';
import * as StatusStore from '@app/store/messenger/status'; import * as StatusStore from '@app/store/messenger/status';
import {
NotiRequest,
NativeService,
UCAP_NATIVE_SERVICE
} from '@ucap-webmessenger/native';
import { StringUtil } from '@ucap-webmessenger/core';
@Injectable() @Injectable()
export class AppNotificationService { export class AppNotificationService {
@ -90,6 +96,7 @@ export class AppNotificationService {
private groupProtocolService: GroupProtocolService, private groupProtocolService: GroupProtocolService,
private buddyProtocolService: BuddyProtocolService, private buddyProtocolService: BuddyProtocolService,
private statusProtocolService: StatusProtocolService, private statusProtocolService: StatusProtocolService,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private store: Store<any>, private store: Store<any>,
private logger: NGXLogger private logger: NGXLogger
) {} ) {}
@ -143,6 +150,22 @@ export class AppNotificationService {
noti noti
}) })
); );
// notification..
if (notiOrRes.SSVC_TYPE === SSVC_TYPE_EVENT_SEND_NOTI) {
const notiReq: NotiRequest = {
roomSeq: noti.roomSeq,
title: '메세지가 도착했습니다.',
contents: StringUtil.convertFinalEventMessage(
noti.eventType,
noti.message
),
image: '',
useSound: true,
interval: 0
};
this.nativeService.showNotify(notiReq);
}
} }
break; break;
case SSVC_TYPE_EVENT_READ_RES: case SSVC_TYPE_EVENT_READ_RES:

View File

@ -29,6 +29,19 @@ export const infoSuccess = createAction(
}>() }>()
); );
export const infoMoreSuccess = createAction(
'[Messenger::Event] Info More Success',
props<{
infoList: Info[];
res: InfoResponse;
}>()
);
export const infoIntervalClear = createAction(
'[Messenger::Event] Info Interval Clear',
props()
);
export const infoFailure = createAction( export const infoFailure = createAction(
'[Messenger::Event] Info Failure', '[Messenger::Event] Info Failure',
props<{ error: any }>() props<{ error: any }>()
@ -174,6 +187,6 @@ export const delNotification = createAction(
export const delInfoList = createAction( export const delInfoList = createAction(
'[Messenger::Event] Delete InfoList', '[Messenger::Event] Delete InfoList',
props<{ props<{
eventSeq: number; eventSeqs: number[];
}>() }>()
); );

View File

@ -67,7 +67,9 @@ import {
forwardFailure, forwardFailure,
forwardAfterRoomOpen, forwardAfterRoomOpen,
sendMass, sendMass,
sendMassFailure sendMassFailure,
infoMoreSuccess,
infoIntervalClear
} from './actions'; } from './actions';
import { SessionStorageService } from '@ucap-webmessenger/web-storage'; import { SessionStorageService } from '@ucap-webmessenger/web-storage';
import { import {
@ -81,6 +83,7 @@ import { openSuccess, openFailure } from '../room';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type';
import { StatusCode } from '@ucap-webmessenger/api'; import { StatusCode } from '@ucap-webmessenger/api';
import { CONST } from '@ucap-webmessenger/core';
@Injectable() @Injectable()
export class Effects { export class Effects {
@ -91,7 +94,7 @@ export class Effects {
return info({ return info({
roomSeq: action.roomSeq, roomSeq: action.roomSeq,
baseSeq: 0, baseSeq: 0,
requestCount: 50 requestCount: CONST.EVENT_INFO_READ_COUNT
}); });
}) })
) )
@ -115,12 +118,21 @@ export class Effects {
break; break;
case SSVC_TYPE_EVENT_INFO_RES: case SSVC_TYPE_EVENT_INFO_RES:
{ {
this.store.dispatch( if (req.baseSeq === 0) {
infoSuccess({ this.store.dispatch(
infoList, infoSuccess({
res: res as InfoResponse infoList,
}) res: res as InfoResponse
); })
);
} else {
this.store.dispatch(
infoMoreSuccess({
infoList,
res: res as InfoResponse
})
);
}
if (req.baseSeq === 0) { if (req.baseSeq === 0) {
// 최초 이벤트 목록 조회시 SSVC_TYPE_EVENT_READ_REQ 수행. // 최초 이벤트 목록 조회시 SSVC_TYPE_EVENT_READ_REQ 수행.
@ -147,6 +159,45 @@ export class Effects {
{ dispatch: false } { dispatch: false }
); );
infoIntervalClear$ = createEffect(
() => {
return this.actions$.pipe(
ofType(infoIntervalClear),
withLatestFrom(
this.store.pipe(
select((state: any) => state.messenger.room.roomInfo as RoomInfo)
),
this.store.pipe(
select(
(state: any) =>
state.messenger.event.infoList.entities as Dictionary<Info>
)
)
),
map(([action, roomInfo, eventList]) => {
if (roomInfo.isTimeRoom && roomInfo.timeRoomInterval > 0) {
const delEventSeq: number[] = [];
// tslint:disable-next-line: forin
for (const key in eventList) {
const event: Info = eventList[key];
if (
new Date().getTime() - new Date(event.sendDate).getTime() >=
roomInfo.timeRoomInterval * 1000
) {
delEventSeq.push(event.seq);
}
}
if (delEventSeq.length > 0) {
this.store.dispatch(delInfoList({ eventSeqs: delEventSeq }));
}
}
})
);
},
{ dispatch: false }
);
read$ = createEffect(() => read$ = createEffect(() =>
this.actions$.pipe( this.actions$.pipe(
ofType(read), ofType(read),
@ -522,7 +573,7 @@ export class Effects {
tap(([noti, roomInfo]) => { tap(([noti, roomInfo]) => {
// 현재 방이 오픈되어 있으면 방내용 갱신 // 현재 방이 오픈되어 있으면 방내용 갱신
if (!!roomInfo && roomInfo.roomSeq === noti.roomSeq) { if (!!roomInfo && roomInfo.roomSeq === noti.roomSeq) {
this.store.dispatch(delInfoList({ eventSeq: noti.eventSeq })); this.store.dispatch(delInfoList({ eventSeqs: [noti.eventSeq] }));
} }
// 대화 > 리스트의 항목 갱신 // 대화 > 리스트의 항목 갱신

View File

@ -6,10 +6,12 @@ import {
info, info,
infoFailure, infoFailure,
recallInfoList, recallInfoList,
delInfoList delInfoList,
infoMoreSuccess
} from './actions'; } from './actions';
import * as AuthenticationStore from '@app/store/account/authentication'; import * as AuthenticationStore from '@app/store/account/authentication';
import { Info, EventType } from '@ucap-webmessenger/protocol-event'; import { Info, EventType } from '@ucap-webmessenger/protocol-event';
import { CONST } from '@ucap-webmessenger/core';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
@ -27,7 +29,27 @@ export const reducer = createReducer(
...state.infoList ...state.infoList
}), }),
infoStatus: action.res, infoStatus: action.res,
infoListProcessing: false infoListProcessing: false,
remainInfo:
!!action.infoList &&
action.infoList.length === CONST.EVENT_INFO_READ_COUNT
? true
: false
};
}),
on(infoMoreSuccess, (state, action) => {
return {
...state,
infoList: adapterInfoList.upsertMany(action.infoList, {
...state.infoList
}),
infoStatus: action.res,
infoListProcessing: false,
remainInfo:
!!action.infoList &&
action.infoList.length === CONST.EVENT_INFO_READ_COUNT
? true
: false
}; };
}), }),
@ -75,11 +97,11 @@ export const reducer = createReducer(
}), }),
on(delInfoList, (state, action) => { on(delInfoList, (state, action) => {
const eventSeq = action.eventSeq;
return { return {
...state, ...state,
infoList: adapterInfoList.removeOne(eventSeq, { ...state.infoList }) infoList: adapterInfoList.removeMany(action.eventSeqs, {
...state.infoList
})
}; };
}), }),

View File

@ -8,6 +8,7 @@ export interface State {
infoListProcessing: boolean; infoListProcessing: boolean;
infoList: InfoListState; infoList: InfoListState;
infoStatus: InfoResponse | null; infoStatus: InfoResponse | null;
remainInfo: boolean;
} }
export const adapterInfoList = createEntityAdapter<Info>({ export const adapterInfoList = createEntityAdapter<Info>({
@ -22,7 +23,8 @@ const infoListInitialState: InfoListState = adapterInfoList.getInitialState({});
export const initialState: State = { export const initialState: State = {
infoListProcessing: false, infoListProcessing: false,
infoList: infoListInitialState, infoList: infoListInitialState,
infoStatus: null infoStatus: null,
remainInfo: false
}; };
const { const {
@ -43,6 +45,10 @@ export function selectors<S>(selector: Selector<any, State>) {
selector, selector,
(state: State) => state.infoListProcessing (state: State) => state.infoListProcessing
), ),
remainInfo: createSelector(
selector,
(state: State) => state.remainInfo
),
infoList: createSelector( infoList: createSelector(
selector, selector,
(state: State) => state.infoList (state: State) => state.infoList

View File

@ -101,6 +101,7 @@ import {
import * as ChatStore from '@app/store/messenger/chat'; import * as ChatStore from '@app/store/messenger/chat';
import * as RoomStore from '@app/store/messenger/room'; import * as RoomStore from '@app/store/messenger/room';
import { CONST } from '@ucap-webmessenger/core';
@Injectable() @Injectable()
export class Effects { export class Effects {
@ -405,7 +406,7 @@ export class Effects {
divCd: 'DivCodeT', divCd: 'DivCodeT',
roomName: '', roomName: '',
isTimerRoom: true, isTimerRoom: true,
timerRoomInterval: 24 * 60 * 60, // 24h default timerRoomInterval: CONST.DEFAULT_TIMER_ROOM_INTERVAL, // 24h default
userSeqs: userSeqList userSeqs: userSeqList
} }
}) })

View File

@ -1,4 +1,3 @@
import { JsonObject } from 'type-fest';
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { import {
initialState, initialState,
@ -29,9 +28,7 @@ import {
import * as RoomStore from '@app/store/messenger/room'; import * as RoomStore from '@app/store/messenger/room';
import { RoomInfo } from '@ucap-webmessenger/protocol-room'; import { RoomInfo } from '@ucap-webmessenger/protocol-room';
import { EventType } from '@ucap-webmessenger/protocol-event'; import { StringUtil } from '@ucap-webmessenger/core';
import { FileType } from '@ucap-webmessenger/protocol-file';
import { JsonAnalization } from '@ucap-webmessenger/api';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
@ -103,63 +100,36 @@ export const reducer = createReducer(
}), }),
on(updateRoomForNewEventMessage, (state, action) => { on(updateRoomForNewEventMessage, (state, action) => {
let finalEventMessage: string = action.info.sentMessage; const finalEventMessage:
switch (action.info.type) { | string
case EventType.Join: | null = StringUtil.convertFinalEventMessage(
case EventType.Exit: action.info.type,
case EventType.RenameRoom: action.info.sentMessage
case EventType.NotificationForTimerRoom: );
case EventType.GuideForRoomTimerChanged: {
/**
* .
* @description Edit with ui-chat > messages.component.ts
*/
return {
...state
};
}
case EventType.Sticker:
finalEventMessage = '스티커';
break;
case EventType.File:
{
const contentJson = JSON.parse(finalEventMessage);
if (contentJson.FileType === FileType.Image) {
finalEventMessage = '이미지';
} else {
finalEventMessage = '첨부파일';
}
}
break;
case EventType.VideoConference:
finalEventMessage = '화상회의';
break;
case EventType.MassText:
{
try {
const json: JsonObject | Error = JsonAnalization.receiveAnalization(
finalEventMessage
);
finalEventMessage = json.Content.toString();
} catch (e) {
finalEventMessage = '대용량 텍스트';
}
}
break;
}
const roomInfo = {
...state.room.entities[action.roomSeq],
finalEventDate: action.info.sendDate,
finalEventMessage
};
return { if (!finalEventMessage) {
...state, /**
room: adapterRoom.updateOne( * .
{ id: action.roomSeq, changes: roomInfo }, * @description Edit with ui-chat > messages.component.ts
{ ...state.room } */
) return {
}; ...state
};
} else {
const roomInfo = {
...state.room.entities[action.roomSeq],
finalEventDate: action.info.sendDate,
finalEventMessage
};
return {
...state,
room: adapterRoom.updateOne(
{ id: action.roomSeq, changes: roomInfo },
{ ...state.room }
)
};
}
}), }),
on(RoomStore.updateSuccess, (state, action) => { on(RoomStore.updateSuccess, (state, action) => {

View File

@ -97,6 +97,10 @@ $lg-red: (
background: mat-color($accent, 600); background: mat-color($accent, 600);
color: mat-color($primary, default-contrast); color: mat-color($primary, default-contrast);
} }
.bg-accent-light {
background: mat-color($accent, 300);
color: mat-color($primary, default-contrast);
}
.bg-accent-color { .bg-accent-color {
background: mat-color($accent); background: mat-color($accent);
color: mat-color($accent, default-contrast); color: mat-color($accent, default-contrast);
@ -107,6 +111,9 @@ $lg-red: (
.text-accent-color { .text-accent-color {
color: mat-color($accent); color: mat-color($accent);
} }
.text-warn-color{
color:mat-color($warn);
}
.border-primary-color { .border-primary-color {
border: 1px solid mat-color($primary); border: 1px solid mat-color($primary);
} }

View File

@ -0,0 +1,10 @@
export enum CONST {
/** 대용량 텍스트로 보내는 문자열의 길이 기준 */
MASSTEXT_LEN = 800,
/** 대화방의 이벤트를 조회하는 개수 */
EVENT_INFO_READ_COUNT = 50,
/** Timer Room 최초 오픈시 timer interval */
DEFAULT_TIMER_ROOM_INTERVAL = 24 * 60 * 60,
/** 한번에 채팅을 할 수 있는 인원수 제한 */
CHATROOM_USER = 300
}

View File

@ -1,4 +0,0 @@
export enum Maximum_Range {
MassText = 800,
ChatRoom = 300
}

View File

@ -0,0 +1,56 @@
import { EventType } from '@ucap-webmessenger/protocol-event';
import { FileType } from '@ucap-webmessenger/protocol-file';
import { JsonObject } from 'type-fest';
import { JsonAnalization } from '@ucap-webmessenger/api';
export class StringUtil {
public static convertFinalEventMessage(
eventType: EventType,
finalEventMessage: string
): string | null {
switch (eventType) {
case EventType.Join:
case EventType.Exit:
case EventType.RenameRoom:
case EventType.NotificationForTimerRoom:
case EventType.GuideForRoomTimerChanged: {
/**
* .
* @description Edit with ui-chat > messages.component.ts
*/
return null;
}
case EventType.Sticker:
finalEventMessage = '스티커';
break;
case EventType.File:
{
const contentJson = JSON.parse(finalEventMessage);
if (contentJson.FileType === FileType.Image) {
finalEventMessage = '이미지';
} else {
finalEventMessage = '첨부파일';
}
}
break;
case EventType.VideoConference:
finalEventMessage = '화상회의';
break;
case EventType.MassText:
{
try {
const json: JsonObject | Error = JsonAnalization.receiveAnalization(
finalEventMessage
);
finalEventMessage = json.Content.toString();
} catch (e) {
finalEventMessage = '대용량 텍스트';
}
}
break;
default:
return finalEventMessage;
}
}
}

View File

@ -7,12 +7,12 @@ export * from './lib/types/call-alarm.type';
export * from './lib/types/call-forward.type'; export * from './lib/types/call-forward.type';
export * from './lib/types/call-mode.type'; export * from './lib/types/call-mode.type';
export * from './lib/types/caller-type.type'; export * from './lib/types/caller-type.type';
export * from './lib/types/const.type';
export * from './lib/types/default-screen.type'; export * from './lib/types/default-screen.type';
export * from './lib/types/device-devision.type'; export * from './lib/types/device-devision.type';
export * from './lib/types/device-type.type'; export * from './lib/types/device-type.type';
export * from './lib/types/file-transfer-permissions.type'; export * from './lib/types/file-transfer-permissions.type';
export * from './lib/types/locale-code.type'; export * from './lib/types/locale-code.type';
export * from './lib/types/maximum-range.type';
export * from './lib/types/notification-method.type'; export * from './lib/types/notification-method.type';
export * from './lib/types/organization-chart-permissions.type'; export * from './lib/types/organization-chart-permissions.type';
export * from './lib/types/push-type.type'; export * from './lib/types/push-type.type';
@ -20,4 +20,4 @@ export * from './lib/types/status-code.type';
export * from './lib/types/status-type.type'; export * from './lib/types/status-type.type';
export * from './lib/types/video-conference-type.type'; export * from './lib/types/video-conference-type.type';
export * from './lib/types/video-conference-type.type'; export * from './lib/utils/string.util';

View File

@ -1,17 +1,15 @@
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { NativeService, WindowState } from '@ucap-webmessenger/native'; import {
NativeService,
WindowState,
NotiRequest
} from '@ucap-webmessenger/native';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
export class BrowserNativeService implements NativeService { export class BrowserNativeService implements NativeService {
showNotify( showNotify(noti: NotiRequest): void {}
roomSeq: number,
title: string,
contents: string,
image: string,
useSound: boolean
): void {}
checkForUpdates(): Observable<boolean> { checkForUpdates(): Observable<boolean> {
return new Observable<boolean>(subscriber => { return new Observable<boolean>(subscriber => {

View File

@ -2,7 +2,11 @@ import { ipcRenderer, remote, IpcRendererEvent } from 'electron';
import { Observable, Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { NativeService, WindowState } from '@ucap-webmessenger/native'; import {
NativeService,
WindowState,
NotiRequest
} from '@ucap-webmessenger/native';
import { Channel } from '../types/channel.type'; import { Channel } from '../types/channel.type';
import { share } from 'rxjs/operators'; import { share } from 'rxjs/operators';
@ -10,20 +14,15 @@ export class ElectronNativeService implements NativeService {
private windowStateChangedSubject: Subject<WindowState> | null = null; private windowStateChangedSubject: Subject<WindowState> | null = null;
private windowStateChanged$: Observable<WindowState> | null = null; private windowStateChanged$: Observable<WindowState> | null = null;
showNotify( showNotify(noti: NotiRequest): void {
roomSeq: number,
title: string,
contents: string,
image: string,
useSound: boolean
): void {
ipcRenderer.send( ipcRenderer.send(
Channel.showNotify, Channel.showNotify,
roomSeq, noti.roomSeq,
title, noti.title,
contents, noti.contents,
image, noti.image,
useSound noti.useSound,
noti.interval
); );
} }

View File

@ -3,13 +3,7 @@ import { Observable } from 'rxjs';
import { WindowState } from '../types/window-state.type'; import { WindowState } from '../types/window-state.type';
export interface NativeService { export interface NativeService {
showNotify( showNotify(noti: NotiRequest): void;
roomSeq: number,
title: string,
contents: string,
image: string,
useSound: boolean
): void;
checkForUpdates(): Observable<boolean>; checkForUpdates(): Observable<boolean>;
@ -23,3 +17,12 @@ export interface NativeService {
windowMinimize(): void; windowMinimize(): void;
windowMaximize(): void; windowMaximize(): void;
} }
export interface NotiRequest {
roomSeq: string;
title: string;
contents: string;
image: string;
useSound: boolean;
interval?: number;
}

View File

@ -35,8 +35,6 @@ export interface InfoResponse extends ProtocolResponse {
baseSeq: number; baseSeq: number;
// 유효한파일기준이벤트SEQ(n) // 유효한파일기준이벤트SEQ(n)
validFileBaseSeq: number; validFileBaseSeq: number;
// 이벤트정보 개수(n)
count: number;
} }
export const encodeInfo: ProtocolEncoder<InfoRequest> = (req: InfoRequest) => { export const encodeInfo: ProtocolEncoder<InfoRequest> = (req: InfoRequest) => {
@ -82,7 +80,6 @@ export const decodeInfo: ProtocolDecoder<InfoResponse> = (
return decodeProtocolMessage(message, { return decodeProtocolMessage(message, {
roomSeq: message.bodyList[0], roomSeq: message.bodyList[0],
baseSeq: message.bodyList[1], baseSeq: message.bodyList[1],
validFileBaseSeq: message.bodyList[2], validFileBaseSeq: message.bodyList[2]
count: message.bodyList[3]
} as InfoResponse); } as InfoResponse);
}; };

View File

@ -17,7 +17,7 @@
</ul> </ul>
</div> </div>
<div class="btn-box"> <div class="btn-box">
<ul *ngIf="expired"> <ul *ngIf="expired" class="expired">
<li>기간이 만료된 파일입니다.</li> <li>기간이 만료된 파일입니다.</li>
</ul> </ul>
<ul *ngIf="!expired && fileInfo && fileInfo.AttSEQ"> <ul *ngIf="!expired && fileInfo && fileInfo.AttSEQ">

View File

@ -102,5 +102,14 @@
height: 100%; height: 100%;
} }
} }
&.expired{
li{
width:100%;
white-space: nowrap;
color:#999999;
align-items: center;
line-height:40px;
}
}
} }
} }

View File

@ -1,4 +1,7 @@
<div class="chat-messages"> <div class="chat-messages">
<!-- <div class="message-row" *ngIf="eventRemain">
<button mat-button (click)="onClickMore($event)">이전 대화 보기</button>
</div> -->
<!-- MESSAGE --> <!-- MESSAGE -->
<div <div
*ngFor="let message of messages; let i = index" *ngFor="let message of messages; let i = index"
@ -34,6 +37,7 @@
<ucap-chat-message-box-information <ucap-chat-message-box-information
*ngSwitchCase="EventType.Exit" *ngSwitchCase="EventType.Exit"
[message]="message" [message]="message"
class="information-msg"
> >
</ucap-chat-message-box-information> </ucap-chat-message-box-information>
<ucap-chat-message-box-information <ucap-chat-message-box-information

View File

@ -144,13 +144,13 @@ $meBox-bg: #ffffff;
width: 100%; width: 100%;
height: 100%; height: 100%;
text-align: center; text-align: center;
background-color: #cccccc; background-color: #dddddd;
padding: 10px; padding: 10px;
margin: 10px 0; margin: 10px 0;
} }
.message-row { .message-row {
margin-bottom: 30px; margin-bottom: 20px;
.date-splitter { .date-splitter {
display: block; display: block;
width: 100%; width: 100%;
@ -162,6 +162,9 @@ $meBox-bg: #ffffff;
flex-direction: row; flex-direction: row;
.profile-img { .profile-img {
flex: 0 0 auto; flex: 0 0 auto;
img{
border-radius: 50%;
}
} }
} }
&.me { &.me {

View File

@ -1,3 +1,4 @@
import { CONST } from '@ucap-webmessenger/core';
import { import {
Component, Component,
OnInit, OnInit,
@ -32,17 +33,20 @@ export class MessagesComponent implements OnInit {
@Input() @Input()
eventInfoStatus?: InfoResponse; eventInfoStatus?: InfoResponse;
@Input() @Input()
eventRemain: boolean;
@Input()
userInfos?: UserInfo[]; userInfos?: UserInfo[];
@Input() @Input()
sessionVerInfo: VersionInfo2Response; sessionVerInfo: VersionInfo2Response;
@Output()
moreEvent = new EventEmitter<number>();
@Output() @Output()
massDetail = new EventEmitter<number>(); massDetail = new EventEmitter<number>();
@Output() @Output()
imageViewer = new EventEmitter<FileInfo>(); imageViewer = new EventEmitter<FileInfo>();
@Output() @Output()
save = new EventEmitter<{ fileInfo: FileInfo; type: string }>(); save = new EventEmitter<{ fileInfo: FileInfo; type: string }>();
@Output() @Output()
contextMenu = new EventEmitter<{ contextMenu = new EventEmitter<{
event: MouseEvent; event: MouseEvent;
@ -50,6 +54,7 @@ export class MessagesComponent implements OnInit {
}>(); }>();
EventType = EventType; EventType = EventType;
CONST = CONST;
profileImageRoot: string; profileImageRoot: string;
constructor(private logger: NGXLogger, private datePipe: DatePipe) {} constructor(private logger: NGXLogger, private datePipe: DatePipe) {}
@ -138,6 +143,13 @@ export class MessagesComponent implements OnInit {
return false; return false;
} }
onClickMore(event: any) {
event.preventDefault();
event.stopPropagation();
this.moreEvent.emit(this.messages[0].seq);
}
/** [Event] MassTalk Detail View */ /** [Event] MassTalk Detail View */
onMassDetail(value: number) { onMassDetail(value: number) {
this.massDetail.emit(value); this.massDetail.emit(value);

View File

@ -46,10 +46,10 @@
class="groupExpansionPanel" class="groupExpansionPanel"
> >
<mat-expansion-panel-header> <mat-expansion-panel-header>
<mat-panel-title <mat-panel-title class="panel-title">
>{{ groupBuddy.group.name }} <div class="title-name ellipsis">{{ groupBuddy.group.name }}</div>
<span>({{ groupBuddy.buddyList.length }}명)</span></mat-panel-title <span class="text-accent-color number">({{ groupBuddy.buddyList.length }}명)</span>
> </mat-panel-title>
<mat-panel-description> <mat-panel-description>
<span class="more-spacer"></span> <span class="more-spacer"></span>
<button <button

View File

@ -38,6 +38,18 @@
.mat-content { .mat-content {
color: #666666; color: #666666;
overflow: unset; overflow: unset;
.panel-title{
display:inline-flex;
.title-name{
display:inline-flex;
flex:1 1 auto;
}
.number{
margin-left:6px;
display: inline-flex;
flex: 0 0 auto;
}
}
} }
} }
} }
@ -47,4 +59,10 @@
::ng-deep .mat-content{ ::ng-deep .mat-content{
overflow: unset; overflow: unset;
} }
.number{
margin-left:6px;
display: inline-flex;
flex: 0 0 auto;
}

View File

@ -1,5 +1,6 @@
<div class="list-search"> <div class="list-search">
<div class="selectbox"> <div class="selectbox">
<!--<mat-label>회사선택</mat-label>-->
<mat-select [(value)]="companyCode"> <mat-select [(value)]="companyCode">
<mat-option <mat-option
*ngFor="let company of companyList" *ngFor="let company of companyList"
@ -10,26 +11,25 @@
</mat-select> </mat-select>
</div> </div>
<div class="searchbox"> <div class="searchbox">
<input <mat-form-field floatLabel="never" style="width:100%;">
matInput <input
#searchWordInput matInput
placeholder="name" #searchWordInput
(keydown.enter)="onKeyDownEnter(searchWordInput.value)" placeholder="name"
/> (keydown.enter)="onKeyDownEnter(searchWordInput.value)"
<div class="btn-search"> />
<button <button
mat-button mat-button
matSuffix matSuffix
mat-icon-button mat-icon-button
aria-label="cancel" aria-label="Clear"
(click)="searchWordInput.value = ''; onClickCancel()" (click)="searchWordInput.value = ''; onClickCancel()"
> >
<mat-icon>close</mat-icon> <mat-icon>close</mat-icon>
</button> </button>
</div> </mat-form-field>
</div> </div>
</div> </div>
<!--검색창만 있는 경우-------------------------------------------------------------------------- <!--검색창만 있는 경우--------------------------------------------------------------------------
<div class="list-search"> <div class="list-search">
<div class="searchbox"> <div class="searchbox">

View File

@ -15,13 +15,19 @@
.list-search { .list-search {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
position: relative;
height: 60px; height: 60px;
align-items: center; align-items: center;
padding: 0 10px 0 20px; padding: 0 10px 0 20px;
font-size: 14px; font-size: 14px;
border-bottom: 1px solid #dddddd; border-bottom: 1px solid #dddddd;
.selectbox {
display: inline-flex;
width: 38%;
margin-right: 2%;
}
.searchbox { .searchbox {
width: 100%; width: 60%;
display: flex; display: flex;
input { input {
display: inline-flex; display: inline-flex;

View File

@ -47,14 +47,15 @@
line-height: 20px; line-height: 20px;
margin-right:10px; margin-right:10px;
.mat-icon-rtl-mirror{ .mat-icon-rtl-mirror{
border: 1px solid #dddddd; border: 1px solid #dddddd;
padding: 2px; padding: 2px;
font-size: 14px; font-size: 14px;
min-width: 14px; min-width: 14px;
min-height: 14px; min-height: 14px;
line-height: 14px; line-height: 14px;
width:20px; width:20px;
height:20px; height:20px;
box-shadow: 0 1px 4px rgba(32, 33, 36, 0.1); box-shadow: 0 2px 1px rgba(48, 48, 48, 0.2);
border-radius: 50%;
} }
} }

View File

@ -9,6 +9,9 @@
[path]="imagePath" [path]="imagePath"
[default]="defaultPath" [default]="defaultPath"
/> />
<span *ngIf="roomInfo.isTimeRoom" class="text-warn-color badge-timer">
<mat-icon>timer</mat-icon>
</span>
<!-- <ucap-ui-imaage <!-- <ucap-ui-imaage
[imageClass]="'thumbnail'" [imageClass]="'thumbnail'"
[base]="sessionVerinfo.profileRoot" [base]="sessionVerinfo.profileRoot"
@ -20,14 +23,16 @@
<div class="detail"> <div class="detail">
<div class="room-name"> <div class="room-name">
<div class="name">{{ getRoomName(roomInfo) }}</div> <div class="name">{{ getRoomName(roomInfo) }}</div>
<div class="num" *ngIf="roomInfo.roomType === RoomType.Multi"> <div class="num text-accent-color" *ngIf="roomInfo.roomType === RoomType.Multi">
{{ roomInfo.joinUserCount }}명 {{ roomInfo.joinUserCount }}명
</div> </div>
<div *ngIf="!checkable && !roomInfo.receiveAlarm"> <div *ngIf="!checkable && !roomInfo.receiveAlarm">
<mat-icon>notifications_off</mat-icon> <mat-icon>notifications_off</mat-icon>
</div> </div>
</div> </div>
<div class="room-msg">{{ finalEventMessage }}</div> <div class="room-msg">
{{ finalEventMessage }}
</div>
</div> </div>
<div class="date"> <div class="date">
@ -44,9 +49,7 @@
</mat-checkbox> </mat-checkbox>
</dd> </dd>
</dl> </dl>
<span *ngIf="roomInfo.isTimeRoom">
<mat-icon>timer</mat-icon>
</span>
<span <span
class="notiBadge" class="notiBadge"
*ngIf="roomInfo.noReadCnt > 0" *ngIf="roomInfo.noReadCnt > 0"

View File

@ -3,9 +3,8 @@ $font-dark: #212121;
$font-mid: #666666; $font-mid: #666666;
$font-light: #848d95; $font-light: #848d95;
$font-white: #ffffff; $font-white: #ffffff;
$line-basic:1px solid #dddddd; $line-basic: 1px solid #dddddd;
$bg-list-hover: #efefef; $bg-list-hover: #efefef;
$color-main:#e53096;
$listH-row2: 80px; $listH-row2: 80px;
$presence-size: 8px; $presence-size: 8px;
$thumbnail-msize: 40px; $thumbnail-msize: 40px;
@ -25,6 +24,23 @@ $thumbnail-msize: 40px;
} }
} }
.badge-timer {
position:absolute;
background-color: #ffffff;
width: 18px;
height: 18px;
border-radius: 50%;
bottom: 14px;
left: 46px;
text-align:center;
.mat-icon{
font-size:14px;
width: 18px;
height: 18px;
line-height:18px;
}
}
.profile { .profile {
white-space: normal; white-space: normal;
text-align: left; text-align: left;
@ -84,7 +100,7 @@ $thumbnail-msize: 40px;
width: $thumbnail-msize; width: $thumbnail-msize;
height: $thumbnail-msize; height: $thumbnail-msize;
margin-right: 16px; margin-right: 16px;
border-radius:50%; border-radius: 50%;
} }
.info { .info {
position: relative; position: relative;
@ -117,9 +133,8 @@ $thumbnail-msize: 40px;
} }
.num { .num {
font-size: 12px; font-size: 12px;
color: $color-main;
flex: none; flex: none;
margin-left: 10px; margin-left: 6px;
} }
} }
.room-msg { .room-msg {
@ -155,9 +170,8 @@ $thumbnail-msize: 40px;
padding: 0 6px; padding: 0 6px;
font-size: 11px; font-size: 11px;
color: #ffffff; color: #ffffff;
background-color: $color-main;
@include ellipsis(1); @include ellipsis(1);
border-radius:50%; border-radius: 50%;
} }
.notiBadge { .notiBadge {

View File

@ -17,4 +17,11 @@
.mat-card-header .mat-card-title{ .mat-card-header .mat-card-title{
margin:0 -16px; margin:0 -16px;
padding-bottom:10px; padding-bottom:10px;
}
.search-result{
.result-num{
padding:10px;
display:flex;
height:40px;
}
} }