0220 업무 번들이미지 뷰어 작업

This commit is contained in:
Park Byung Eun 2020-02-21 09:35:58 +09:00
parent ef603030ef
commit 41499618ea
25 changed files with 4916 additions and 1 deletions

View File

@ -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->전화걸기 메뉴 삭제 (주석)

View 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

View File

@ -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>

View 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
});
}
}
}

View File

@ -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;
}
};

View 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;
})
);
}
}

View 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);
}

View File

@ -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>

View File

@ -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
);
};
}

View 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
});
}
}
}

View 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;
}
};

View 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')
}
});
}
}

View 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;
}
};

View File

@ -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;
}
};

View 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);
}
}

File diff suppressed because it is too large Load Diff

View 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;
}

View File

@ -0,0 +1,4 @@
export interface SelectFileInfo {
attachmentSeq: number;
index?: number;
}

View File

@ -0,0 +1,4 @@
export interface SelectFileInfo {
attachmentSeq: number;
index?: number;
}

View 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;
}
};

View 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';

View 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';

View 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]
};
}
}

View 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]
};
}
}

View File

@ -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;
}
};