0220 업무 번들이미지 뷰어 작업
This commit is contained in:
parent
ef603030ef
commit
41499618ea
|
@ -3,9 +3,166 @@
|
||||||
모바일로 테스트 하지 못할경우
|
모바일로 테스트 하지 못할경우
|
||||||
JSON 데이터 만들어서 테스트 진행
|
JSON 데이터 만들어서 테스트 진행
|
||||||
state
|
state
|
||||||
|
|
||||||
reducer
|
reducer
|
||||||
|
|
||||||
action
|
action
|
||||||
sync-service
|
sync-service
|
||||||
테스트 후 수정
|
테스트 후 수정
|
||||||
PhoneBookData 추가
|
PhoneBookData 추가
|
||||||
|
|
||||||
|
파일 뷰어
|
||||||
|
common-api-service.ts
|
||||||
|
file-viewer.dialog.component.html
|
||||||
|
file-viewer.dialog.component.ts
|
||||||
|
messages.component.ts ucap-webmessenger-app/layouts/messenger/component
|
||||||
|
file.servic.ts ucap-webmessenger-app/service
|
||||||
|
bundle-image.event-json.ts
|
||||||
|
file.event-json.ts
|
||||||
|
mass-text.event-json.ts
|
||||||
|
mass-translation.event-json.ts
|
||||||
|
plan.event-json.ts
|
||||||
|
video-confrence.event-json.ts
|
||||||
|
file-info.ts ucap-webmessenger-protocol-file/src/lib/models
|
||||||
|
public-api.ts ucap-webmessenger-ui/src
|
||||||
|
ucap-ui.module.ts ucap-webmessenger-ui/src/lib
|
||||||
|
file-viewer.compoent.html
|
||||||
|
file-viewer.compoent.ts
|
||||||
|
media-viewe.component.html
|
||||||
|
media-viewe.component.scss
|
||||||
|
media-viewe.component.spec.ts
|
||||||
|
media-viewe.component.ts
|
||||||
|
binary-viewe.component.html
|
||||||
|
binary-viewe.component.ts
|
||||||
|
document-viewer.component.ts
|
||||||
|
image-viewer.component.html
|
||||||
|
image-viewer.component.ts
|
||||||
|
sound-viewer.component.html
|
||||||
|
sound-viewer.component.ts
|
||||||
|
video-viewer.component.html
|
||||||
|
video-viewer.component.ts
|
||||||
|
select-file-info.ts
|
||||||
|
message-box.component.ts
|
||||||
|
messages.component.ts ucap-webmessenger-ui-chat/src /lib/component
|
||||||
|
bundle-image.component.html
|
||||||
|
bundle-image.component.ts
|
||||||
|
file.component.ts ucap-webmessenger-ui-chat/src/lib/component/message-box
|
||||||
|
|
||||||
|
요구사항
|
||||||
|
이미지 뷰어 묶음 파일 출력 기능
|
||||||
|
기존 이미지 타입에 영향 주지 않아야 한다.
|
||||||
|
이미지 뷰어 묶음 파일 다운로드 기능
|
||||||
|
이미지 뷰어 이전/다음 기능
|
||||||
|
|
||||||
|
메세지 출력
|
||||||
|
메세지 박스 출력
|
||||||
|
메세지 파일 출력
|
||||||
|
|
||||||
|
기존 파일 뷰어 분석
|
||||||
|
app->messages.component 호출
|
||||||
|
파일 클릭 이벤트 정의()
|
||||||
|
room-info 서브젝트 생성
|
||||||
|
이벤트 리스트 서브젝트 생성
|
||||||
|
fileviewer 클릭 이벤트 생성
|
||||||
|
ui-chat->messages.component 호출
|
||||||
|
room-info 서브젝트 전달
|
||||||
|
이벤트 서브젝트 전달
|
||||||
|
fileviewer 클릭 이벤트 전달
|
||||||
|
[for message.length]
|
||||||
|
ui-chat->message-box.component 호출
|
||||||
|
message 정보 전달
|
||||||
|
room-info 전달
|
||||||
|
파일저장 클릭 이벤트 전달
|
||||||
|
fileviewer 클릭 이벤트 전달(FileEventJson)
|
||||||
|
ui-chat->message-box.component.html
|
||||||
|
EventType.File
|
||||||
|
ucap-chat-message-box-file 호출
|
||||||
|
room-info 전달
|
||||||
|
fileviewer 클릭 이벤트 전달 (FileEventJson)
|
||||||
|
EventType.BundleImage
|
||||||
|
ucap-chat-message-box-bundle-image 호출
|
||||||
|
room-info 전달
|
||||||
|
파일 클릭 이벤트 실행(FileEventJson)
|
||||||
|
file-viewer.dialog.component.ts 호출
|
||||||
|
fileInfo, downloadUrl, userSeq, deviceType, token 전달
|
||||||
|
download 클릭 이벤트 정의
|
||||||
|
file-viewer.component.ts 호출
|
||||||
|
fileInfo 전달
|
||||||
|
fileDownloadUrl 전달
|
||||||
|
download 클릭 이벤트 전달()
|
||||||
|
클로즈 이벤트 전달
|
||||||
|
file-viewer.component.html
|
||||||
|
FileViewerType 구분
|
||||||
|
ucap-image-viewer
|
||||||
|
fileInfo 전달
|
||||||
|
fileDownloadUrl 전달
|
||||||
|
closed 이벤트 클릭 전달
|
||||||
|
download 이벤트 클릭 전달
|
||||||
|
|
||||||
|
수정 파일 뷰어 분석
|
||||||
|
app->messages.component 호출
|
||||||
|
파일 정보 리스트 서브젝트 생성
|
||||||
|
선택 파일 정보 맵핑
|
||||||
|
이벤트 정의(선택 파일)
|
||||||
|
이미지 뷰어 다이얼로그 호출
|
||||||
|
ui-chat->messages.component 호출
|
||||||
|
파일 클릭 이벤트(상위호출)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
시작
|
||||||
|
layout/messages.component.ts
|
||||||
|
ui-chat/messages.compoent
|
||||||
|
ui-chat/message-box.component
|
||||||
|
message-box/file.component.ts
|
||||||
|
image.component
|
||||||
|
|
||||||
|
onFileViewer(fileInfo)
|
||||||
|
layout/messages.component.ts-> onFileViewer(fileInfo) 실행
|
||||||
|
layout/common/dialog/file-view.dialog.component.ts
|
||||||
|
ui/file-viewer.component
|
||||||
|
ui/document-viewer.component.ts
|
||||||
|
ui/image-viewer.component.ts
|
||||||
|
ui/sound-viewer.component.ts
|
||||||
|
ui/video-viewer.compoent.ts
|
||||||
|
ui/binary-viewer.component.ts
|
||||||
|
끝
|
||||||
|
|
||||||
|
있는것
|
||||||
|
파일정보 리스트
|
||||||
|
방정보
|
||||||
|
사용자 정보
|
||||||
|
|
||||||
|
없는것
|
||||||
|
|
||||||
|
추가 기능
|
||||||
|
대화방 미디어 이미지 이전/다음 처리
|
||||||
|
메세지 박스 번들이미지 클릭 처리
|
||||||
|
미디어 이미지 뷰어 (이미지, 번들 이미지, 사운드 이미지, 비디오 이미지) 처리
|
||||||
|
번들이미지 다운로드 처리
|
||||||
|
|
||||||
|
정보
|
||||||
|
대화방 전체 파일정보 리스트
|
||||||
|
현재 사용자 선택 파일 정보
|
||||||
|
|
||||||
|
file-viewer.dialog.component.ts
|
||||||
|
현재파일 정보
|
||||||
|
|
||||||
|
번들이미지 다운로드 처리
|
||||||
|
|
||||||
|
대화방 전체 파일정보 변경에 대한 구독 등록
|
||||||
|
|
||||||
|
|
||||||
|
미디어 뷰어 컴포넌트 생성
|
||||||
|
ui/image-viewer.component.ts
|
||||||
|
ui/sound-viewer.component.ts
|
||||||
|
ui/video-viewer.compoent.ts
|
||||||
|
|
||||||
|
|
||||||
|
layout/messages.component.ts
|
||||||
|
|
||||||
|
|
||||||
|
TODO
|
||||||
|
설정->쪽지 알림 메뉴 삭제 (주석)
|
||||||
|
설정->원격 지원 메뉴 삭제 (주석)
|
||||||
|
left-menu->전화걸기 메뉴 삭제 (주석)
|
30
documents/업무/2월/3째주/0221.txt
Normal file
30
documents/업무/2월/3째주/0221.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
추가 기능
|
||||||
|
대화방 미디어 이미지 이전/다음 처리
|
||||||
|
메세지 박스 번들이미지 클릭 처리
|
||||||
|
미디어 이미지 뷰어 (이미지, 번들 이미지, 사운드 이미지, 비디오 이미지) 처리
|
||||||
|
번들이미지 다운로드 처리
|
||||||
|
|
||||||
|
|
||||||
|
TODO
|
||||||
|
설정->쪽지 알림 메뉴 삭제 (주석)
|
||||||
|
설정->원격 지원 메뉴 삭제 (주석)
|
||||||
|
left-menu->전화걸기 메뉴 삭제 (주석)
|
||||||
|
|
||||||
|
|
||||||
|
시작
|
||||||
|
layout/messages.component.ts
|
||||||
|
ui-chat/messages.compoent
|
||||||
|
ui-chat/message-box.component
|
||||||
|
message-box/file.component.ts
|
||||||
|
image.component
|
||||||
|
|
||||||
|
onFileViewer(fileInfo)
|
||||||
|
layout/messages.component.ts-> onFileViewer(fileInfo) 실행
|
||||||
|
layout/common/dialog/file-view.dialog.component.ts
|
||||||
|
ui/file-viewer.component
|
||||||
|
ui/document-viewer.component.ts
|
||||||
|
ui/image-viewer.component.ts
|
||||||
|
ui/sound-viewer.component.ts
|
||||||
|
ui/video-viewer.compoent.ts
|
||||||
|
ui/binary-viewer.component.ts
|
||||||
|
끝
|
|
@ -0,0 +1,23 @@
|
||||||
|
<div
|
||||||
|
class="bubble-main"
|
||||||
|
(mouseenter)="mouseEnter($event)"
|
||||||
|
(mouseleave)="mouseLeave($event)"
|
||||||
|
>
|
||||||
|
<div *ngIf="showExpired" class="expired-text">
|
||||||
|
<span>{{ 'common.file.errors.expired' | translate }}</span>
|
||||||
|
</div>
|
||||||
|
<mat-grid-list
|
||||||
|
cols="6"
|
||||||
|
rowHeight="100px"
|
||||||
|
gutterSize="3px"
|
||||||
|
style="width: 300px;"
|
||||||
|
>
|
||||||
|
<mat-grid-tile
|
||||||
|
*ngFor="let tile of tiles; let i = index"
|
||||||
|
[colspan]="tile.colspan"
|
||||||
|
(click)="onClickFileViewer(i)"
|
||||||
|
>
|
||||||
|
<img src="{{ tile.imgSrc }}" />
|
||||||
|
</mat-grid-tile>
|
||||||
|
</mat-grid-list>
|
||||||
|
</div>
|
123
documents/업무/2월/3째주/file-viewer-prj/bundle-image.component.ts
Normal file
123
documents/업무/2월/3째주/file-viewer-prj/bundle-image.component.ts
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
|
||||||
|
import { BundleImageEventJson, Info } from '@ucap-webmessenger/protocol-event';
|
||||||
|
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { RoomInfo } from '@ucap-webmessenger/protocol-room';
|
||||||
|
import { SelectFileInfo } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
|
export interface Tile {
|
||||||
|
colspan?: number;
|
||||||
|
imgSrc?: string;
|
||||||
|
}
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-message-box-bundle-image',
|
||||||
|
templateUrl: './bundle-image.component.html',
|
||||||
|
styleUrls: ['./bundle-image.component.scss']
|
||||||
|
})
|
||||||
|
export class BundleImageComponent implements OnInit {
|
||||||
|
@Input()
|
||||||
|
set message(m: Info<BundleImageEventJson>) {
|
||||||
|
this._message = m;
|
||||||
|
|
||||||
|
this.makeTileGrid();
|
||||||
|
}
|
||||||
|
get message() {
|
||||||
|
return this._message;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
_message: Info<BundleImageEventJson>;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
save = new EventEmitter<{
|
||||||
|
fileInfo: BundleImageEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
type: string;
|
||||||
|
}>();
|
||||||
|
@Output()
|
||||||
|
fileViewer = new EventEmitter<SelectFileInfo>();
|
||||||
|
|
||||||
|
showExpired = false;
|
||||||
|
tiles: Tile[] = [];
|
||||||
|
|
||||||
|
constructor(private logger: NGXLogger) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.makeTileGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
makeTileGrid() {
|
||||||
|
if (
|
||||||
|
!this.message.sentMessageJson.thumbUrls ||
|
||||||
|
0 === this.message.sentMessageJson.thumbUrls.length ||
|
||||||
|
1 > this.message.sentMessageJson.fileCount
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fileCount = Number(this.message.sentMessageJson.fileCount);
|
||||||
|
|
||||||
|
this.tiles = [];
|
||||||
|
|
||||||
|
const remainder = fileCount % 3;
|
||||||
|
const quotient = Math.floor(fileCount / 3);
|
||||||
|
|
||||||
|
let checkCount: number;
|
||||||
|
|
||||||
|
if (remainder === 1 && fileCount > 1) {
|
||||||
|
checkCount = quotient - 1;
|
||||||
|
} else if (remainder === 2 && fileCount > 1) {
|
||||||
|
checkCount = quotient;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.message.sentMessageJson.thumbUrls.forEach((v, idx) => {
|
||||||
|
const tile: Tile = {};
|
||||||
|
|
||||||
|
if (checkCount <= Math.floor(idx / 3)) {
|
||||||
|
tile.colspan = 3;
|
||||||
|
} else if (fileCount === 1) {
|
||||||
|
tile.colspan = 6;
|
||||||
|
} else {
|
||||||
|
tile.colspan = 2;
|
||||||
|
}
|
||||||
|
tile.imgSrc = this.message.sentMessageJson.baseUrl + v;
|
||||||
|
this.tiles.push(tile);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseEnter(event: MouseEvent): void {
|
||||||
|
if (!this.roomInfo || this.roomInfo.expiredFileStdSeq > this.message.seq) {
|
||||||
|
this.showExpired = true;
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
mouseLeave(event: MouseEvent): void {
|
||||||
|
if (!this.roomInfo || this.roomInfo.expiredFileStdSeq > this.message.seq) {
|
||||||
|
this.showExpired = false;
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
getExpiredFile() {
|
||||||
|
if (
|
||||||
|
!!this.roomInfo &&
|
||||||
|
this.roomInfo.expiredFileStdSeq <= this.message.seq
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickFileViewer(index: number) {
|
||||||
|
if (!this.getExpiredFile()) {
|
||||||
|
this.fileViewer.emit({
|
||||||
|
attachmentSeq: this._message.sentMessageJson.attachmentSeq,
|
||||||
|
index
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api';
|
||||||
|
import { EventJsonDecoder } from './event-json';
|
||||||
|
|
||||||
|
export interface BundleImageEventJson {
|
||||||
|
statusCode?: StatusCode;
|
||||||
|
errorMessage?: string;
|
||||||
|
roomSeq?: number;
|
||||||
|
attachmentSeq?: number;
|
||||||
|
fileCount?: number;
|
||||||
|
baseUrl?: string;
|
||||||
|
thumbUrls?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeBundleImageEventJson: EventJsonDecoder<BundleImageEventJson> = (
|
||||||
|
message: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const json = JsonAnalization.receiveAnalization(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: json.StatusCode,
|
||||||
|
errorMessage: json.ErrorMessage,
|
||||||
|
roomSeq: Number(json.RoomID),
|
||||||
|
attachmentSeq: Number(json.AttSEQ),
|
||||||
|
fileCount: Number(json.FileCount),
|
||||||
|
baseUrl: json.BaseURL,
|
||||||
|
thumbUrls: json.ThumbURL
|
||||||
|
} as BundleImageEventJson;
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
statusCode: StatusCode.Fail,
|
||||||
|
errorMessage: e.toString()
|
||||||
|
} as BundleImageEventJson;
|
||||||
|
}
|
||||||
|
};
|
409
documents/업무/2월/3째주/file-viewer-prj/common-api.service.ts
Normal file
409
documents/업무/2월/3째주/file-viewer-prj/common-api.service.ts
Normal file
|
@ -0,0 +1,409 @@
|
||||||
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpClient,
|
||||||
|
HttpEventType,
|
||||||
|
HttpResponse,
|
||||||
|
HttpRequest
|
||||||
|
} from '@angular/common/http';
|
||||||
|
|
||||||
|
import { Observable, Subject } from 'rxjs';
|
||||||
|
import { map, filter } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FileProfileSaveRequest,
|
||||||
|
FileProfileSaveResponse,
|
||||||
|
encodeFileProfileSave,
|
||||||
|
decodeFileProfileSave
|
||||||
|
} from '../apis/file-profile-save';
|
||||||
|
import {
|
||||||
|
FileTalkDownloadRequest,
|
||||||
|
encodeFileTalkDownload,
|
||||||
|
encodeFormDataFileTalkDownload
|
||||||
|
} from '../apis/file-talk-download';
|
||||||
|
import {
|
||||||
|
FileTalkSaveRequest,
|
||||||
|
FileTalkSaveResponse,
|
||||||
|
encodeFileTalkSave,
|
||||||
|
decodeFileTalkSave
|
||||||
|
} from '../apis/file-talk-save';
|
||||||
|
import {
|
||||||
|
FileTalkSaveMultiRequest,
|
||||||
|
FileTalkSaveMultiResponse,
|
||||||
|
encodeFileTalkSaveMulti,
|
||||||
|
decodeFileTalkSaveMulti
|
||||||
|
} from '../apis/file-talk-save-multi';
|
||||||
|
import {
|
||||||
|
FileTalkShareRequest,
|
||||||
|
FileTalkShareResponse,
|
||||||
|
encodeFileTalkShare,
|
||||||
|
decodeFileTalkShare
|
||||||
|
} from '../apis/file-talk-share';
|
||||||
|
import {
|
||||||
|
MassTalkDownloadRequest,
|
||||||
|
MassTalkDownloadResponse,
|
||||||
|
encodeMassTalkDownload,
|
||||||
|
decodeMassTalkDownload
|
||||||
|
} from '../apis/mass-talk-download';
|
||||||
|
import {
|
||||||
|
MassTalkSaveRequest,
|
||||||
|
MassTalkSaveResponse,
|
||||||
|
encodeMassTalkSave,
|
||||||
|
decodeMassTalkSave
|
||||||
|
} from '../apis/mass-talk-save';
|
||||||
|
import {
|
||||||
|
TransMassTalkDownloadRequest,
|
||||||
|
TransMassTalkDownloadResponse,
|
||||||
|
encodeTransMassTalkDownload,
|
||||||
|
decodeTransMassTalkDownload
|
||||||
|
} from '../apis/trans-mass-talk-download';
|
||||||
|
import {
|
||||||
|
TransMassTalkSaveRequest,
|
||||||
|
TransMassTalkSaveResponse,
|
||||||
|
encodeTransMassTalkSave,
|
||||||
|
decodeTransMassTalkSave
|
||||||
|
} from '../apis/trans-mass-talk-save';
|
||||||
|
import {
|
||||||
|
TranslationReqRequest,
|
||||||
|
TranslationReqResponse,
|
||||||
|
encodeTranslationReq,
|
||||||
|
decodeTranslationReq
|
||||||
|
} from '../apis/translation-req';
|
||||||
|
import {
|
||||||
|
TranslationSaveRequest,
|
||||||
|
TranslationSaveResponse,
|
||||||
|
encodeTranslationSave,
|
||||||
|
decodeTranslationSave
|
||||||
|
} from '../apis/translation-save';
|
||||||
|
|
||||||
|
import { _MODULE_CONFIG } from '../config/token';
|
||||||
|
import { ModuleConfig } from '../config/module-config';
|
||||||
|
import { Urls } from '../config/urls';
|
||||||
|
import { UrlConfig } from '@ucap-webmessenger/core';
|
||||||
|
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class CommonApiService {
|
||||||
|
readonly urls: Urls;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig,
|
||||||
|
private httpClient: HttpClient
|
||||||
|
) {
|
||||||
|
this.urls = UrlConfig.getUrls(
|
||||||
|
this.moduleConfig.hostConfig,
|
||||||
|
this.moduleConfig.urls
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileProfileSave(
|
||||||
|
req: FileProfileSaveRequest,
|
||||||
|
fileProfileSaveUrl?: string
|
||||||
|
): Observable<FileProfileSaveResponse> {
|
||||||
|
const httpReq = new HttpRequest(
|
||||||
|
'POST',
|
||||||
|
!!fileProfileSaveUrl ? fileProfileSaveUrl : this.urls.fileProfileSave,
|
||||||
|
encodeFileProfileSave(req),
|
||||||
|
{ reportProgress: true, responseType: 'text' as 'json' }
|
||||||
|
);
|
||||||
|
|
||||||
|
const progress = req.fileUploadItem.uploadStart();
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
} else if (HttpEventType.UploadProgress === event.type) {
|
||||||
|
progress.next(Math.round((100 * event.loaded) / event.total));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
map((event: HttpResponse<any>) => {
|
||||||
|
req.fileUploadItem.uploadComplete();
|
||||||
|
return decodeFileProfileSave(event.body);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public urlForFileTalkDownload(
|
||||||
|
req: FileTalkDownloadRequest,
|
||||||
|
fileTalkDownloadUrl?: string
|
||||||
|
): string {
|
||||||
|
const httpReq = new HttpRequest(
|
||||||
|
'GET',
|
||||||
|
!!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
params: encodeFileTalkDownload(req)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return httpReq.urlWithParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileTalkDownload(
|
||||||
|
req: FileTalkDownloadRequest,
|
||||||
|
fileTalkDownloadUrl?: string
|
||||||
|
): Observable<Blob> {
|
||||||
|
const httpReq = new HttpRequest(
|
||||||
|
'POST',
|
||||||
|
!!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload,
|
||||||
|
encodeFormDataFileTalkDownload(req),
|
||||||
|
{ reportProgress: true, responseType: 'blob' }
|
||||||
|
);
|
||||||
|
|
||||||
|
let progress: Subject<number>;
|
||||||
|
if (!!req.fileDownloadItem) {
|
||||||
|
progress = req.fileDownloadItem.downloadStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
} else if (HttpEventType.DownloadProgress === event.type) {
|
||||||
|
if (!!progress) {
|
||||||
|
progress.next(Math.round((100 * event.loaded) / event.total));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
map((event: HttpResponse<any>) => {
|
||||||
|
if (!!progress) {
|
||||||
|
req.fileDownloadItem.downloadComplete();
|
||||||
|
}
|
||||||
|
return event.body;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileTalkSave(
|
||||||
|
req: FileTalkSaveRequest,
|
||||||
|
fileTalkSaveUrl?: string
|
||||||
|
): Observable<FileTalkSaveResponse> {
|
||||||
|
const httpReq = new HttpRequest(
|
||||||
|
'POST',
|
||||||
|
!!fileTalkSaveUrl ? fileTalkSaveUrl : this.urls.fileTalkSave,
|
||||||
|
encodeFileTalkSave(req),
|
||||||
|
{ reportProgress: true, responseType: 'text' as 'json' }
|
||||||
|
);
|
||||||
|
|
||||||
|
const progress = req.fileUploadItem.uploadStart();
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
} else if (HttpEventType.UploadProgress === event.type) {
|
||||||
|
progress.next(Math.round((100 * event.loaded) / event.total));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
map((event: HttpResponse<any>) => {
|
||||||
|
req.fileUploadItem.uploadComplete();
|
||||||
|
return decodeFileTalkSave(event.body);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileTalkSaveMulti(
|
||||||
|
req: FileTalkSaveMultiRequest,
|
||||||
|
fileTalkSaveMultiUrl?: string
|
||||||
|
): Observable<FileTalkSaveMultiResponse> {
|
||||||
|
const httpReq = new HttpRequest(
|
||||||
|
'POST',
|
||||||
|
!!fileTalkSaveMultiUrl
|
||||||
|
? fileTalkSaveMultiUrl
|
||||||
|
: this.urls.fileTalkSaveMulti,
|
||||||
|
encodeFileTalkSaveMulti(req),
|
||||||
|
{ reportProgress: true, responseType: 'text' as 'json' }
|
||||||
|
);
|
||||||
|
|
||||||
|
const progress = req.fileUploadItem.uploadStart();
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
} else if (HttpEventType.UploadProgress === event.type) {
|
||||||
|
progress.next(Math.round((100 * event.loaded) / event.total));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
map((event: HttpResponse<any>) => {
|
||||||
|
req.fileUploadItem.uploadComplete();
|
||||||
|
return decodeFileTalkSaveMulti(event.body);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public acceptableExtensionForFileTalk(
|
||||||
|
extensions: string[]
|
||||||
|
): { accept: boolean; reject: string[] } {
|
||||||
|
let accept = true;
|
||||||
|
const reject: string[] = [];
|
||||||
|
for (const extension of extensions) {
|
||||||
|
if (
|
||||||
|
-1 ===
|
||||||
|
this.moduleConfig.acceptableFileExtensions.indexOf(
|
||||||
|
extension.toLowerCase()
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
reject.push(extension);
|
||||||
|
accept = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
accept,
|
||||||
|
reject
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileTalkShare(
|
||||||
|
req: FileTalkShareRequest
|
||||||
|
): Observable<FileTalkShareResponse> {
|
||||||
|
return this.httpClient
|
||||||
|
.post<any>(
|
||||||
|
this.urls.fileTalkShare,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
params: encodeFileTalkShare(req)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(map(res => decodeFileTalkShare(res)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public massTalkDownload(
|
||||||
|
req: MassTalkDownloadRequest
|
||||||
|
): Observable<MassTalkDownloadResponse> {
|
||||||
|
return this.httpClient
|
||||||
|
.post<any>(
|
||||||
|
this.urls.massTalkDownload,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
params: encodeMassTalkDownload(req),
|
||||||
|
responseType: 'text' as 'json'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(map(res => decodeMassTalkDownload(res)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public massTalkSave(
|
||||||
|
req: MassTalkSaveRequest
|
||||||
|
): Observable<MassTalkSaveResponse> {
|
||||||
|
const httpReq = new HttpRequest(
|
||||||
|
'POST',
|
||||||
|
this.urls.massTalkSave,
|
||||||
|
encodeMassTalkSave(req),
|
||||||
|
{ reportProgress: true, responseType: 'text' as 'json' }
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
map(res => decodeMassTalkSave((res as HttpResponse<any>).body))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public transMassTalkDownload(
|
||||||
|
req: TransMassTalkDownloadRequest
|
||||||
|
): Observable<TransMassTalkDownloadResponse> {
|
||||||
|
return this.httpClient
|
||||||
|
.post<any>(
|
||||||
|
this.urls.transMassTalkDownload,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
params: encodeTransMassTalkDownload(req)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(map(res => decodeTransMassTalkDownload(res)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public transMassTalkSave(
|
||||||
|
req: TransMassTalkSaveRequest
|
||||||
|
): Observable<TransMassTalkSaveResponse> {
|
||||||
|
return this.httpClient
|
||||||
|
.post<any>(
|
||||||
|
this.urls.transMassTalkSave,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
params: encodeTransMassTalkSave(req)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(map(res => decodeTransMassTalkSave(res)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public translationReq(
|
||||||
|
req: TranslationReqRequest
|
||||||
|
): Observable<TranslationReqResponse> {
|
||||||
|
return this.httpClient
|
||||||
|
.post<any>(
|
||||||
|
this.urls.translationReq,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
params: encodeTranslationReq(req)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.pipe(map(res => decodeTranslationReq(res)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public translationSave(
|
||||||
|
req: TranslationSaveRequest
|
||||||
|
): Observable<TranslationSaveResponse> {
|
||||||
|
const httpReq = new HttpRequest(
|
||||||
|
'POST',
|
||||||
|
this.urls.translationSave,
|
||||||
|
encodeTranslationSave(req),
|
||||||
|
{ reportProgress: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
map(res => decodeTranslationSave((res as HttpResponse<any>).body))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public fileDownload(
|
||||||
|
fileDownloadUrl: string,
|
||||||
|
fileDownloadItem: FileDownloadItem
|
||||||
|
): Observable<Blob> {
|
||||||
|
const httpReq = new HttpRequest('GET', fileDownloadUrl, null, {
|
||||||
|
reportProgress: true,
|
||||||
|
responseType: 'blob'
|
||||||
|
});
|
||||||
|
|
||||||
|
let progress: Subject<number>;
|
||||||
|
if (!!fileDownloadItem) {
|
||||||
|
progress = fileDownloadItem.downloadStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.httpClient.request(httpReq).pipe(
|
||||||
|
filter(event => {
|
||||||
|
if (event instanceof HttpResponse) {
|
||||||
|
return true;
|
||||||
|
} else if (HttpEventType.DownloadProgress === event.type) {
|
||||||
|
if (!!progress) {
|
||||||
|
progress.next(Math.round((100 * event.loaded) / event.total));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}),
|
||||||
|
map((event: HttpResponse<any>) => {
|
||||||
|
if (!!progress) {
|
||||||
|
fileDownloadItem.downloadComplete();
|
||||||
|
}
|
||||||
|
return event.body;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
43
documents/업무/2월/3째주/file-viewer-prj/file-info.ts
Normal file
43
documents/업무/2월/3째주/file-viewer-prj/file-info.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import { FileType } from '../types/file.type';
|
||||||
|
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
|
||||||
|
|
||||||
|
export interface FileInfo {
|
||||||
|
// 대화방SEQ
|
||||||
|
roomSeq: string;
|
||||||
|
// 이벤트SEQ
|
||||||
|
eventSeq: number;
|
||||||
|
// 파일SEQ
|
||||||
|
seq: number;
|
||||||
|
// 송신자SEQ
|
||||||
|
senderSeq: number;
|
||||||
|
// 파일타입
|
||||||
|
type: FileType;
|
||||||
|
// 파일이름
|
||||||
|
name: string;
|
||||||
|
// 파일URL
|
||||||
|
url: string;
|
||||||
|
// 파일크기(byte)
|
||||||
|
size: number;
|
||||||
|
// 전송일시
|
||||||
|
sendDate: string;
|
||||||
|
// 수신완료자수
|
||||||
|
receivedUserCount: number;
|
||||||
|
// 수신자수
|
||||||
|
receiverCount: number;
|
||||||
|
// 발신내용
|
||||||
|
sentMessage: string;
|
||||||
|
// 발신내용 For Json
|
||||||
|
sentMessageJson?: FileEventJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMedia(fileInfo: FileInfo): boolean {
|
||||||
|
return (
|
||||||
|
[FileType.Image, FileType.Sound, FileType.Video, FileType.Bundle].some(
|
||||||
|
v => v === fileInfo.type
|
||||||
|
) || isSound(fileInfo)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSound(fileInfo: FileInfo): boolean {
|
||||||
|
return -1 !== ['mp3'].indexOf(fileInfo.sentMessageJson.fileExt);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<ng-container *ngIf="!!currentFileInfo">
|
||||||
|
<ucap-file-viewer
|
||||||
|
*ngIf="!isMediaType"
|
||||||
|
[fileInfo]="fileInfo"
|
||||||
|
[fileDownloadUrl]="fileDownloadUrl"
|
||||||
|
(download)="onDownload($event)"
|
||||||
|
(closed)="onClosedViewer()"
|
||||||
|
></ucap-file-viewer>
|
||||||
|
|
||||||
|
<ucap-media-viewer
|
||||||
|
*ngIf="isMediaType"
|
||||||
|
[fileInfo]="fileInfo"
|
||||||
|
[fileDownloadUrl]="fileDownloadUrl"
|
||||||
|
(download)="onDownload($event)"
|
||||||
|
(closed)="onClosedViewer()"
|
||||||
|
></ucap-media-viewer>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,111 @@
|
||||||
|
import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
|
||||||
|
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { DeviceType } from '@ucap-webmessenger/core';
|
||||||
|
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
|
import { CommonApiService } from '@ucap-webmessenger/api-common';
|
||||||
|
import { AppFileService } from '@app/services/file.service';
|
||||||
|
import { FileInfo, isMedia } from '@ucap-webmessenger/protocol-file';
|
||||||
|
import { SelectFileInfo } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
|
export interface FileViewerDialogData {
|
||||||
|
fileInfos: FileInfo[];
|
||||||
|
selectFileInfo: SelectFileInfo;
|
||||||
|
downloadUrl: string;
|
||||||
|
userSeq: number;
|
||||||
|
deviceType: DeviceType;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileViewerDialogResult {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-layout-common-file-viewer',
|
||||||
|
templateUrl: './file-viewer.dialog.component.html',
|
||||||
|
styleUrls: ['./file-viewer.dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class FileViewerDialogComponent implements OnInit, OnDestroy {
|
||||||
|
isMediaType: boolean;
|
||||||
|
fileInfo: {
|
||||||
|
fileInfos: FileInfo[];
|
||||||
|
selectFileInfo: SelectFileInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
currentFileInfo: FileInfo;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialogRef: MatDialogRef<
|
||||||
|
FileViewerDialogData,
|
||||||
|
FileViewerDialogResult
|
||||||
|
>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: FileViewerDialogData,
|
||||||
|
private commonApiService: CommonApiService,
|
||||||
|
private appFileService: AppFileService,
|
||||||
|
private logger: NGXLogger
|
||||||
|
) {
|
||||||
|
this.currentFileInfo = this.data.fileInfos.find(
|
||||||
|
f => f.seq === this.data.selectFileInfo.attachmentSeq
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this.currentFileInfo) {
|
||||||
|
this.logger.warn(
|
||||||
|
'file info is exist',
|
||||||
|
this.data.fileInfos,
|
||||||
|
this.data.selectFileInfo
|
||||||
|
);
|
||||||
|
this.dialogRef.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isMediaType = isMedia(this.currentFileInfo);
|
||||||
|
|
||||||
|
this.fileInfo = {
|
||||||
|
fileInfos: this.data.fileInfos.filter(f => {
|
||||||
|
const i = isMedia(f);
|
||||||
|
return this.isMediaType ? i : !i;
|
||||||
|
}),
|
||||||
|
selectFileInfo: this.data.selectFileInfo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
|
onDownload(info: {
|
||||||
|
attachmentSeq?: number;
|
||||||
|
downloadUrl?: string;
|
||||||
|
fileName: string;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
}): void {
|
||||||
|
this.appFileService.fileTalkDownlod({
|
||||||
|
req: {
|
||||||
|
userSeq: this.data.userSeq,
|
||||||
|
deviceType: this.data.deviceType,
|
||||||
|
token: this.data.token,
|
||||||
|
attachmentsSeq: info.attachmentSeq,
|
||||||
|
fileDownloadItem: info.fileDownloadItem
|
||||||
|
},
|
||||||
|
directDownloadUrl: info.downloadUrl,
|
||||||
|
fileName: info.fileName,
|
||||||
|
fileDownloadUrl: this.data.downloadUrl
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClosedViewer(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDownloadUrl = (attachmentSeq: number) => {
|
||||||
|
return this.commonApiService.urlForFileTalkDownload(
|
||||||
|
{
|
||||||
|
userSeq: this.data.userSeq,
|
||||||
|
deviceType: this.data.deviceType,
|
||||||
|
token: this.data.token,
|
||||||
|
attachmentsSeq: attachmentSeq
|
||||||
|
},
|
||||||
|
this.data.downloadUrl
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
73
documents/업무/2월/3째주/file-viewer-prj/file.component.ts
Normal file
73
documents/업무/2월/3째주/file-viewer-prj/file.component.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
|
||||||
|
import { Info, FileEventJson } from '@ucap-webmessenger/protocol-event';
|
||||||
|
import { StatusCode, FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
|
import { FileType } from '@ucap-webmessenger/protocol-file';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { RoomInfo } from '@ucap-webmessenger/protocol-room';
|
||||||
|
import { SelectFileInfo } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-message-box-file',
|
||||||
|
templateUrl: './file.component.html',
|
||||||
|
styleUrls: ['./file.component.scss']
|
||||||
|
})
|
||||||
|
export class FileComponent implements OnInit {
|
||||||
|
@Input()
|
||||||
|
message: Info<FileEventJson>;
|
||||||
|
@Input()
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
save = new EventEmitter<{
|
||||||
|
fileInfo: FileEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
type: string;
|
||||||
|
}>();
|
||||||
|
@Output()
|
||||||
|
fileViewer = new EventEmitter<SelectFileInfo>();
|
||||||
|
|
||||||
|
fileInfo?: FileEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
errorMessage?: string;
|
||||||
|
FileType = FileType;
|
||||||
|
|
||||||
|
constructor(private logger: NGXLogger) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (StatusCode.Success === this.message.sentMessageJson.statusCode) {
|
||||||
|
this.fileInfo = this.message.sentMessageJson;
|
||||||
|
} else {
|
||||||
|
this.errorMessage =
|
||||||
|
this.message.sentMessageJson.errorMessage || '[Error] System Error!!';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileDownloadItem = new FileDownloadItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
getExpiredFile() {
|
||||||
|
if (
|
||||||
|
!!this.roomInfo &&
|
||||||
|
this.roomInfo.expiredFileStdSeq <= this.message.seq
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickFileViewer(fileInfo: FileEventJson) {
|
||||||
|
if (!this.getExpiredFile()) {
|
||||||
|
this.fileViewer.emit({ attachmentSeq: this.fileInfo.attachmentSeq });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onSave(value: string) {
|
||||||
|
if (!this.getExpiredFile()) {
|
||||||
|
this.save.emit({
|
||||||
|
fileInfo: this.fileInfo,
|
||||||
|
fileDownloadItem: this.fileDownloadItem,
|
||||||
|
type: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
documents/업무/2월/3째주/file-viewer-prj/file.event-json.ts
Normal file
52
documents/업무/2월/3째주/file-viewer-prj/file.event-json.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api';
|
||||||
|
import { FileType } from '@ucap-webmessenger/protocol-file';
|
||||||
|
import { EventJsonDecoder } from './event-json';
|
||||||
|
|
||||||
|
export interface FileEventJson {
|
||||||
|
statusCode?: StatusCode;
|
||||||
|
errorMessage?: string;
|
||||||
|
roomSeq?: number;
|
||||||
|
fileName?: string;
|
||||||
|
fileExt?: string;
|
||||||
|
fileType?: FileType;
|
||||||
|
thumbUrl?: string;
|
||||||
|
attachmentSeq?: number;
|
||||||
|
attachmentSize?: number;
|
||||||
|
attachmentRegDate?: string;
|
||||||
|
imageWidth?: number;
|
||||||
|
imageHeight?: number;
|
||||||
|
companyCode?: string;
|
||||||
|
voiceTime?: string;
|
||||||
|
synappKey?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeFileEventJson: EventJsonDecoder<FileEventJson> = (
|
||||||
|
message: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const json = JsonAnalization.receiveAnalization(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: json.StatusCode,
|
||||||
|
errorMessage: json.ErrorMessage,
|
||||||
|
roomSeq: Number(json.RoomID),
|
||||||
|
fileName: json.FileName,
|
||||||
|
fileExt: json.FileExt,
|
||||||
|
fileType: json.FileType,
|
||||||
|
thumbUrl: json.ThumbURL,
|
||||||
|
attachmentSeq: Number(json.AttSEQ),
|
||||||
|
attachmentSize: Number(json.AttSize),
|
||||||
|
attachmentRegDate: json.AttRegDate,
|
||||||
|
imageWidth: Number(json.ImageWidth),
|
||||||
|
imageHeight: Number(json.ImageHeight),
|
||||||
|
companyCode: json.CompanyCode,
|
||||||
|
voiceTime: json.VoiceTime,
|
||||||
|
synappKey: json.SynappKey
|
||||||
|
} as FileEventJson;
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
statusCode: StatusCode.Fail,
|
||||||
|
errorMessage: e.toString()
|
||||||
|
} as FileEventJson;
|
||||||
|
}
|
||||||
|
};
|
167
documents/업무/2월/3째주/file-viewer-prj/file.service.ts
Normal file
167
documents/업무/2월/3째주/file-viewer-prj/file.service.ts
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import { Injectable, Inject, NgZone } from '@angular/core';
|
||||||
|
import {
|
||||||
|
FileTalkDownloadRequest,
|
||||||
|
CommonApiService
|
||||||
|
} from '@ucap-webmessenger/api-common';
|
||||||
|
import { map, take, finalize, catchError } from 'rxjs/operators';
|
||||||
|
import { MimeUtil, FileUtil } from '@ucap-webmessenger/core';
|
||||||
|
import { FileProtocolService } from '@ucap-webmessenger/protocol-file';
|
||||||
|
import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native';
|
||||||
|
import {
|
||||||
|
SnackBarService,
|
||||||
|
AlertSnackbarComponent,
|
||||||
|
AlertSnackbarData
|
||||||
|
} from '@ucap-webmessenger/ui';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AppFileService {
|
||||||
|
constructor(
|
||||||
|
private commonApiService: CommonApiService,
|
||||||
|
private fileProtocolService: FileProtocolService,
|
||||||
|
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
|
||||||
|
private snackBarService: SnackBarService,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private httpClient: HttpClient,
|
||||||
|
private ngZone: NgZone,
|
||||||
|
private logger: NGXLogger
|
||||||
|
) {}
|
||||||
|
|
||||||
|
fileTalkDownlod(param: {
|
||||||
|
req?: FileTalkDownloadRequest;
|
||||||
|
directDownloadUrl?: string;
|
||||||
|
fileName: string;
|
||||||
|
fileDownloadUrl?: string;
|
||||||
|
savePath?: string;
|
||||||
|
}) {
|
||||||
|
const req = param.req;
|
||||||
|
const directDownloadUrl = param.directDownloadUrl;
|
||||||
|
const fileName = param.fileName;
|
||||||
|
const fileDownloadItem = req.fileDownloadItem;
|
||||||
|
const fileDownloadUrl = param.fileDownloadUrl;
|
||||||
|
const savePath = param.savePath;
|
||||||
|
|
||||||
|
if (!!req && !!req.attachmentsSeq) {
|
||||||
|
this.commonApiService
|
||||||
|
.fileTalkDownload(req, fileDownloadUrl)
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
map(rawBlob => {
|
||||||
|
const mimeType = MimeUtil.getMimeFromExtension(
|
||||||
|
FileUtil.getExtension(fileName)
|
||||||
|
);
|
||||||
|
const blob = rawBlob.slice(0, rawBlob.size, mimeType);
|
||||||
|
FileUtil.fromBlobToBuffer(blob)
|
||||||
|
.then(buffer => {
|
||||||
|
/** download check */
|
||||||
|
this.fileProtocolService
|
||||||
|
.downCheck({
|
||||||
|
seq: req.attachmentsSeq
|
||||||
|
})
|
||||||
|
.pipe(take(1))
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
this.saveFile(buffer, fileName, mimeType, savePath);
|
||||||
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
this.fileTalkDownloadError(reason);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
finalize(() => {
|
||||||
|
if (!!fileDownloadItem) {
|
||||||
|
setTimeout(() => {
|
||||||
|
fileDownloadItem.downloadingProgress$ = undefined;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError(error => of(error))
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
} else if (!!directDownloadUrl) {
|
||||||
|
this.commonApiService
|
||||||
|
.fileDownload(directDownloadUrl, fileDownloadItem)
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
map(rawBlob => {
|
||||||
|
const mimeType = MimeUtil.getMimeFromExtension(
|
||||||
|
FileUtil.getExtension(fileName)
|
||||||
|
);
|
||||||
|
const blob = rawBlob.slice(0, rawBlob.size, mimeType);
|
||||||
|
FileUtil.fromBlobToBuffer(blob)
|
||||||
|
.then(buffer => {
|
||||||
|
this.saveFile(buffer, fileName, mimeType, savePath);
|
||||||
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
this.fileTalkDownloadError(reason);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
finalize(() => {
|
||||||
|
if (!!fileDownloadItem) {
|
||||||
|
setTimeout(() => {
|
||||||
|
fileDownloadItem.downloadingProgress$ = undefined;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError(error => of(error))
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private saveFile(
|
||||||
|
buffer: Buffer,
|
||||||
|
fileName: string,
|
||||||
|
mimeType: string,
|
||||||
|
savePath: string
|
||||||
|
): void {
|
||||||
|
this.nativeService
|
||||||
|
.saveFile(buffer, fileName, mimeType, savePath)
|
||||||
|
.then(filePath => {
|
||||||
|
if (!!filePath) {
|
||||||
|
const snackBarRef = this.snackBarService.open(
|
||||||
|
this.translateService.instant('common.file.results.savedToPath', {
|
||||||
|
path: filePath
|
||||||
|
}),
|
||||||
|
this.translateService.instant('common.file.open'),
|
||||||
|
{
|
||||||
|
duration: 3000,
|
||||||
|
verticalPosition: 'bottom',
|
||||||
|
horizontalPosition: 'center'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
snackBarRef.onAction().subscribe(() => {
|
||||||
|
snackBarRef.dismiss();
|
||||||
|
this.ngZone.runOutsideAngular(() => {
|
||||||
|
this.nativeService.openTargetItem(filePath).catch(reason => {
|
||||||
|
this.logger.warn(reason);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.fileTalkDownloadError('fail');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
this.fileTalkDownloadError(reason);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private fileTalkDownloadError(reason: any): void {
|
||||||
|
this.logger.warn(reason);
|
||||||
|
this.snackBarService.openFromComponent<
|
||||||
|
AlertSnackbarComponent,
|
||||||
|
AlertSnackbarData
|
||||||
|
>(AlertSnackbarComponent, {
|
||||||
|
data: {
|
||||||
|
html: this.translateService.instant('common.file.errors.failToSave'),
|
||||||
|
buttonText: this.translateService.instant('common.file.errors.label')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
33
documents/업무/2월/3째주/file-viewer-prj/mass-text.event-json.ts
Normal file
33
documents/업무/2월/3째주/file-viewer-prj/mass-text.event-json.ts
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api';
|
||||||
|
import { EventJsonDecoder } from './event-json';
|
||||||
|
|
||||||
|
export interface MassTextEventJson {
|
||||||
|
statusCode?: StatusCode;
|
||||||
|
errorMessage?: string;
|
||||||
|
roomSeq?: number;
|
||||||
|
massSeq?: number;
|
||||||
|
regDate?: string;
|
||||||
|
content?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeMassTextEventJson: EventJsonDecoder<MassTextEventJson> = (
|
||||||
|
message: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const json = JsonAnalization.receiveAnalization(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: json.StatusCode,
|
||||||
|
errorMessage: json.ErrorMessage,
|
||||||
|
roomSeq: Number(json.RoomID),
|
||||||
|
massSeq: Number(json.EventMassSeq),
|
||||||
|
regDate: json.RegDate,
|
||||||
|
content: json.Content
|
||||||
|
} as MassTextEventJson;
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
statusCode: StatusCode.Fail,
|
||||||
|
errorMessage: e.toString()
|
||||||
|
} as MassTextEventJson;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api';
|
||||||
|
import { EventJsonDecoder } from './event-json';
|
||||||
|
|
||||||
|
export interface MassTranslationEventJson {
|
||||||
|
statusCode?: StatusCode;
|
||||||
|
errorMessage?: string;
|
||||||
|
translationSeq?: number;
|
||||||
|
destLocale?: string;
|
||||||
|
roomSeq?: number;
|
||||||
|
regDate?: string;
|
||||||
|
original?: string;
|
||||||
|
translation?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeMassTranslationEventJson: EventJsonDecoder<MassTranslationEventJson> = (
|
||||||
|
message: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const json = JsonAnalization.receiveAnalization(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: json.StatusCode,
|
||||||
|
errorMessage: json.ErrorMessage,
|
||||||
|
translationSeq: Number(json.EventTransSeq),
|
||||||
|
destLocale: json.DestLocale,
|
||||||
|
roomSeq: Number(json.RoomID),
|
||||||
|
regDate: json.RegDate,
|
||||||
|
original: json.Original,
|
||||||
|
translation: json.Translation
|
||||||
|
} as MassTranslationEventJson;
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
statusCode: StatusCode.Fail,
|
||||||
|
errorMessage: e.toString()
|
||||||
|
} as MassTranslationEventJson;
|
||||||
|
}
|
||||||
|
};
|
205
documents/업무/2월/3째주/file-viewer-prj/message-box.component.ts
Normal file
205
documents/업무/2월/3째주/file-viewer-prj/message-box.component.ts
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output,
|
||||||
|
AfterViewInit,
|
||||||
|
ElementRef,
|
||||||
|
ViewChild,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { shakeAnimation } from 'angular-animations';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Info,
|
||||||
|
EventType,
|
||||||
|
InfoResponse,
|
||||||
|
EventJson,
|
||||||
|
FileEventJson,
|
||||||
|
MassTranslationEventJson
|
||||||
|
} from '@ucap-webmessenger/protocol-event';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
|
import { RoomInfo } from '@ucap-webmessenger/protocol-room';
|
||||||
|
import { SelectFileInfo } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-message-box',
|
||||||
|
templateUrl: './message-box.component.html',
|
||||||
|
styleUrls: ['./message-box.component.scss'],
|
||||||
|
animations: [shakeAnimation()]
|
||||||
|
})
|
||||||
|
export class MessageBoxComponent implements OnInit, AfterViewInit {
|
||||||
|
@Input()
|
||||||
|
message: Info<EventJson>;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
mine = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
highlight = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
existReadToHere = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
dateChanged = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
senderName: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImageRoot: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
translationSimpleview = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
unreadCount: number;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openProfile = new EventEmitter<number>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
massDetail = new EventEmitter<number>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
massTranslationDetail = new EventEmitter<{
|
||||||
|
message: Info<MassTranslationEventJson>;
|
||||||
|
contentsType: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
fileViewer = new EventEmitter<SelectFileInfo>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
save = new EventEmitter<{
|
||||||
|
fileInfo: FileEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
type: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
contextMenu = new EventEmitter<{
|
||||||
|
event: MouseEvent;
|
||||||
|
message: Info<EventJson>;
|
||||||
|
type?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@ViewChild('mbContainer', { static: true })
|
||||||
|
mbContainer: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
|
@ViewChild('mbChatRow', { static: true })
|
||||||
|
mbChatRow: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
|
EventType = EventType;
|
||||||
|
|
||||||
|
moment = moment;
|
||||||
|
|
||||||
|
firstEventSeq = 0;
|
||||||
|
existReadHere = false;
|
||||||
|
shakeIt = false;
|
||||||
|
|
||||||
|
get offsetTop() {
|
||||||
|
return this.mbChatRow.nativeElement.offsetTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private elementRef: ElementRef<HTMLElement>,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private logger: NGXLogger
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.mbContainer.nativeElement.classList.add('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
this.elementRef.nativeElement.style.height = `${this.mbContainer.nativeElement.offsetHeight}px`;
|
||||||
|
this.elementRef.nativeElement.style.maxHeight = `${this.mbContainer.nativeElement.offsetHeight}px`;
|
||||||
|
this.mbContainer.nativeElement.classList.remove('hide');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 정보성 Event 인지 판단.
|
||||||
|
* @description 정보성 event 일 경우 프로필, 일시 를 표현하지 않는다.
|
||||||
|
* Edit with reducers.ts / sync / updateRoomForNewEventMessage
|
||||||
|
*/
|
||||||
|
isInformation(info: Info<EventJson>) {
|
||||||
|
if (
|
||||||
|
info.type === EventType.Join ||
|
||||||
|
info.type === EventType.Exit ||
|
||||||
|
info.type === EventType.ForcedExit ||
|
||||||
|
info.type === EventType.RenameRoom ||
|
||||||
|
info.type === EventType.NotificationForiOSCapture ||
|
||||||
|
info.type === EventType.NotificationForTimerRoom ||
|
||||||
|
info.type === EventType.GuideForRoomTimerChanged
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickOpenProfile(event: MouseEvent, userSeq: number) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
this.openProfile.emit(userSeq);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** [Event] MassTalk Detail View */
|
||||||
|
onMassDetail(value: number) {
|
||||||
|
this.massDetail.emit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// onMassTranslationDetail(params: {
|
||||||
|
// message: Info<MassTranslationEventJson>;
|
||||||
|
// contentsType: string;
|
||||||
|
// }) {
|
||||||
|
// this.massTranslationDetail.emit(params);
|
||||||
|
// }
|
||||||
|
|
||||||
|
/** [Event] Image Viewer */
|
||||||
|
onFileViewer(fileInfo: SelectFileInfo) {
|
||||||
|
this.fileViewer.emit(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** [Event] Attach File Save & Save As */
|
||||||
|
onSave(value: {
|
||||||
|
fileInfo: FileEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
type: string;
|
||||||
|
}) {
|
||||||
|
this.save.emit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** [Event] Context Menu */
|
||||||
|
onContextMenu(event: any, message: Info<EventJson>) {
|
||||||
|
if (
|
||||||
|
message.type === EventType.Translation ||
|
||||||
|
message.type === EventType.MassTranslation
|
||||||
|
) {
|
||||||
|
this.contextMenu.emit({ event: event.event, message, type: event.type });
|
||||||
|
} else {
|
||||||
|
this.contextMenu.emit({ event, message });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shake() {
|
||||||
|
this.shakeIt = false;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.shakeIt = true;
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}, 1);
|
||||||
|
}
|
||||||
|
}
|
2158
documents/업무/2월/3째주/file-viewer-prj/messages.component copy.ts
Normal file
2158
documents/업무/2월/3째주/file-viewer-prj/messages.component copy.ts
Normal file
File diff suppressed because it is too large
Load Diff
699
documents/업무/2월/3째주/file-viewer-prj/messages.component.ts
Normal file
699
documents/업무/2월/3째주/file-viewer-prj/messages.component.ts
Normal file
|
@ -0,0 +1,699 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output,
|
||||||
|
ViewChild,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ElementRef,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
ViewChildren,
|
||||||
|
QueryList
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Info,
|
||||||
|
EventType,
|
||||||
|
InfoResponse,
|
||||||
|
EventJson,
|
||||||
|
FileEventJson,
|
||||||
|
MassTranslationEventJson
|
||||||
|
} from '@ucap-webmessenger/protocol-event';
|
||||||
|
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
||||||
|
import { UserInfo, RoomInfo, RoomType } from '@ucap-webmessenger/protocol-room';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
||||||
|
import moment from 'moment';
|
||||||
|
import { FileDownloadItem } from '@ucap-webmessenger/api';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Observable, Subscription, timer } from 'rxjs';
|
||||||
|
import { VirtualScrollerComponent, IPageInfo } from 'ngx-virtual-scroller';
|
||||||
|
import { MessageBoxComponent } from './message-box.component';
|
||||||
|
import { debounce } from 'rxjs/operators';
|
||||||
|
import { SelectFileInfo } from '@ucap-webmessenger/ui';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-messages',
|
||||||
|
templateUrl: './messages.component.html',
|
||||||
|
styleUrls: ['./messages.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class MessagesComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
loginRes$: Observable<LoginResponse>;
|
||||||
|
@Input()
|
||||||
|
roomInfo$: Observable<RoomInfo>;
|
||||||
|
@Input()
|
||||||
|
eventList$: Observable<Info<EventJson>[]>;
|
||||||
|
@Input()
|
||||||
|
newEventList$: Observable<Info<EventJson>[]>;
|
||||||
|
@Input()
|
||||||
|
searchedList$: Observable<Info<EventJson>[]>;
|
||||||
|
@Input()
|
||||||
|
eventInfoStatus$: Observable<InfoResponse>;
|
||||||
|
@Input()
|
||||||
|
eventRemained$: Observable<boolean>;
|
||||||
|
@Input()
|
||||||
|
sessionVerInfo: VersionInfo2Response;
|
||||||
|
@Input()
|
||||||
|
userInfos$: Observable<UserInfo[]>;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
lock$: Observable<boolean>;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
isShowUnreadCount = true;
|
||||||
|
@Input()
|
||||||
|
clearReadHere: boolean;
|
||||||
|
@Input()
|
||||||
|
minShowReadHere = 10;
|
||||||
|
@Input()
|
||||||
|
translationSimpleview = false;
|
||||||
|
@Input()
|
||||||
|
searchingMode = false;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openProfile = new EventEmitter<number>();
|
||||||
|
@Output()
|
||||||
|
moreEvent = new EventEmitter<number>();
|
||||||
|
@Output()
|
||||||
|
massDetail = new EventEmitter<number>();
|
||||||
|
@Output()
|
||||||
|
massTranslationDetail = new EventEmitter<{
|
||||||
|
message: Info<MassTranslationEventJson>;
|
||||||
|
contentsType: string;
|
||||||
|
}>();
|
||||||
|
@Output()
|
||||||
|
fileViewer = new EventEmitter<SelectFileInfo>();
|
||||||
|
@Output()
|
||||||
|
save = new EventEmitter<{
|
||||||
|
fileInfo: FileEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
type: string;
|
||||||
|
}>();
|
||||||
|
@Output()
|
||||||
|
contextMenu = new EventEmitter<{
|
||||||
|
event: MouseEvent;
|
||||||
|
message: Info<EventJson>;
|
||||||
|
type?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
scrollUp = new EventEmitter<any>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
yReachEnd = new EventEmitter<any>();
|
||||||
|
@Output()
|
||||||
|
yReachStart = new EventEmitter<any>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
existNewMessage = new EventEmitter<Info<EventJson>>();
|
||||||
|
|
||||||
|
@ViewChild('chatMessagesContainer', { static: false })
|
||||||
|
chatMessagesContainer: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
|
@ViewChild('chatMessagesBuffer', { static: false })
|
||||||
|
chatMessagesBuffer: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
|
@ViewChild('chatMessagesBufferContainer', { static: false })
|
||||||
|
chatMessagesBufferContainer: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
|
@ViewChild(VirtualScrollerComponent, { static: false })
|
||||||
|
private virtualScroller: VirtualScrollerComponent;
|
||||||
|
|
||||||
|
@ViewChildren('chatMessageBox')
|
||||||
|
chatMessageBoxList: QueryList<MessageBoxComponent>;
|
||||||
|
|
||||||
|
storedScrollItem: Info<EventJson>; // 이전대화를 불러올 경우 현재 스크롤 포지션 유지를 위한 값. 0 이면 초기로딩.
|
||||||
|
storedScrollItemOffsetTop: number | undefined;
|
||||||
|
scrollUpInitalized = false; // ps 에서 초기 로딩시 scroll reach start 이벤트 발생 버그를 우회하기 위한 init 값으로 scrollUp 에 의해 true 로 된다.
|
||||||
|
firstCheckReadHere = true;
|
||||||
|
initRoomLastEventSeq: number;
|
||||||
|
baseEventSeq = 0;
|
||||||
|
gotoEventSeq: number;
|
||||||
|
|
||||||
|
loginRes: LoginResponse;
|
||||||
|
loginResSubscription: Subscription;
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
roomInfoSubscription: Subscription;
|
||||||
|
eventList: Info<EventJson>[];
|
||||||
|
eventListSubscription: Subscription;
|
||||||
|
newEventList: Info<EventJson>[];
|
||||||
|
newEventListSubscription: Subscription;
|
||||||
|
searchedList: Info<EventJson>[];
|
||||||
|
searchedListSubscription: Subscription;
|
||||||
|
eventInfoStatus: InfoResponse;
|
||||||
|
eventInfoStatusSubscription: Subscription;
|
||||||
|
eventRemained: boolean;
|
||||||
|
eventRemainedSubscription: Subscription;
|
||||||
|
userInfos: UserInfo[];
|
||||||
|
userInfosSubscription: Subscription;
|
||||||
|
|
||||||
|
EventType = EventType;
|
||||||
|
profileImageRoot: string;
|
||||||
|
moment = moment;
|
||||||
|
|
||||||
|
readToHereEvent: Info<EventJson>;
|
||||||
|
existReadToHereEvent = true;
|
||||||
|
hidden = false;
|
||||||
|
swapped = false;
|
||||||
|
initalized = false;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private logger: NGXLogger,
|
||||||
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
|
private translateService: TranslateService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.profileImageRoot =
|
||||||
|
this.profileImageRoot || this.sessionVerInfo.profileRoot;
|
||||||
|
|
||||||
|
this.loginResSubscription = this.loginRes$.subscribe(loginRes => {
|
||||||
|
this.loginRes = loginRes;
|
||||||
|
});
|
||||||
|
this.roomInfoSubscription = this.roomInfo$.subscribe(roomInfo => {
|
||||||
|
this.roomInfo = roomInfo;
|
||||||
|
this.initalized = false;
|
||||||
|
|
||||||
|
/** [S] initializing by changed room */
|
||||||
|
// reset :: roomLastEventSeq
|
||||||
|
if (!!roomInfo && !!roomInfo.finalEventSeq) {
|
||||||
|
this.initRoomLastEventSeq = roomInfo.finalEventSeq;
|
||||||
|
}
|
||||||
|
// clear :: readToHearEvent object
|
||||||
|
this.readToHereEvent = undefined;
|
||||||
|
this.existReadToHereEvent = true;
|
||||||
|
/** [E] initializing by changed room */
|
||||||
|
|
||||||
|
if (!this.roomInfo || this.roomInfo.roomSeq !== roomInfo.roomSeq) {
|
||||||
|
this.initEventMore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.eventListSubscription = this.eventList$
|
||||||
|
.pipe(debounce(() => timer(100)))
|
||||||
|
.subscribe(eventList => {
|
||||||
|
this.eventList = eventList;
|
||||||
|
|
||||||
|
if (!!eventList && eventList.length > 0) {
|
||||||
|
if (!this.readToHereEvent && this.existReadToHereEvent) {
|
||||||
|
this.readToHereEvent = this.getReadHere();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.baseEventSeq > 0 &&
|
||||||
|
!!this.roomInfo &&
|
||||||
|
!!this.roomInfo.lastReadEventSeq &&
|
||||||
|
this.baseEventSeq <= this.roomInfo.lastReadEventSeq
|
||||||
|
) {
|
||||||
|
// 기존 대화 내용이 있는 상태에서 추가로 조회된 내용중에 read here 가 있을 경우.
|
||||||
|
this.firstCheckReadHere = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.readToHereEvent = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
|
||||||
|
if (this.searchingMode) {
|
||||||
|
const baseseq = this.baseEventSeq;
|
||||||
|
// setTimeout(() => {
|
||||||
|
// this.doSearchTextInEvent(this.searchText, baseseq);
|
||||||
|
// }, 800);
|
||||||
|
this.baseEventSeq = eventList[0].seq;
|
||||||
|
} else {
|
||||||
|
if (!!eventList && eventList.length > 0) {
|
||||||
|
this.baseEventSeq = eventList[0].seq;
|
||||||
|
this.ready();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.newEventListSubscription = this.newEventList$.subscribe(
|
||||||
|
newEventList => {
|
||||||
|
this.newEventList = newEventList;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.searchedListSubscription = this.searchedList$.subscribe(
|
||||||
|
searchedList => {
|
||||||
|
this.searchedList = searchedList;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eventInfoStatusSubscription = this.eventInfoStatus$.subscribe(
|
||||||
|
eventInfoStatus => {
|
||||||
|
this.eventInfoStatus = eventInfoStatus;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.eventRemainedSubscription = this.eventRemained$.subscribe(
|
||||||
|
eventRemained => {
|
||||||
|
this.eventRemained = eventRemained;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.userInfosSubscription = this.userInfos$.subscribe(userInfos => {
|
||||||
|
this.userInfos = userInfos;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.loginResSubscription) {
|
||||||
|
this.loginResSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (!!this.roomInfoSubscription) {
|
||||||
|
this.roomInfoSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (!!this.eventListSubscription) {
|
||||||
|
this.eventListSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (!!this.newEventListSubscription) {
|
||||||
|
this.newEventListSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (!!this.searchedListSubscription) {
|
||||||
|
this.searchedListSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (!!this.eventInfoStatusSubscription) {
|
||||||
|
this.eventInfoStatusSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (!!this.eventRemainedSubscription) {
|
||||||
|
this.eventRemainedSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
if (!!this.userInfosSubscription) {
|
||||||
|
this.userInfosSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserInfo getter
|
||||||
|
*/
|
||||||
|
getUserName(seq: number): string {
|
||||||
|
if (!this.userInfos) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo: UserInfo[] = this.userInfos.filter(
|
||||||
|
user => user.seq === seq
|
||||||
|
);
|
||||||
|
if (!!userInfo && userInfo.length > 0) {
|
||||||
|
return userInfo[0].name;
|
||||||
|
}
|
||||||
|
return '(알수없는 사용자)';
|
||||||
|
}
|
||||||
|
getUserProfile(seq: number): string {
|
||||||
|
if (!this.userInfos) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const userInfo: UserInfo[] = this.userInfos.filter(
|
||||||
|
user => user.seq === seq
|
||||||
|
);
|
||||||
|
if (!!userInfo && userInfo.length > 0) {
|
||||||
|
return userInfo[0].profileImageFile;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
isHighlightedEvent(seq: number): boolean {
|
||||||
|
return (
|
||||||
|
!!this.searchedList &&
|
||||||
|
this.searchedList.filter(event => event.seq === seq).length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnreadCount(message: Info<EventJson>): string | number {
|
||||||
|
// if (!this.userInfos || 0 === this.userInfos.length) {
|
||||||
|
// return '';
|
||||||
|
// }
|
||||||
|
const unreadCnt = this.userInfos
|
||||||
|
.filter(user => user.isJoinRoom && user.seq !== message.senderSeq)
|
||||||
|
.filter(user => user.lastReadEventSeq < message.seq).length;
|
||||||
|
return unreadCnt === 0 ? '' : unreadCnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 정보성 Event 인지 판단.
|
||||||
|
* @description 정보성 event 일 경우 프로필, 일시 를 표현하지 않는다.
|
||||||
|
* Edit with reducers.ts / sync / updateRoomForNewEventMessage
|
||||||
|
*/
|
||||||
|
getIsInformation(info: Info<EventJson>) {
|
||||||
|
if (
|
||||||
|
info.type === EventType.Join ||
|
||||||
|
info.type === EventType.Exit ||
|
||||||
|
info.type === EventType.ForcedExit ||
|
||||||
|
info.type === EventType.RenameRoom ||
|
||||||
|
info.type === EventType.NotificationForTimerRoom ||
|
||||||
|
info.type === EventType.GuideForRoomTimerChanged
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Date Splitter show check */
|
||||||
|
getDateSplitter(message: Info<EventJson>): boolean {
|
||||||
|
const curIndex = this.eventList.findIndex(v => v.seq === message.seq);
|
||||||
|
|
||||||
|
if (curIndex === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (curIndex > 0) {
|
||||||
|
if (!this.eventList[curIndex]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !moment(this.eventList[curIndex].sendDate).isSame(
|
||||||
|
moment(this.eventList[curIndex - 1].sendDate),
|
||||||
|
'day'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getReadHere(): Info<EventJson> | undefined {
|
||||||
|
if (
|
||||||
|
!!this.roomInfo &&
|
||||||
|
!!this.roomInfo.lastReadEventSeq &&
|
||||||
|
this.initRoomLastEventSeq - this.roomInfo.lastReadEventSeq >
|
||||||
|
this.minShowReadHere
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
this.roomInfo.roomType === RoomType.Single ||
|
||||||
|
this.roomInfo.roomType === RoomType.Multi
|
||||||
|
) {
|
||||||
|
if (!this.roomInfo.isTimeRoom) {
|
||||||
|
return this.eventList.find(
|
||||||
|
v => v.seq === this.roomInfo.lastReadEventSeq + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.existReadToHereEvent = false;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStringReadHereMore(): string {
|
||||||
|
let rtnStr = '';
|
||||||
|
rtnStr = this.translateService.instant('chat.event.moreUnreadEventsWith', {
|
||||||
|
countOfUnread: this.baseEventSeq - (this.roomInfo.lastReadEventSeq + 1)
|
||||||
|
});
|
||||||
|
return rtnStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
storeScrollPosition() {
|
||||||
|
this.storedScrollItem = this.eventList[
|
||||||
|
this.virtualScroller.viewPortInfo.startIndex
|
||||||
|
];
|
||||||
|
|
||||||
|
const chatMessageBox = this.chatMessageBoxList.find(
|
||||||
|
el =>
|
||||||
|
el.message.seq ===
|
||||||
|
this.eventList[this.virtualScroller.viewPortInfo.startIndex].seq
|
||||||
|
);
|
||||||
|
if (!!chatMessageBox) {
|
||||||
|
this.storedScrollItemOffsetTop =
|
||||||
|
chatMessageBox.offsetTop -
|
||||||
|
this.virtualScroller.viewPortInfo.scrollStartPosition;
|
||||||
|
} else {
|
||||||
|
this.storedScrollItemOffsetTop = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
swapScrollTo(
|
||||||
|
to: Info<EventJson>,
|
||||||
|
preCallback: () => void,
|
||||||
|
postCallback: () => void,
|
||||||
|
useHide: boolean,
|
||||||
|
useSwap: boolean,
|
||||||
|
addtionalOffset?: number
|
||||||
|
) {
|
||||||
|
this.preSwapScroll(useHide, useSwap);
|
||||||
|
if (!!preCallback) {
|
||||||
|
preCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.virtualScroller.scrollInto(
|
||||||
|
to,
|
||||||
|
true,
|
||||||
|
undefined !== this.storedScrollItemOffsetTop
|
||||||
|
? -this.storedScrollItemOffsetTop
|
||||||
|
: undefined !== addtionalOffset
|
||||||
|
? -addtionalOffset
|
||||||
|
: 0,
|
||||||
|
0,
|
||||||
|
() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!!postCallback) {
|
||||||
|
postCallback();
|
||||||
|
}
|
||||||
|
this.postSwapScroll(useHide, useSwap);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
preSwapScroll(useHide: boolean, useSwap: boolean) {
|
||||||
|
if (useSwap && !this.swapped) {
|
||||||
|
this.chatMessagesBuffer.nativeElement.innerHTML = this.chatMessagesContainer.nativeElement.innerHTML;
|
||||||
|
this.chatMessagesBuffer.nativeElement.scrollTop = this.chatMessagesContainer.nativeElement.scrollTop;
|
||||||
|
this.chatMessagesBufferContainer.nativeElement.classList.remove(
|
||||||
|
'disappear'
|
||||||
|
);
|
||||||
|
this.swapped = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useHide && !this.hidden) {
|
||||||
|
this.chatMessagesContainer.nativeElement.classList.add('hide');
|
||||||
|
this.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
postSwapScroll(useHide: boolean, useSwap: boolean) {
|
||||||
|
if (useSwap && this.swapped) {
|
||||||
|
this.chatMessagesBuffer.nativeElement.innerHTML = '';
|
||||||
|
this.chatMessagesBuffer.nativeElement.scrollTop = 0;
|
||||||
|
this.chatMessagesBufferContainer.nativeElement.classList.add('disappear');
|
||||||
|
this.swapped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useHide && this.hidden) {
|
||||||
|
this.chatMessagesContainer.nativeElement.classList.remove('hide');
|
||||||
|
this.hidden = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ready(): void {
|
||||||
|
this.scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollToBottom(): void {
|
||||||
|
if (!!this.storedScrollItem) {
|
||||||
|
if (!!this.readToHereEvent && this.firstCheckReadHere) {
|
||||||
|
this.swapScrollTo(
|
||||||
|
this.readToHereEvent,
|
||||||
|
() => {},
|
||||||
|
() => {
|
||||||
|
this.firstCheckReadHere = false;
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.swapScrollTo(
|
||||||
|
this.storedScrollItem,
|
||||||
|
() => {},
|
||||||
|
() => {
|
||||||
|
this.storedScrollItem = undefined;
|
||||||
|
this.storedScrollItemOffsetTop = undefined;
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (this.scrollUpInitalized) {
|
||||||
|
if (!!this.newEventList && this.newEventList.length > 0) {
|
||||||
|
this.existNewMessage.emit(
|
||||||
|
this.newEventList[this.newEventList.length - 1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!!this.readToHereEvent && this.firstCheckReadHere) {
|
||||||
|
this.swapScrollTo(
|
||||||
|
this.readToHereEvent,
|
||||||
|
() => {},
|
||||||
|
() => {
|
||||||
|
this.firstCheckReadHere = false;
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if (!this.eventList || 0 === this.eventList.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!this.gotoEventSeq) {
|
||||||
|
this.gotoPosition(this.gotoEventSeq);
|
||||||
|
this.gotoEventSeq = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastEvent =
|
||||||
|
!!this.eventList && 0 < this.eventList.length
|
||||||
|
? this.eventList[this.eventList.length - 1]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
if (undefined === lastEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isInViewPortItems = this.isInViewPortItems(lastEvent.seq);
|
||||||
|
this.swapScrollTo(
|
||||||
|
this.eventList[this.eventList.length - 1],
|
||||||
|
() => {},
|
||||||
|
() => {
|
||||||
|
this.initalized = true;
|
||||||
|
},
|
||||||
|
-1 === this.virtualScroller.viewPortInfo.endIndex ||
|
||||||
|
!isInViewPortItems,
|
||||||
|
!isInViewPortItems
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initEventMore(gotoEventSeq?: number) {
|
||||||
|
// 방정보가 바뀌면 이전대화 보기 관련 값들을 초기화 한다.
|
||||||
|
this.scrollUpInitalized = false;
|
||||||
|
this.storedScrollItem = undefined;
|
||||||
|
this.storedScrollItemOffsetTop = undefined;
|
||||||
|
this.gotoEventSeq = gotoEventSeq;
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {}
|
||||||
|
|
||||||
|
gotoPosition(eventSeq: number) {
|
||||||
|
const isInViewPortItems = this.isInViewPortItems(eventSeq);
|
||||||
|
|
||||||
|
if (!!this.virtualScroller) {
|
||||||
|
const e = this.eventList.find(v => v.seq === eventSeq);
|
||||||
|
this.swapScrollTo(
|
||||||
|
e,
|
||||||
|
() => {},
|
||||||
|
() => {
|
||||||
|
const chatMessageBox = this.chatMessageBoxList.find(
|
||||||
|
el => el.message.seq === eventSeq
|
||||||
|
);
|
||||||
|
if (!!chatMessageBox) {
|
||||||
|
chatMessageBox.shake();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
!isInViewPortItems,
|
||||||
|
!isInViewPortItems,
|
||||||
|
50
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isInViewPortItems(eventSeq: number): boolean {
|
||||||
|
if (undefined === eventSeq) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const viewPortItemIndex = this.virtualScroller.viewPortItems.findIndex(
|
||||||
|
v => v.seq === eventSeq
|
||||||
|
);
|
||||||
|
|
||||||
|
const newEvent =
|
||||||
|
!!this.newEventList &&
|
||||||
|
-1 !== this.newEventList.findIndex(e => e.seq === eventSeq);
|
||||||
|
|
||||||
|
return -1 !== viewPortItemIndex || newEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickOpenProfile(userSeq: number) {
|
||||||
|
this.openProfile.emit(userSeq);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickMore(event: any) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (this.scrollUpInitalized && this.eventRemained) {
|
||||||
|
this.virtualScroller.scrollToPosition(0);
|
||||||
|
this.virtualScroller.invalidateCachedMeasurementAtIndex(0);
|
||||||
|
|
||||||
|
this.storeScrollPosition();
|
||||||
|
|
||||||
|
this.preSwapScroll(true, true);
|
||||||
|
|
||||||
|
this.moreEvent.emit(this.eventList[0].seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** [Event] MassTalk Detail View */
|
||||||
|
onMassDetail(value: number) {
|
||||||
|
this.massDetail.emit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMassTranslationDetail(params: {
|
||||||
|
message: Info<MassTranslationEventJson>;
|
||||||
|
contentsType: string;
|
||||||
|
}) {
|
||||||
|
this.massTranslationDetail.emit(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** [Event] Image Viewer */
|
||||||
|
onFileViewer(fileInfo: SelectFileInfo) {
|
||||||
|
this.fileViewer.emit(fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** [Event] Attach File Save & Save As */
|
||||||
|
onSave(value: {
|
||||||
|
fileInfo: FileEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
type: string;
|
||||||
|
}) {
|
||||||
|
this.save.emit(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** [Event] Context Menu */
|
||||||
|
onContextMenu(event: {
|
||||||
|
event: MouseEvent;
|
||||||
|
message: Info<EventJson>;
|
||||||
|
type?: string;
|
||||||
|
}) {
|
||||||
|
this.contextMenu.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onScrollup(event: any) {
|
||||||
|
if (!this.eventList || 0 === this.eventList.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.scrollUpInitalized = true;
|
||||||
|
this.scrollUp.emit(event);
|
||||||
|
}
|
||||||
|
onYReachStart(event: any) {
|
||||||
|
this.yReachStart.emit(event);
|
||||||
|
}
|
||||||
|
onYReachEnd(event: any) {
|
||||||
|
this.yReachEnd.emit(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
onVsChange(event: IPageInfo) {
|
||||||
|
if (
|
||||||
|
-1 === event.startIndex ||
|
||||||
|
-1 === event.endIndex ||
|
||||||
|
(0 === event.startIndex && 0 === event.endIndex)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// this.logger.debug('onVsChange', event);
|
||||||
|
}
|
||||||
|
|
||||||
|
trackByEvent(index: number, info: Info<EventJson>): number {
|
||||||
|
return info.seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
compareItemsFunc = (
|
||||||
|
item1: Info<EventJson>,
|
||||||
|
item2: Info<EventJson>
|
||||||
|
// tslint:disable-next-line: semicolon
|
||||||
|
): boolean => !!item1 && !!item2 && item1.seq === item2.seq;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface SelectFileInfo {
|
||||||
|
attachmentSeq: number;
|
||||||
|
index?: number;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
export interface SelectFileInfo {
|
||||||
|
attachmentSeq: number;
|
||||||
|
index?: number;
|
||||||
|
}
|
34
documents/업무/2월/3째주/file-viewer-prj/plan.event-json.ts
Normal file
34
documents/업무/2월/3째주/file-viewer-prj/plan.event-json.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import { EventJsonDecoder } from './event-json';
|
||||||
|
import { JsonAnalization } from '@ucap-webmessenger/api';
|
||||||
|
|
||||||
|
export interface PlanEventJson {
|
||||||
|
planSeq?: number;
|
||||||
|
title?: string;
|
||||||
|
contents?: PlanContentType | string;
|
||||||
|
date?: string;
|
||||||
|
endDate?: string;
|
||||||
|
active?: boolean;
|
||||||
|
}
|
||||||
|
export enum PlanContentType {
|
||||||
|
New = 'PLAN_CONTENTS_NEW',
|
||||||
|
Update = 'PLAN_CONTENTS_UPDATE',
|
||||||
|
Delete = 'PLAN_CONTENTS_DELETE'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodePlanEventJson: EventJsonDecoder<PlanEventJson> = (
|
||||||
|
message: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const json = JsonAnalization.receiveAnalization(message);
|
||||||
|
return {
|
||||||
|
planSeq: Number(json.planSeq),
|
||||||
|
title: json.title,
|
||||||
|
contents: json.contents,
|
||||||
|
date: json.date,
|
||||||
|
endDate: json.endDate,
|
||||||
|
active: !!json.activeYn && 'Y' === json.activeYn ? true : false
|
||||||
|
} as PlanEventJson;
|
||||||
|
} catch (e) {
|
||||||
|
return {} as PlanEventJson;
|
||||||
|
}
|
||||||
|
};
|
49
documents/업무/2월/3째주/file-viewer-prj/public-api copy.ts
Normal file
49
documents/업무/2월/3째주/file-viewer-prj/public-api copy.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Public API Surface of ucap-webmessenger-ui
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './lib/animations/index';
|
||||||
|
|
||||||
|
export * from './lib/components/file-viewer/document-viewer.component';
|
||||||
|
export * from './lib/components/file-viewer/image-viewer.component';
|
||||||
|
export * from './lib/components/file-viewer/sound-viewer.component';
|
||||||
|
export * from './lib/components/file-viewer/video-viewer.component';
|
||||||
|
|
||||||
|
export * from './lib/components/expansion-panel.component';
|
||||||
|
export * from './lib/components/file-upload-queue.component';
|
||||||
|
export * from './lib/components/file-viewer.component';
|
||||||
|
export * from './lib/components/float-action-button.component';
|
||||||
|
export * from './lib/components/pick-date.component';
|
||||||
|
export * from './lib/components/pick-time.component';
|
||||||
|
export * from './lib/components/step-input.component';
|
||||||
|
export * from './lib/components/split-button.component';
|
||||||
|
export * from './lib/components/sticker-selector.component';
|
||||||
|
export * from './lib/components/inline-edit-input.component';
|
||||||
|
|
||||||
|
export * from './lib/data-source/virtual-scroll-tree-flat.data-source';
|
||||||
|
|
||||||
|
export * from './lib/dialogs/alert.dialog.component';
|
||||||
|
export * from './lib/dialogs/confirm.dialog.component';
|
||||||
|
|
||||||
|
export * from './lib/directives/click-outside.directive';
|
||||||
|
export * from './lib/directives/file-upload-for.directive';
|
||||||
|
export * from './lib/directives/image.directive';
|
||||||
|
|
||||||
|
export * from './lib/services/bottom-sheet.service';
|
||||||
|
export * from './lib/services/clipboard.service';
|
||||||
|
export * from './lib/services/dialog.service';
|
||||||
|
export * from './lib/services/snack-bar.service';
|
||||||
|
export * from './lib/services/splash-screen.service';
|
||||||
|
export * from './lib/services/translate.service';
|
||||||
|
export * from './lib/services/date.service';
|
||||||
|
export * from './lib/services/paginator-intl.service';
|
||||||
|
|
||||||
|
export * from './lib/snackbars/alert.snackbar.component';
|
||||||
|
|
||||||
|
export * from './lib/types/file-viewer.type';
|
||||||
|
|
||||||
|
export * from './lib/models/select-file-info';
|
||||||
|
|
||||||
|
export * from './lib/utils/string.util';
|
||||||
|
|
||||||
|
export * from './lib/ucap-ui.module';
|
49
documents/업무/2월/3째주/file-viewer-prj/public-api.ts
Normal file
49
documents/업무/2월/3째주/file-viewer-prj/public-api.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Public API Surface of ucap-webmessenger-ui
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './lib/animations/index';
|
||||||
|
|
||||||
|
export * from './lib/components/file-viewer/document-viewer.component';
|
||||||
|
export * from './lib/components/file-viewer/image-viewer.component';
|
||||||
|
export * from './lib/components/file-viewer/sound-viewer.component';
|
||||||
|
export * from './lib/components/file-viewer/video-viewer.component';
|
||||||
|
|
||||||
|
export * from './lib/components/expansion-panel.component';
|
||||||
|
export * from './lib/components/file-upload-queue.component';
|
||||||
|
export * from './lib/components/file-viewer.component';
|
||||||
|
export * from './lib/components/float-action-button.component';
|
||||||
|
export * from './lib/components/pick-date.component';
|
||||||
|
export * from './lib/components/pick-time.component';
|
||||||
|
export * from './lib/components/step-input.component';
|
||||||
|
export * from './lib/components/split-button.component';
|
||||||
|
export * from './lib/components/sticker-selector.component';
|
||||||
|
export * from './lib/components/inline-edit-input.component';
|
||||||
|
|
||||||
|
export * from './lib/data-source/virtual-scroll-tree-flat.data-source';
|
||||||
|
|
||||||
|
export * from './lib/dialogs/alert.dialog.component';
|
||||||
|
export * from './lib/dialogs/confirm.dialog.component';
|
||||||
|
|
||||||
|
export * from './lib/directives/click-outside.directive';
|
||||||
|
export * from './lib/directives/file-upload-for.directive';
|
||||||
|
export * from './lib/directives/image.directive';
|
||||||
|
|
||||||
|
export * from './lib/services/bottom-sheet.service';
|
||||||
|
export * from './lib/services/clipboard.service';
|
||||||
|
export * from './lib/services/dialog.service';
|
||||||
|
export * from './lib/services/snack-bar.service';
|
||||||
|
export * from './lib/services/splash-screen.service';
|
||||||
|
export * from './lib/services/translate.service';
|
||||||
|
export * from './lib/services/date.service';
|
||||||
|
export * from './lib/services/paginator-intl.service';
|
||||||
|
|
||||||
|
export * from './lib/snackbars/alert.snackbar.component';
|
||||||
|
|
||||||
|
export * from './lib/types/file-viewer.type';
|
||||||
|
|
||||||
|
export * from './lib/models/select-file-info';
|
||||||
|
|
||||||
|
export * from './lib/utils/string.util';
|
||||||
|
|
||||||
|
export * from './lib/ucap-ui.module';
|
185
documents/업무/2월/3째주/file-viewer-prj/ucap-ui.module copy.ts
Normal file
185
documents/업무/2월/3째주/file-viewer-prj/ucap-ui.module copy.ts
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
import { MatSliderModule } from '@angular/material/slider';
|
||||||
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
import {
|
||||||
|
MatTabsModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule
|
||||||
|
} from '@angular/material';
|
||||||
|
|
||||||
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { FileUploadQueueComponent } from './components/file-upload-queue.component';
|
||||||
|
import { FloatActionButtonComponent } from './components/float-action-button.component';
|
||||||
|
import { FileViewerComponent } from './components/file-viewer.component';
|
||||||
|
import { MediaViewerComponent } from './components/media-viewer.component';
|
||||||
|
import { ExpansionPanelComponent } from './components/expansion-panel.component';
|
||||||
|
import { SplitButtonComponent } from './components/split-button.component';
|
||||||
|
import { PickDateComponent } from './components/pick-date.component';
|
||||||
|
import { PickTimeComponent } from './components/pick-time.component';
|
||||||
|
import { StepInputComponent } from './components/step-input.component';
|
||||||
|
import { StickerSelectorComponent } from './components/sticker-selector.component';
|
||||||
|
import { InlineEditInputComponent } from './components/inline-edit-input.component';
|
||||||
|
|
||||||
|
import { BinaryViewerComponent } from './components/file-viewer/binary-viewer.component';
|
||||||
|
import { DocumentViewerComponent } from './components/file-viewer/document-viewer.component';
|
||||||
|
import { ImageViewerComponent } from './components/file-viewer/image-viewer.component';
|
||||||
|
import { SoundViewerComponent } from './components/file-viewer/sound-viewer.component';
|
||||||
|
import { VideoViewerComponent } from './components/file-viewer/video-viewer.component';
|
||||||
|
|
||||||
|
import { BottomSheetService } from './services/bottom-sheet.service';
|
||||||
|
import { ClipboardService } from './services/clipboard.service';
|
||||||
|
import { DialogService } from './services/dialog.service';
|
||||||
|
import { SnackBarService } from './services/snack-bar.service';
|
||||||
|
import { SplashScreenService } from './services/splash-screen.service';
|
||||||
|
import { TranslateService } from './services/translate.service';
|
||||||
|
import { DateService } from './services/date.service';
|
||||||
|
import { PaginatorIntlService } from './services/paginator-intl.service';
|
||||||
|
|
||||||
|
import { ClickOutsideDirective } from './directives/click-outside.directive';
|
||||||
|
import { FileUploadForDirective } from './directives/file-upload-for.directive';
|
||||||
|
import { ImageDirective } from './directives/image.directive';
|
||||||
|
import { CdkVirtualScrollViewportPatchDirective } from './directives/cdk-virtual-scroll-viewport-patch.directive';
|
||||||
|
|
||||||
|
import { AlertDialogComponent } from './dialogs/alert.dialog.component';
|
||||||
|
import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component';
|
||||||
|
|
||||||
|
import { BytesPipe } from './pipes/bytes.pipe';
|
||||||
|
import { LinefeedToHtmlPipe, HtmlToLinefeedPipe } from './pipes/linefeed.pipe';
|
||||||
|
import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe';
|
||||||
|
import { LinkyPipe } from './pipes/linky.pipe';
|
||||||
|
import { TranslatePipe } from './pipes/translate.pipe';
|
||||||
|
import { DatePipe } from './pipes/date.pipe';
|
||||||
|
import { SafeHtmlPipe } from './pipes/safe-html.pipe';
|
||||||
|
|
||||||
|
import {
|
||||||
|
StringEmptyCheckPipe,
|
||||||
|
StringFormatterPhonePipe
|
||||||
|
} from './pipes/string.pipe';
|
||||||
|
import { ClickDebounceDirective } from './directives/click-debounce.directive';
|
||||||
|
import { TranslationSectionComponent } from './components/translation-section.component';
|
||||||
|
import { AlertSnackbarComponent } from './snackbars/alert.snackbar.component';
|
||||||
|
import { IntegratedSearchFormComponent } from './components/integrated-search-form.component';
|
||||||
|
import { IntegratedSearchComponent } from './components/integrated-search.component';
|
||||||
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
|
const COMPONENTS = [
|
||||||
|
FileUploadQueueComponent,
|
||||||
|
FloatActionButtonComponent,
|
||||||
|
FileViewerComponent,
|
||||||
|
ExpansionPanelComponent,
|
||||||
|
StickerSelectorComponent,
|
||||||
|
SplitButtonComponent,
|
||||||
|
PickDateComponent,
|
||||||
|
PickTimeComponent,
|
||||||
|
StepInputComponent,
|
||||||
|
TranslationSectionComponent,
|
||||||
|
InlineEditInputComponent,
|
||||||
|
IntegratedSearchComponent,
|
||||||
|
IntegratedSearchFormComponent,
|
||||||
|
|
||||||
|
BinaryViewerComponent,
|
||||||
|
DocumentViewerComponent,
|
||||||
|
ImageViewerComponent,
|
||||||
|
SoundViewerComponent,
|
||||||
|
VideoViewerComponent,
|
||||||
|
MediaViewerComponent
|
||||||
|
];
|
||||||
|
const DIALOGS = [
|
||||||
|
AlertDialogComponent,
|
||||||
|
ConfirmDialogComponent,
|
||||||
|
AlertSnackbarComponent
|
||||||
|
];
|
||||||
|
const DIRECTIVES = [
|
||||||
|
ClickOutsideDirective,
|
||||||
|
FileUploadForDirective,
|
||||||
|
ImageDirective,
|
||||||
|
CdkVirtualScrollViewportPatchDirective,
|
||||||
|
ClickDebounceDirective
|
||||||
|
];
|
||||||
|
const PIPES = [
|
||||||
|
BytesPipe,
|
||||||
|
LinefeedToHtmlPipe,
|
||||||
|
HtmlToLinefeedPipe,
|
||||||
|
SecondsToMinutesPipe,
|
||||||
|
LinkyPipe,
|
||||||
|
TranslatePipe,
|
||||||
|
DatePipe,
|
||||||
|
StringEmptyCheckPipe,
|
||||||
|
StringFormatterPhonePipe,
|
||||||
|
SafeHtmlPipe
|
||||||
|
];
|
||||||
|
const SERVICES = [
|
||||||
|
BottomSheetService,
|
||||||
|
ClipboardService,
|
||||||
|
DialogService,
|
||||||
|
SnackBarService,
|
||||||
|
SplashScreenService,
|
||||||
|
TranslateService,
|
||||||
|
DateService,
|
||||||
|
PaginatorIntlService
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatProgressBarModule,
|
||||||
|
MatSliderModule,
|
||||||
|
MatSnackBarModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
MatTabsModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonToggleModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatDatepickerModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
|
||||||
|
PerfectScrollbarModule,
|
||||||
|
DragDropModule,
|
||||||
|
TranslateModule
|
||||||
|
],
|
||||||
|
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
||||||
|
declarations: [...COMPONENTS, ...DIALOGS, ...DIRECTIVES, ...PIPES],
|
||||||
|
entryComponents: [...DIALOGS]
|
||||||
|
})
|
||||||
|
export class UCapUiModule {
|
||||||
|
public static forRoot(): ModuleWithProviders<UCapUiModule> {
|
||||||
|
return {
|
||||||
|
ngModule: UCapUiModule,
|
||||||
|
providers: [...SERVICES]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
185
documents/업무/2월/3째주/file-viewer-prj/ucap-ui.module.ts
Normal file
185
documents/업무/2월/3째주/file-viewer-prj/ucap-ui.module.ts
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
|
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatDialogModule } from '@angular/material/dialog';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
import { MatSliderModule } from '@angular/material/slider';
|
||||||
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||||
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||||
|
import {
|
||||||
|
MatTabsModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule
|
||||||
|
} from '@angular/material';
|
||||||
|
|
||||||
|
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||||
|
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { FileUploadQueueComponent } from './components/file-upload-queue.component';
|
||||||
|
import { FloatActionButtonComponent } from './components/float-action-button.component';
|
||||||
|
import { FileViewerComponent } from './components/file-viewer.component';
|
||||||
|
import { MediaViewerComponent } from './components/media-viewer.component';
|
||||||
|
import { ExpansionPanelComponent } from './components/expansion-panel.component';
|
||||||
|
import { SplitButtonComponent } from './components/split-button.component';
|
||||||
|
import { PickDateComponent } from './components/pick-date.component';
|
||||||
|
import { PickTimeComponent } from './components/pick-time.component';
|
||||||
|
import { StepInputComponent } from './components/step-input.component';
|
||||||
|
import { StickerSelectorComponent } from './components/sticker-selector.component';
|
||||||
|
import { InlineEditInputComponent } from './components/inline-edit-input.component';
|
||||||
|
|
||||||
|
import { BinaryViewerComponent } from './components/file-viewer/binary-viewer.component';
|
||||||
|
import { DocumentViewerComponent } from './components/file-viewer/document-viewer.component';
|
||||||
|
import { ImageViewerComponent } from './components/file-viewer/image-viewer.component';
|
||||||
|
import { SoundViewerComponent } from './components/file-viewer/sound-viewer.component';
|
||||||
|
import { VideoViewerComponent } from './components/file-viewer/video-viewer.component';
|
||||||
|
|
||||||
|
import { BottomSheetService } from './services/bottom-sheet.service';
|
||||||
|
import { ClipboardService } from './services/clipboard.service';
|
||||||
|
import { DialogService } from './services/dialog.service';
|
||||||
|
import { SnackBarService } from './services/snack-bar.service';
|
||||||
|
import { SplashScreenService } from './services/splash-screen.service';
|
||||||
|
import { TranslateService } from './services/translate.service';
|
||||||
|
import { DateService } from './services/date.service';
|
||||||
|
import { PaginatorIntlService } from './services/paginator-intl.service';
|
||||||
|
|
||||||
|
import { ClickOutsideDirective } from './directives/click-outside.directive';
|
||||||
|
import { FileUploadForDirective } from './directives/file-upload-for.directive';
|
||||||
|
import { ImageDirective } from './directives/image.directive';
|
||||||
|
import { CdkVirtualScrollViewportPatchDirective } from './directives/cdk-virtual-scroll-viewport-patch.directive';
|
||||||
|
|
||||||
|
import { AlertDialogComponent } from './dialogs/alert.dialog.component';
|
||||||
|
import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component';
|
||||||
|
|
||||||
|
import { BytesPipe } from './pipes/bytes.pipe';
|
||||||
|
import { LinefeedToHtmlPipe, HtmlToLinefeedPipe } from './pipes/linefeed.pipe';
|
||||||
|
import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe';
|
||||||
|
import { LinkyPipe } from './pipes/linky.pipe';
|
||||||
|
import { TranslatePipe } from './pipes/translate.pipe';
|
||||||
|
import { DatePipe } from './pipes/date.pipe';
|
||||||
|
import { SafeHtmlPipe } from './pipes/safe-html.pipe';
|
||||||
|
|
||||||
|
import {
|
||||||
|
StringEmptyCheckPipe,
|
||||||
|
StringFormatterPhonePipe
|
||||||
|
} from './pipes/string.pipe';
|
||||||
|
import { ClickDebounceDirective } from './directives/click-debounce.directive';
|
||||||
|
import { TranslationSectionComponent } from './components/translation-section.component';
|
||||||
|
import { AlertSnackbarComponent } from './snackbars/alert.snackbar.component';
|
||||||
|
import { IntegratedSearchFormComponent } from './components/integrated-search-form.component';
|
||||||
|
import { IntegratedSearchComponent } from './components/integrated-search.component';
|
||||||
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
|
const COMPONENTS = [
|
||||||
|
FileUploadQueueComponent,
|
||||||
|
FloatActionButtonComponent,
|
||||||
|
FileViewerComponent,
|
||||||
|
ExpansionPanelComponent,
|
||||||
|
StickerSelectorComponent,
|
||||||
|
SplitButtonComponent,
|
||||||
|
PickDateComponent,
|
||||||
|
PickTimeComponent,
|
||||||
|
StepInputComponent,
|
||||||
|
TranslationSectionComponent,
|
||||||
|
InlineEditInputComponent,
|
||||||
|
IntegratedSearchComponent,
|
||||||
|
IntegratedSearchFormComponent,
|
||||||
|
|
||||||
|
BinaryViewerComponent,
|
||||||
|
DocumentViewerComponent,
|
||||||
|
ImageViewerComponent,
|
||||||
|
SoundViewerComponent,
|
||||||
|
VideoViewerComponent,
|
||||||
|
MediaViewerComponent
|
||||||
|
];
|
||||||
|
const DIALOGS = [
|
||||||
|
AlertDialogComponent,
|
||||||
|
ConfirmDialogComponent,
|
||||||
|
AlertSnackbarComponent
|
||||||
|
];
|
||||||
|
const DIRECTIVES = [
|
||||||
|
ClickOutsideDirective,
|
||||||
|
FileUploadForDirective,
|
||||||
|
ImageDirective,
|
||||||
|
CdkVirtualScrollViewportPatchDirective,
|
||||||
|
ClickDebounceDirective
|
||||||
|
];
|
||||||
|
const PIPES = [
|
||||||
|
BytesPipe,
|
||||||
|
LinefeedToHtmlPipe,
|
||||||
|
HtmlToLinefeedPipe,
|
||||||
|
SecondsToMinutesPipe,
|
||||||
|
LinkyPipe,
|
||||||
|
TranslatePipe,
|
||||||
|
DatePipe,
|
||||||
|
StringEmptyCheckPipe,
|
||||||
|
StringFormatterPhonePipe,
|
||||||
|
SafeHtmlPipe
|
||||||
|
];
|
||||||
|
const SERVICES = [
|
||||||
|
BottomSheetService,
|
||||||
|
ClipboardService,
|
||||||
|
DialogService,
|
||||||
|
SnackBarService,
|
||||||
|
SplashScreenService,
|
||||||
|
TranslateService,
|
||||||
|
DateService,
|
||||||
|
PaginatorIntlService
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatDialogModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
MatProgressBarModule,
|
||||||
|
MatSliderModule,
|
||||||
|
MatSnackBarModule,
|
||||||
|
MatToolbarModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
MatTabsModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatButtonToggleModule,
|
||||||
|
MatMenuModule,
|
||||||
|
MatDatepickerModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatSlideToggleModule,
|
||||||
|
MatTableModule,
|
||||||
|
MatPaginatorModule,
|
||||||
|
|
||||||
|
PerfectScrollbarModule,
|
||||||
|
DragDropModule,
|
||||||
|
TranslateModule
|
||||||
|
],
|
||||||
|
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
||||||
|
declarations: [...COMPONENTS, ...DIALOGS, ...DIRECTIVES, ...PIPES],
|
||||||
|
entryComponents: [...DIALOGS]
|
||||||
|
})
|
||||||
|
export class UCapUiModule {
|
||||||
|
public static forRoot(): ModuleWithProviders<UCapUiModule> {
|
||||||
|
return {
|
||||||
|
ngModule: UCapUiModule,
|
||||||
|
providers: [...SERVICES]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { EventJsonDecoder } from './event-json';
|
||||||
|
import { JsonAnalization } from '@ucap-webmessenger/api';
|
||||||
|
import { VideoConferenceContentsType } from '@ucap-webmessenger/protocol-event';
|
||||||
|
|
||||||
|
export interface VideoConferenceEventJson {
|
||||||
|
conferenceSeq?: number;
|
||||||
|
title?: string;
|
||||||
|
contents?: VideoConferenceContentsType;
|
||||||
|
startDate?: string;
|
||||||
|
endDate?: string;
|
||||||
|
register?: string;
|
||||||
|
attendee?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeVideoConferenceEventJson: EventJsonDecoder<VideoConferenceEventJson> = (
|
||||||
|
message: string
|
||||||
|
) => {
|
||||||
|
try {
|
||||||
|
const json = JsonAnalization.receiveAnalization(message);
|
||||||
|
|
||||||
|
return {
|
||||||
|
conferenceSeq: Number(json.confSeq),
|
||||||
|
title: json.title,
|
||||||
|
contents: json.contents,
|
||||||
|
startDate: json.startDate,
|
||||||
|
endDate: json.endDate,
|
||||||
|
register: json.register,
|
||||||
|
attendee: json.attendee
|
||||||
|
} as VideoConferenceEventJson;
|
||||||
|
} catch (e) {
|
||||||
|
return {} as VideoConferenceEventJson;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user