diff --git a/documents/업무/2월/02_TODO b/documents/업무/2월/02_TODO index 57d6ac7..ea3c791 100644 --- a/documents/업무/2월/02_TODO +++ b/documents/업무/2월/02_TODO @@ -1,5 +1,5 @@ -묶음파일 - 업로드 +묶음파일 + 업로드 (부분 완료) 묶음파일 request 모델 정의 userSeq: number; deviceType: DeviceType; @@ -24,6 +24,12 @@ 이벤트 타입별 출력 묶음파일 타입 그리드 썸네일 컴포넌트 정의 그리드 알고리즘 작성 + 썸네일 출력(완료) + 출력 가이드 라인 + 최대 가로 출력 개수 3개 + 다음행에 출력 개수가 홀수 일때 + 빈 공간이 출력되지 않게 조정 + 최대 width, 최소 height 테스트 후 결정 앨범함 묶음파일 타입 처리 뷰어 컴포넌트 (슬라이드 기능) (진행) @@ -33,13 +39,6 @@ 일반 이미지 출력 동영상인 경우 일반 동영상 썸네일 출력 - 썸네일 출력 - 출력 가이드 라인 - 최대 가로 출력 개수 3개 - 다음행에 출력 개수가 홀수 일때 - 빈 공간이 출력되지 않게 조정 - 최대 width, 최소 height 테스트 후 결정 - 카톡 벤치마킹 묶음파일 전송 후 1개의 이미지 전송 @@ -47,6 +46,7 @@ 동영상 전송 동영상 썸네일 출력 동영상은 묶음파일 지원안함 + 묶음파일 기획은 카톡과 동일하게 진행 원본 출력 //원본 파일 호출할 때 리플레이스 @@ -93,26 +93,67 @@ 기능 목록 모바일 주소록 동기화 기존방식 (데이터가 많은 경우 중간 서버에서 끊길 가능성이 농후) - PC 서버 요청 - 모바일 노티 - for - 모바일 주소록 서버 전송 - 모바일 주소록 서버 응답 - 서버가 주소록 리시브 노티 + 1. PC -> Server + 모바일 주소록 동기화 시작 + SSVC_TYPE_SYNC_PHONEBOOK_READY_REQ + 2. Server -> Mobile + 모바일 주소록 동기화 노티 + 3. Mobile -> Server + 준비 완료 요청 + 4. Server -> PC + SSVC_TYPE_SYNC_PHONEBOOK_READY_NOTI + 5. PC -> Server + 모바일 주소록 받을 준비 완료 요청 + SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_REQ + 6.Server -> PC + SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_RES //사용없음 + SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_NOTI + PC가 모바일로부터 준비 확인 NOTI를 받는 처리를 수행 + 7.Server -> PC + SSVC_TYPE_SYNC_PHONEBOOK_SND_RES //사용없음 + SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI //실데이터받는 프로토콜 박차장님이 제안한 방식 - PC 서버 요청 - 모바일 노티 - 모바일 주소록 JSON 으로 전송 - 새로운 방식 - 생각해봐야함 - 서버에 개인주소록을 저장할 경우 - PC에서 동기화 완료 후 서버 데이터 삭제 고려 - 서버에 개인주소록을 저장하지 않을 경우 - 서버 부하 고려 + PC -> 서버 + 개인 주소록 동기화 요청 + 프로토콜 정의 협의 + 서버 -> 모바일 + 개인 주소록 동기화 요청에 대한 푸시 + 모바일 -> 서버 (REST API) + 개인 주소록 POST 전송 + 데이터 JSON 형태 + 서버 -> 모바일 (REST API) + 개인 주소록 전송에 대한 응답 + (응답코드, 싱크번호, 시간, 사용자번호) + 모바일 -> 서버 + 개인 주소록 전송 동기화 프로토콜 요청 + (싱크번호, 시간, 사용자번호) + 서버 -> 모바일 + 개인 주소록 전송 동기화 프로토콜 요청에 대한 응답 + 서버 -> PC + 개인 주소록 동기화 요청 프로토콜에 대한 푸시 + (싱크번호, 사용자번호) + PC -> 서버 (REST API) + 개인 주소록 요청 + (싱크번호, 사용자번호) + 서버 -> PC + 개인 주소록 JSON + (싱크번호, 시간, 사용자 번호) + 모바일 주소록 초기화 PC에 저장된 모바일 주소록을 삭제 엑샐 Export/Import 템플릿 초기화 -MAC용 빌드 \ No newline at end of file +MAC용 빌드 + +대화 저장 + 두개다 대화내용 암호화? + 서버에서 대화내용을 제공 + 프로토콜 협의 + PC에서 대화내용 조회후 제공 + 서버 부하 + + 협의 필요 + + diff --git a/documents/업무/2월/1번째주/0206.txt b/documents/업무/2월/1째주/0206.txt similarity index 100% rename from documents/업무/2월/1번째주/0206.txt rename to documents/업무/2월/1째주/0206.txt diff --git a/documents/업무/2월/1번째주/0207.txt b/documents/업무/2월/1째주/0207.txt similarity index 100% rename from documents/업무/2월/1번째주/0207.txt rename to documents/업무/2월/1째주/0207.txt diff --git a/documents/업무/2월/2번째주/0210.txt b/documents/업무/2월/2째주/0210.txt similarity index 98% rename from documents/업무/2월/2번째주/0210.txt rename to documents/업무/2월/2째주/0210.txt index d37f824..60ab7ef 100644 --- a/documents/업무/2월/2번째주/0210.txt +++ b/documents/업무/2월/2째주/0210.txt @@ -33,4 +33,5 @@ tems[position].FILE_THUMB_URL .replace("WebFile", "AttFile") .replace(".thumb.jpg", "") -클라이언트 참고 사항입니ㅏㄷ..실제 파일 열기 하실때는 실제 업로드된 파일경로로 접속하셔야 합니다. \ No newline at end of file +클라이언트 참고 사항입니ㅏㄷ..실제 파일 열기 하실때는 실제 업로드된 파일경로로 접속하셔야 합니다. + \ No newline at end of file diff --git a/documents/업무/2월/2번째주/0211.txt b/documents/업무/2월/2째주/0211.txt similarity index 100% rename from documents/업무/2월/2번째주/0211.txt rename to documents/업무/2월/2째주/0211.txt diff --git a/documents/업무/2월/2번째주/0212.txt b/documents/업무/2월/2째주/0212.txt similarity index 99% rename from documents/업무/2월/2번째주/0212.txt rename to documents/업무/2월/2째주/0212.txt index 5998b69..4abab34 100644 --- a/documents/업무/2월/2번째주/0212.txt +++ b/documents/업무/2월/2째주/0212.txt @@ -13,7 +13,7 @@ fileInfo 조회 decodeInfoData-> - + WARNING in Circular dependency detected: projects\ucap-webmessenger-protocol-event\src\lib\protocols\event-json\codec.ts -> projects\ucap-webmessenger-protocol-event\src\lib\protocols\event-json\bundle-image.event-json.ts -> @@ -64,6 +64,7 @@ projects\ucap-webmessenger-protocol-file\src\public-api.ts -> projects\ucap-webm 1: "16F98292020-02-10 07:09:16{ + ↵"StatusCode":"200", ↵"ErrorMessage":"", ↵"RoomID":"76", diff --git a/documents/업무/2월/2째주/0213.txt b/documents/업무/2월/2째주/0213.txt new file mode 100644 index 0000000..50f95ba --- /dev/null +++ b/documents/업무/2월/2째주/0213.txt @@ -0,0 +1,151 @@ +// 그리드 함수 백업 + + makeGrid() { + const totalCount = this.bundleImageJson.fileCount; + const remainder = totalCount % 3; + const quotient = Math.floor(totalCount / 3); + + let tile; + + if (remainder === 0) { + this.bundleImageJson.thumbUrls.forEach((v, idx) => { + tile = {} as Tile; + tile.cols = 2; + this.getTile(tile, v); + }); + } else if (remainder === 1) { + if (quotient === 0) { + tile = {} as Tile; + this.bundleImageJson.thumbUrls.forEach(v => { + this.getTile(tile, v); + }); + tile.cols = 6; + return; + } + + this.bundleImageJson.thumbUrls.forEach((v, idx) => { + tile = {} as Tile; + if (quotient <= idx / 3 + 1) { + tile.cols = 3; + } else { + tile.cols = 2; + } + this.getTile(tile, v); + }); + } else { + this.bundleImageJson.thumbUrls.forEach((v, idx) => { + tile = {} as Tile; + if (quotient <= idx / 3) { + tile.cols = 3; + } else { + tile.cols = 2; + } + this.getTile(tile, v); + }); + } + } + + private getTile(tile: Tile, v: string) { + tile.imgSrc = this.baseURL + v; + tile.color = 'white'; + this.tiles.push(tile); + } + +모바일 개인 주소록을 PC 버전에도 공유 + PC->Mobile + 주소록 요청 + PC<-Mobile + 주소록 전송 + PC->Mobile + 주소록 완료 + +주소록 + UI 구성 + 검색 (이름, 전화번호) + 검색 결과 + 리스트 출력 + 모바일 주소록 동기화(PC-> Mobile 요청) + 모바일 주소록 초기화(PC에서 동기화된 주소록 초기화) + 엑샐 탬플릿 (주소록을 입력할 수 있는 액샐 템플릿을 제공) + 액샐 업로드 (주소록이 입력된 액샐 템플릿을 업로드 하여 PC 주소록 업데이트) + 액샐 데이터 초기화 (액샐 업로드 데이터 초기화) + 액셀 내려받기 (주소록 데이터 액셀로 다운로드) + 기능 목록 + 모바일 주소록 동기화 + 기존방식 (데이터가 많은 경우 중간 서버에서 끊길 가능성) + 1. PC -> Server + 모바일 주소록 동기화 시작 + SSVC_TYPE_SYNC_PHONEBOOK_READY_REQ + 2. Server -> Mobile + 모바일 주소록 동기화 노티 + 3. Mobile -> Server + 모바일 준비 완료 요청 및 데이터 전송 (추측) + 4. Server -> PC + 4. Server -> PC + SSVC_TYPE_SYNC_PHONEBOOK_READY_NOTI + 5. PC -> Server + PC 준비 완료 요청 + SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_REQ + 6.Server -> PC + SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_RES //사용없음 + SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_NOTI + PC가 모바일로부터 준비 확인 NOTI를 받는 처리를 수행 + 7.Server -> PC + SSVC_TYPE_SYNC_PHONEBOOK_SND_RES //사용없음 + SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI //실데이터받는 프로토콜 + 박차장님이 제안한 방식 + PC -> 서버 + 개인 주소록 동기화 요청 + 프로토콜 정의 협의 + SSVC_TYPE_SYNC_PHONEBOOK_READY_REQ + (사용자번호) + 서버 -> 모바일 + 개인 주소록 동기화 요청에 대한 푸시 + 모바일 -> 서버 (REST API) + 개인 주소록 POST 전송 + 데이터 JSON 형태 + 서버 -> 모바일 (REST API) + 개인 주소록 전송에 대한 응답 + (응답코드, 싱크번호, 시간, 사용자번호) + 모바일 -> 서버 + 개인 주소록 전송 동기화 프로토콜 요청 + (싱크번호, 시간, 사용자번호) + 서버 -> 모바일 + 개인 주소록 전송 동기화 프로토콜 요청에 대한 응답 + 서버 -> PC + 모바일 개인 주소록 전송 완료 노티 + SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI + (싱크번호, 사용자번호) + PC -> 서버 (REST API) + 개인 주소록 요청 + (싱크번호, 사용자번호) + 서버 -> PC + 개인 주소록 JSON + (싱크번호, 시간, 사용자 번호) + + 모바일 주소록 초기화 + PC에 저장된 모바일 주소록을 삭제 + 엑샐 + Export/Import + 템플릿 + 초기화 + +대화방 + +TODO 완료 + 대화 묶음파일 그리드 컴포넌트 리팩토링 + + 파일 업로드 큐 컴포넌트 수정 + 묶음파일 처리 추가 + + 묶음파일 업로드 함수 리팩토링 + 묶음파일 모델링 수정 + 메세지 전송 컴포넌트 수정 + 묶음파일 전송 UI 추가 + 묶음파일 전송 로직 추가 + 이벤트 타입 추가 + BundleImage = 'b'; + 파일 타입 추가 + Bundle = 'b'; + 앨범함 컴포넌트 수정 + 묶음파일 타입 처리 추가 diff --git a/documents/업무/2월/2째주/0214.txt b/documents/업무/2월/2째주/0214.txt new file mode 100644 index 0000000..9196152 --- /dev/null +++ b/documents/업무/2월/2째주/0214.txt @@ -0,0 +1,37 @@ +내일 TODO + Call 컴포넌트 작성 + 다이얼러 컴포넌트 작성 + 주소록 컴포넌트 작성 + 최근통화내역 컴포넌트 작성 +요구사항 + 지역번호-3자리-4자리 /^(0(2|3[1-3]|4[1-4]|5[1-5]|6[1-4]))-(\d{3,4})-(\d{4})$/ + 010-4자리-4자리 || 011,017,016-3자리-4자리 /^(?:(010-\d{4})|(01[1|6|7|8|9]-\d{3,4}))-(\d{4})$/ + 기타번호4자리-4자리 + // 추후에 +국가코드3자리- + 입력번호, 출력번호 + 숫자 버튼, + 통화 버튼, + 삭제 버튼 + + 4행 * 3열 그리드 출력 + + 번호 함수 + 입력 이벤트 마다 지역번호, 핸드폰 번호 추출 + 하이픈 넣기 + 입력번호를 출력번호로 변환 + + 삭제 함수 + 이벤트 발생 마다 + 입력번호 삭제 + 번호함수 호출 + + 통화 함수 + 클릭투콜 API 호출 + +주간보고서 작성 + 묶음파일 업로드 추가 + 그리드 기능 추가 + 앨범함 기능 추가 + 묶음파일 전송 기능 추가 + 파일 업로드 기능 수정 + \ No newline at end of file diff --git a/documents/업무/2월/2째주/LF_개인주소록_프로토콜.txt b/documents/업무/2월/2째주/LF_개인주소록_프로토콜.txt new file mode 100644 index 0000000..b0477d6 --- /dev/null +++ b/documents/업무/2월/2째주/LF_개인주소록_프로토콜.txt @@ -0,0 +1,34 @@ +SSVC_TYPE_SYNC_PHONEBOOK_READY_REQ = 21, || 모바일 주소록 씽크 준비 요청 + PC -> Server + 모바일 온라인 확인 단계 필요 + +SSVC_TYPE_SYNC_PHONEBOOK_READY_RES = 22, || 모바일 주소록 씽크 준비 요청완료 + 서버 -> PC + SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI 프로토콜을 30초동안 기다림 + +SSVC_TYPE_SYNC_PHONEBOOK_READY_NOTI = 23, || 모바일 주소록 씽크 준비 알림 + +SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_REQ = 24, || 모바일 주소록 씽크 준비 확인 요청 + 3 + +SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_RES = 25, || 모바일 주소록 씽크 준비 확인 요청완료 + +SSVC_TYPE_SYNC_PHONEBOOK_READY_OK_NOTI = 26, || 모바일 주소록 씽크 준비 확인 알림 + +SSVC_TYPE_SYNC_PHONEBOOK_SND_REQ = 31, || 모바일 주소록 씽크 요청 +SSVC_TYPE_SYNC_PHONEBOOK_SND_RES = 32, || 모바일 주소록 씽크 완료 + 데이터는 M->PC 단방향 해당 RES는 PC 에서는 아무의미가 없다. + +SSVC_TYPE_SYNC_PHONEBOOK_SND_NOTI = 33, || 모바일 주소록 씽크 알림 + 해당 노티는 PC가 받는 노티(실데이터가 있음) + +//** 모바일에서 사용하는 프로토콜 **// +SSVC_TYPE_SYNC_PHONEBOOK_RCV_REQ = 34, || 모바일 주소록 씽크 확인 요청 + +SSVC_TYPE_SYNC_PHONEBOOK_RCV_RES = 35, || 모바일 주소록 씽크 확인 요청완료 + PC 사용 X +SSVC_TYPE_SYNC_PHONEBOOK_RCV_NOTI = 36, || 모바일 주소록 씽크 확인 알림 + PC 사용 X + 모바일에서 사용한는 프로토콜 +//** 모바일에서 사용하는 프로토콜 **// + diff --git a/documents/업무/2월/2째주/묵음파일백업/album-box.component.html b/documents/업무/2월/2째주/묵음파일백업/album-box.component.html new file mode 100644 index 0000000..9110e07 --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/album-box.component.html @@ -0,0 +1,182 @@ +
+
+ + + + +
+
+ +
+ + + + + + {{ 'common.file.selectFiles' | translate }} +
+ +
+ + + + + + + + + + + {{ 'common.file.selectFiles' | translate }} +
+
+ +
+ + +
+ {{ 'common.file.errors.cantPlay' | translate }} +
+
+
    +
  • {{ selectedFile.info.name }}
  • +
  • + size : + {{ selectedFile.info.size | ucapBytes }} +
  • +
  • + date : + {{ selectedFile.info.sendDate | ucapDate: 'YYYY.MM.DD' }} +
  • +
+
+
+
+ +
+
+
+
+ +
+
+ + + + +
+
+
+
+
+
+ + + + + + + +
+
+
+
+
+
+ + +
+
diff --git a/documents/업무/2월/2째주/묵음파일백업/album-box.component.ts b/documents/업무/2월/2째주/묵음파일백업/album-box.component.ts new file mode 100644 index 0000000..2a579ef --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/album-box.component.ts @@ -0,0 +1,264 @@ +import { + Component, + OnInit, + OnDestroy, + Inject, + ElementRef, + ViewChild +} from '@angular/core'; +import { + FileInfo, + FileDownloadInfo, + FileType +} from '@ucap-webmessenger/protocol-file'; +import { Subscription, combineLatest } from 'rxjs'; +import { Store, select } from '@ngrx/store'; + +import * as AppStore from '@app/store'; +import { tap } from 'rxjs/operators'; +import { FileUtil } from '@ucap-webmessenger/core'; +import { CommonApiService } from '@ucap-webmessenger/api-common'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { SessionStorageService } from '@ucap-webmessenger/web-storage'; +import { KEY_LOGIN_RES_INFO } from '@app/types/login-res-info.type'; +import { + EnvironmentsInfo, + KEY_ENVIRONMENTS_INFO, + KEY_VER_INFO +} from '@app/types'; +import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; +import { UCAP_NATIVE_SERVICE, NativeService } from '@ucap-webmessenger/native'; +import { NGXLogger } from 'ngx-logger'; +import { FileDownloadItem } from '@ucap-webmessenger/api'; +import { ModuleConfig } from '@ucap-webmessenger/api-common'; +import { _MODULE_CONFIG } from 'projects/ucap-webmessenger-api-common/src/lib/config/token'; +import { AppFileService } from '@app/services/file.service'; + +export interface FileInfoTotal { + info: FileInfo; + checkInfo: FileDownloadInfo[]; + fileDownloadItem: FileDownloadItem; +} + +@Component({ + selector: 'app-layout-chat-right-drawer-album-box', + templateUrl: './album-box.component.html', + styleUrls: ['./album-box.component.scss'] +}) +export class AlbumBoxComponent implements OnInit, OnDestroy { + @ViewChild('videoPlayer', { static: false }) + videoPlayer: ElementRef; + + filteredList: FileInfoTotal[] = []; + fileInfoTotal: FileInfoTotal[]; + fileInfoList: FileInfo[]; + fileInfoListSubscription: Subscription; + + selectedFile: FileInfoTotal; + selectedFileList: FileInfoTotal[] = []; + + loginRes: LoginResponse; + environmentsInfo: EnvironmentsInfo; + sessionVerinfo: VersionInfo2Response; + + FileType = FileType; + currentTabIndex = 0; + + thumbBaseUrl: string; + + playable = true; + + constructor( + private store: Store, + private sessionStorageService: SessionStorageService, + private commonApiService: CommonApiService, + @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, + private appFileService: AppFileService, + @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig, + private logger: NGXLogger + ) { + this.loginRes = this.sessionStorageService.get( + KEY_LOGIN_RES_INFO + ); + this.environmentsInfo = this.sessionStorageService.get( + KEY_ENVIRONMENTS_INFO + ); + this.sessionVerinfo = this.sessionStorageService.get( + KEY_VER_INFO + ); + + this.thumbBaseUrl = `${this.moduleConfig.hostConfig.protocol}://${ + this.moduleConfig.hostConfig.domain + }:${String(this.moduleConfig.hostConfig.port)}`; + } + + ngOnInit() { + this.fileInfoListSubscription = combineLatest([ + this.store.pipe(select(AppStore.MessengerSelector.RoomSelector.roomInfo)), + this.store.pipe( + select(AppStore.MessengerSelector.EventSelector.selectAllFileInfoList) + ), + this.store.pipe( + select( + AppStore.MessengerSelector.EventSelector.selectAllFileInfoCheckList + ) + ) + ]) + .pipe( + tap(() => (this.fileInfoTotal = [])), + tap(([roomInfo, fileInfoList, fileInfoCheckList]) => { + this.fileInfoList = fileInfoList.filter(fileInfo => { + if ( + !!roomInfo && + fileInfo.roomSeq === roomInfo.roomSeq && + (fileInfo.type === FileType.Image || + fileInfo.type === FileType.Video) + ) { + return true; + } else { + return false; + } + }); + + this.fileInfoList.map(fileInfo => { + this.fileInfoTotal.push({ + info: fileInfo, + checkInfo: fileInfoCheckList.filter( + checkInfo => checkInfo.seq === fileInfo.seq + ), + fileDownloadItem: new FileDownloadItem() + }); + }); + + this.onSelectedIndexChange(this.currentTabIndex); + }) + ) + .subscribe(); + } + + ngOnDestroy(): void { + if (!!this.fileInfoListSubscription) { + this.fileInfoListSubscription.unsubscribe(); + } + } + + getExtention(name: string): string { + return FileUtil.getExtension(name); + } + + getImageUrl(fileInfo: FileInfoTotal): string { + return this.commonApiService.urlForFileTalkDownload( + { + userSeq: this.loginRes.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginRes.tokenString, + attachmentsSeq: fileInfo.info.seq + }, + this.sessionVerinfo.downloadUrl + ); + } + + onErrorThumbnail(el: HTMLElement, fileInfo: FileInfoTotal): void { + const iconEl = document.createElement('div'); + iconEl.setAttribute( + 'class', + 'mime-icon light ico-' + this.getExtention(fileInfo.info.name) + ); + iconEl.innerHTML = `
`; + el.replaceWith(iconEl); + } + + onSelectedIndexChange(index: number) { + this.selectedFile = null; + this.currentTabIndex = index; + if (this.currentTabIndex === 0) { + // Image + this.filteredList = this.fileInfoTotal.filter( + fileInfo => fileInfo.info.type === FileType.Image + ); + } else { + // Video + this.filteredList = this.fileInfoTotal.filter( + fileInfo => fileInfo.info.type === FileType.Video + ); + } + } + + onClickImage(event: MouseEvent, fileInfo: FileInfoTotal) { + if (!!event) { + event.preventDefault(); + event.stopPropagation(); + } + + this.playable = true; + this.selectedFile = fileInfo; + } + + getCheckItem(fileInfo: FileInfoTotal) { + if (this.selectedFileList) { + if ( + this.selectedFileList.filter( + info => info.info.seq === fileInfo.info.seq + ).length > 0 + ) { + return true; + } else { + return false; + } + } else { + return false; + } + } + onCheckItem(value: boolean, fileInfo: FileInfoTotal) { + if (value) { + this.onClickImage(undefined, fileInfo); + this.selectedFileList.push(fileInfo); + } else { + this.selectedFileList = this.selectedFileList.filter( + info => info.info.seq !== fileInfo.info.seq + ); + } + } + + onClickDownload(fileInfo: FileInfoTotal) { + this.appFileService.fileTalkDownlod({ + req: { + userSeq: this.loginRes.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginRes.tokenString, + attachmentsSeq: fileInfo.info.seq, + fileDownloadItem: fileInfo.fileDownloadItem + }, + fileName: fileInfo.info.name + }); + } + + onClickDownloadAll(): void { + this.selectedFileList.forEach(fileInfo => { + this.onClickDownload(fileInfo); + }); + } + + onClickOpenDownloadFolder(): void { + this.nativeService + .openDefaultDownloadFolder() + .then(result => { + if (!!result) { + } else { + throw new Error('response Error'); + } + }) + .catch(reason => { + this.logger.error(reason); + }); + } + + onLoadedDataVideo(): void { + if ( + 0 === this.videoPlayer.nativeElement.videoWidth || + 0 === this.videoPlayer.nativeElement.videoHeight + ) { + this.playable = false; + } + } +} diff --git a/documents/업무/2월/2째주/묵음파일백업/bundle-image.event-json.ts b/documents/업무/2월/2째주/묵음파일백업/bundle-image.event-json.ts new file mode 100644 index 0000000..766272a --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/bundle-image.event-json.ts @@ -0,0 +1,36 @@ +import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api'; +import { FileType } from '@ucap-webmessenger/protocol-file'; +import { EventJsonDecoder } from './event-json'; + +export interface BundleImageEventJson { + statusCode?: StatusCode; + errorMessage?: string; + roomSeq?: number; + attachmentSeq?: number; + fileCount?: number; + baseUrl?: string; + thumbUrls?: string[]; + fileType?: FileType; +} + +export const decodeBundleImageEventJson: EventJsonDecoder = ( + message: string +) => { + try { + const json = JsonAnalization.receiveAnalization(message); + + return { + statusCode: json.StatusCode, + errorMessage: json.ErrorMessage, + roomSeq: json.RoomID, + attachmentSeq: json.AttSEQ, + fileCount: json.FileCount, + thumbUrls: json.ThumbURL + } as BundleImageEventJson; + } catch (e) { + return { + statusCode: StatusCode.Fail, + errorMessage: e.toString() + } as BundleImageEventJson; + } +}; diff --git a/documents/업무/2월/2째주/묵음파일백업/common-api.service.ts b/documents/업무/2월/2째주/묵음파일백업/common-api.service.ts new file mode 100644 index 0000000..b955052 --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/common-api.service.ts @@ -0,0 +1,382 @@ +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'; + +@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 { + 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) => { + 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 { + const httpReq = new HttpRequest( + 'POST', + !!fileTalkDownloadUrl ? fileTalkDownloadUrl : this.urls.fileTalkDownload, + encodeFormDataFileTalkDownload(req), + { reportProgress: true, responseType: 'blob' } + ); + + let progress: Subject; + 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) => { + if (!!progress) { + req.fileDownloadItem.downloadComplete(); + } + return event.body; + }) + ); + } + + public fileTalkSaveMulti( + req: FileTalkSaveMultiRequest, + fileTalkSaveMultiUrl?: string + ): Observable { + const httpReq = new HttpRequest( + 'POST', + !!fileTalkSaveMultiUrl + ? fileTalkSaveMultiUrl + : this.urls.fileTalkSaveMulti, + encodeFileTalkSaveMulti(req), + { reportProgress: true, responseType: 'text' as 'json' } + ); + + const progressList: Subject[] = []; + for (const p of req.fileUploadItems) { + progressList.push(p.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)); + for (const progress of progressList) { + progress.next(Math.round((100 * event.loaded) / event.total)); + } + } + return false; + }), + map((event: HttpResponse) => { + for (const p of req.fileUploadItems) { + p.uploadComplete(); + } + return decodeFileTalkSaveMulti(event.body); + }) + ); + } + + public fileTalkSave( + req: FileTalkSaveRequest, + fileTalkSaveUrl?: string + ): Observable { + 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) => { + req.fileUploadItem.uploadComplete(); + return decodeFileTalkSave(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 { + return this.httpClient + .post( + this.urls.fileTalkShare, + {}, + { + params: encodeFileTalkShare(req) + } + ) + .pipe(map(res => decodeFileTalkShare(res))); + } + + public massTalkDownload( + req: MassTalkDownloadRequest + ): Observable { + return this.httpClient + .post( + this.urls.massTalkDownload, + {}, + { + params: encodeMassTalkDownload(req), + responseType: 'text' as 'json' + } + ) + .pipe(map(res => decodeMassTalkDownload(res))); + } + + public massTalkSave( + req: MassTalkSaveRequest + ): Observable { + 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).body)) + ); + } + + public transMassTalkDownload( + req: TransMassTalkDownloadRequest + ): Observable { + return this.httpClient + .post( + this.urls.transMassTalkDownload, + {}, + { + params: encodeTransMassTalkDownload(req) + } + ) + .pipe(map(res => decodeTransMassTalkDownload(res))); + } + + public transMassTalkSave( + req: TransMassTalkSaveRequest + ): Observable { + return this.httpClient + .post( + this.urls.transMassTalkSave, + {}, + { + params: encodeTransMassTalkSave(req) + } + ) + .pipe(map(res => decodeTransMassTalkSave(res))); + } + + public translationReq( + req: TranslationReqRequest + ): Observable { + return this.httpClient + .post( + this.urls.translationReq, + {}, + { + params: encodeTranslationReq(req) + } + ) + .pipe(map(res => decodeTranslationReq(res))); + } + + public translationSave( + req: TranslationSaveRequest + ): Observable { + 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).body)) + ); + } +} diff --git a/documents/업무/2월/2째주/묵음파일백업/file-talk-save-multi.ts b/documents/업무/2월/2째주/묵음파일백업/file-talk-save-multi.ts new file mode 100644 index 0000000..17a7c71 --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/file-talk-save-multi.ts @@ -0,0 +1,79 @@ +import { DeviceType } from '@ucap-webmessenger/core'; +import { + APIRequest, + APIResponse, + APIEncoder, + APIDecoder, + ParameterUtil, + StatusCode, + JsonAnalization, + APIFormDataEncoder +} from '@ucap-webmessenger/api'; +import { FileUploadItem } from '../../../../ucap-webmessenger-api/src/lib/models/file-upload-item'; + +export interface FileTalkSaveMultiRequest extends APIRequest { + userSeq: number; + deviceType: DeviceType; + token: string; + files: File[]; + fileUploadItems: FileUploadItem[]; + roomSeq?: string; + type?: string; +} + +export interface FileTalkSaveMultiResponse extends APIResponse { + roomSeq?: string; + attachmentSeq?: string; + fileCount?: string; + baseUrl?: string; + fileType?: string; + thumbnailUrls?: string[]; + returnJson?: string; +} + +const fileTalkSaveEncodeMapTemp = { + userSeq: 'p_user_seq', + deviceType: 'p_device_type', + token: 'p_token', + roomSeq: 'p_room_id', + files: 'file[]', + type: 'p_type' +}; + +export const encodeFileTalkSaveMulti: APIFormDataEncoder = ( + req: FileTalkSaveMultiRequest +) => { + const extraParams: any = {}; + extraParams.userSeq = String(req.userSeq); + + return ParameterUtil.encodeFormData( + fileTalkSaveEncodeMapTemp, + req, + extraParams + ); +}; + +export const decodeFileTalkSaveMulti: APIDecoder = ( + res: any +) => { + try { + const json = JsonAnalization.receiveAnalization(res); + const fileTypeDefault = + !json.fileType || json.fileType === '' ? 'b' : json.fileType; + return { + statusCode: json.StatusCode, + roomSeq: json.RoomID, + attachmentSeq: json.AttSEQ, + fileCount: json.FileCount, + fileType: fileTypeDefault, + baseUrl: json.BaseURL, + thumbnailUrls: json.ThumbURL, + returnJson: res + } as FileTalkSaveMultiResponse; + } catch (e) { + return { + statusCode: StatusCode.Fail, + errorMessage: e + } as FileTalkSaveMultiResponse; + } +}; diff --git a/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.html b/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.html new file mode 100644 index 0000000..4895086 --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.html @@ -0,0 +1,44 @@ +
+
+
+ + + + + + +
{{ fileUploadItem.file.name }}
+ +
+ +
+ + +
+
+ +
+
+ {{ 'common.file.dropZoneForUpload' | translate }} +
+
+
+
diff --git a/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.scss b/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.scss new file mode 100644 index 0000000..8d0975c --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.scss @@ -0,0 +1,55 @@ +@mixin ellipsis($row) { + overflow: hidden; + text-overflow: ellipsis; + @if $row == 1 { + display: block; + white-space: nowrap; + word-wrap: normal; + } @else if $row >= 2 { + display: -webkit-box; + -webkit-line-clamp: $row; + -webkit-box-orient: vertical; + word-wrap: break-word; + } +} + +.ucap-file-upload-queue-container { + width: 100%; + height: 100%; + .file-upload-item { + min-width: 200px; + margin: 0 1%; + margin-bottom: 10px; + width: 100%; + border-radius: 3px; + background-color: #f9f9f9; + border: 1px solid #dddddd; + .file-upload-info { + padding: 10px; + svg { + margin-right: 6px; + } + .file-upload-name { + height: 20px; + @include ellipsis(2); + } + } + .file-upload-progress { + padding: 6px 10px; + } + } + .uploadItems { + width: 100%; + font-size: 0.9em; + .msg-guide { + display: flex; + flex: row; + color: #ffffff; + justify-content: center; + align-items: center; + .icon-img { + margin-right: 6px; + } + } + } +} diff --git a/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.ts b/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.ts new file mode 100644 index 0000000..e62da98 --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/file-upload-queue.component.ts @@ -0,0 +1,117 @@ +import { + Component, + OnInit, + Input, + ElementRef, + AfterViewInit +} from '@angular/core'; + +import { NGXLogger } from 'ngx-logger'; +import { FileUploadItem } from '@ucap-webmessenger/api'; + +@Component({ + selector: 'ucap-file-upload-queue', + templateUrl: './file-upload-queue.component.html', + styleUrls: ['./file-upload-queue.component.scss'] +}) +export class FileUploadQueueComponent implements OnInit, AfterViewInit { + @Input() + dropZoneIncludeParent = false; + + fileUploadItems: FileUploadItem[]; + uploadItems: DataTransferItem[]; + + constructor( + private elementRef: ElementRef, + private logger: NGXLogger + ) {} + + ngOnInit() {} + + ngAfterViewInit(): void { + this.changeStyleDisplay(false); + } + + onDragEnter(items: DataTransferItemList): void { + if (!items || 0 === items.length) { + return; + } + + const uploadItems: DataTransferItem[] = []; + // tslint:disable-next-line: prefer-for-of + for (let i = 0; i < items.length; i++) { + uploadItems.push(items[i]); + } + this.uploadItems = [...uploadItems]; + this.changeStyleDisplay(true); + } + + onDragLeave(): void { + this.changeStyleDisplay(false); + } + + onDrop(fileUploadItems: FileUploadItem[]) { + if (!fileUploadItems || 0 === fileUploadItems.length) { + return; + } + this.fileUploadItems = fileUploadItems; + this.uploadItems = undefined; + } + + onFileSelected(fileUploadItems: FileUploadItem[]): void { + if (!fileUploadItems || 0 === fileUploadItems.length) { + return; + } + this.fileUploadItems = fileUploadItems; + this.uploadItems = undefined; + this.changeStyleDisplay(true); + } + + onUploadComplete(): void { + setTimeout(() => { + this.fileUploadItems = undefined; + this.changeStyleDisplay(false); + }, 1000); + } + + isEventInElement(event: DragEvent): boolean { + const rect = this.elementRef.nativeElement.getBoundingClientRect(); + // const rect: DOMRect = this.elementRef.nativeElement.getBoundingClientRect(); + + if ( + event.pageX >= rect.left && + event.pageX <= rect.left + rect.width && + event.pageY >= rect.top && + event.pageY <= rect.top + rect.height + ) { + return true; + } + return false; + } + + private changeStyleDisplay(show: boolean): void { + if (show || (!!this.fileUploadItems && 0 < this.fileUploadItems.length)) { + if (this.dropZoneIncludeParent) { + this.elementRef.nativeElement.parentElement.style.display = ''; + } else { + this.elementRef.nativeElement.style.display = ''; + } + } else { + if (this.dropZoneIncludeParent) { + this.elementRef.nativeElement.parentElement.style.display = 'none'; + } else { + this.elementRef.nativeElement.style.display = 'none'; + } + } + } + + // onClickClear(fileUploadItem: FileUploadItem) { + // this.fileUploadItems = this.fileUploadItems.filter(f => { + // return ( + // f.file.name !== fileUploadItem.file.name && + // f.file.path !== fileUploadItem.file.path + // ); + // }); + // this.filesChange.emit(this.fileUploadItems); + // } +} diff --git a/documents/업무/2월/2째주/묵음파일백업/messages.component.html b/documents/업무/2월/2째주/묵음파일백업/messages.component.html new file mode 100644 index 0000000..06b3d64 --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/messages.component.html @@ -0,0 +1,384 @@ + +
+ + +
+
+ + + + +
+
+
+ {{ getConvertTimer(roomInfoSubject.value.timeRoomInterval) }} + +
+

+ + {{ 'chat.getRoomNameInProgress' | translate }} + + + + + MyTalk + + + {{ _roomUserInfos | ucapTranslate: 'name':',' }} + + + + {{ roomInfoSubject.value.roomName }} + + + {{ getRoomNameByRoomUser(_roomUserInfos) }} + + + + +

+ + + +
+
+ + + +
+
+
+
+ +
+ +
+
+ + + +
+ + + + + + +
+ + +
+
+ + + +
+ +
+
+ + + + + + + +
+ + + + +
+ +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + diff --git a/documents/업무/2월/2째주/묵음파일백업/messages.component.scss b/documents/업무/2월/2째주/묵음파일백업/messages.component.scss new file mode 100644 index 0000000..1960830 --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/messages.component.scss @@ -0,0 +1,233 @@ +@charset 'utf-s'; +:host { + display: flex; + width: 100%; + height: 100%; +} +@mixin ellipsis($row) { + overflow: hidden; + text-overflow: ellipsis; + @if $row == 1 { + display: block; + white-space: nowrap; + word-wrap: normal; + } @else if $row >= 2 { + display: -webkit-box; + -webkit-line-clamp: $row; + -webkit-box-orient: vertical; + word-wrap: break-word; + } +} +.container { + position: relative; + width: 100%; +} +.chat-toolbar { + position: relative; + display: flex; + flex-flow: column; + width: 100%; + height: auto; + align-items: center; + background-color: #ffffff !important; + border-bottom: 1px solid #dddddd; + box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16); + z-index: 1; + padding: 0; + .chat-header { + width: 100%; + align-items: center; + display: flex; + justify-content: space-between; + justify-items: center; + padding: 4px 20px; + .profile-img { + margin-right: 10px; + width: 30px; + height: 30px; + .responsive-chats-button { + display: none; + line-height: normal; + cursor: unset; + &:last-child { + display: block; + padding: 0; + width: 30px; + height: 30px; + border-radius: 50%; + color: #efefef; + font-size: 1rem; + } + } + &.thumbnail-mask { + border-radius: 50%; + overflow: hidden; + img { + width: 50px; + height: auto; + } + } + } + .room-info { + display: flex; + flex-flow: row; + overflow: hidden; + align-items: center; + .room-name { + font-size: 0.94rem; + line-height: normal; + @include ellipsis(1); + } + .room-type { + font-size: 0.9rem; + line-height: normal; + height: 20px; + span { + border-radius: 10px; + padding: 2px 6px; + margin-right: 6px; + font-size: 0.7rem; + } + } + } + .room-option { + margin-left: auto; + margin-right: -10px; + .icon-button { + transform: translateY(-2px); + i { + font-size: 0.9em; + } + } + } + } + .chat-search-frame { + position: relative; + width: 100%; + .chat-search { + margin: 0 4px 4px; + } + } +} + +.chat-content { + position: relative; + background: transparent; + overflow: auto; + -webkit-overflow-scrolling: touch; + + .file-drop-zone-container { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: transparent; + + .file-drop-zone { + position: absolute; + padding: 10px; + background-color: rgb(54, 54, 54, 0.8); + bottom: 0; + width: 100%; + } + } + + .sticker-selector-container { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + background-color: transparent; + + .sticker-selector-zone { + position: absolute; + padding: 10px 10px 0 10px; + background-color: rgba(250, 255, 255, 0.8); + bottom: 0; + width: 100%; + } + } +} +.translation-container { + .translation-section { + display: flex; + flex-flow: column; + border-top: 1px solid #dddddd; + .translation-container { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + background-color: transparent; + background-color: rgba(250, 255, 255, 0.8); + border-top: 1px solid; + + .translation-zone { + position: absolute; + padding: 10px 10px 0 10px; + background-color: rgba(250, 255, 255, 0.8); + bottom: 0; + width: 100%; + } + } + } +} +//mat-snack-bar +::ng-deep .cdk-global-overlay-wrapper { + .mat-snack-bar-container { + margin: 0; + padding: 30px; + max-width: 60vw; + .mat-simple-snackbar { + display: flex; + justify-content: center; + span { + @include ellipsis(3); + display: inline-block; + padding: 7px 20px; + border: 1px solid #ffffff; + background-color: rgb(255, 255, 255, 0.2); + color: #ffffff; + margin-right: 4px; + flex: 1 1 auto; + max-width: 40vw; + } + &-action { + display: inline-flex; + margin-left: auto; + flex: 0 0 auto; + height: 100%; + button { + //background-color: #00b6d5; + border-radius: 2px; + span { + padding: 0 20px; + color: #ffffff; + background: none; + border: none; + font-weight: 500; + } + } + } + } + } +} + +::ng-deep .chat-header { + .profile-img { + .chat-timer { + .mat-button-wrapper { + display: flex; + justify-content: center; + justify-items: center; + .mat-icon { + line-height: normal; + color: #ffffff; + font-size: 20px; + transform: translateY(1px); + } + } + } + } +} diff --git a/documents/업무/2월/2째주/묵음파일백업/messages.component.ts b/documents/업무/2월/2째주/묵음파일백업/messages.component.ts new file mode 100644 index 0000000..fb0e95f --- /dev/null +++ b/documents/업무/2월/2째주/묵음파일백업/messages.component.ts @@ -0,0 +1,2086 @@ +import { + Component, + OnInit, + OnDestroy, + ViewChild, + AfterViewInit, + Output, + EventEmitter, + Inject, + ChangeDetectorRef, + ChangeDetectionStrategy +} from '@angular/core'; +import { + ucapAnimations, + SnackBarService, + ClipboardService, + DialogService, + ConfirmDialogComponent, + ConfirmDialogData, + ConfirmDialogResult, + AlertDialogComponent, + AlertDialogData, + AlertDialogResult, + FileUploadQueueComponent, + StringUtil +} from '@ucap-webmessenger/ui'; +import { Store, select } from '@ngrx/store'; +import { NGXLogger } from 'ngx-logger'; +import { Observable, Subscription, forkJoin, of, BehaviorSubject } from 'rxjs'; + +import { + Info, + EventType, + isRecalled, + isCopyable, + isRecallable, + isForwardable, + InfoResponse, + EventJson, + FileEventJson, + StickerEventJson, + MassTextEventJson, + TranslationEventJson, + MassTranslationEventJson +} from '@ucap-webmessenger/protocol-event'; + +import * as AppStore from '@app/store'; +import * as EventStore from '@app/store/messenger/event'; +import * as ChatStore from '@app/store/messenger/chat'; +import * as RoomStore from '@app/store/messenger/room'; +import * as SyncStore from '@app/store/messenger/sync'; +import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; +import { + SessionStorageService, + LocalStorageService +} from '@ucap-webmessenger/web-storage'; +import { + EnvironmentsInfo, + KEY_ENVIRONMENTS_INFO, + UserSelectDialogType, + RightDrawer, + KEY_STICKER_HISTORY +} from '@app/types'; +import { + RoomInfo, + UserInfo, + RoomType, + UserInfoShort +} from '@ucap-webmessenger/protocol-room'; +import { take, map, catchError } from 'rxjs/operators'; +import { + FormComponent as UCapUiChatFormComponent, + MessagesComponent as UCapUiChatMessagesComponent +} from '@ucap-webmessenger/ui-chat'; +import { KEY_VER_INFO } from '@app/types'; +import { VersionInfo2Response } from '@ucap-webmessenger/api-public'; +import { + MatMenuTrigger, + MatSnackBarRef, + SimpleSnackBar +} from '@angular/material'; +import { FileUploadItem, FileDownloadItem } from '@ucap-webmessenger/api'; +import { + CommonApiService, + FileTalkSaveRequest, + FileTalkSaveResponse, + FileTalkSaveMultiRequest +} from '@ucap-webmessenger/api-common'; +import { + CreateChatDialogComponent, + CreateChatDialogData, + CreateChatDialogResult +} from '../dialogs/chat/create-chat.dialog.component'; +import { + FileViewerDialogComponent, + FileViewerDialogData, + FileViewerDialogResult +} from '@app/layouts/common/dialogs/file-viewer.dialog.component'; +import { FileUtil, StickerFilesInfo } from '@ucap-webmessenger/core'; + +import { StatusCode } from '@ucap-webmessenger/api'; +import { + EditChatRoomDialogComponent, + EditChatRoomDialogResult, + EditChatRoomDialogData +} from '../dialogs/chat/edit-chat-room.dialog.component'; +import { + SelectGroupDialogComponent, + SelectGroupDialogResult, + SelectGroupDialogData +} from '../dialogs/group/select-group.dialog.component'; +import { GroupDetailData } from '@ucap-webmessenger/protocol-sync'; +import { environment } from '../../../../environments/environment'; +import { + MassDetailComponent, + MassDetailDialogData +} from '../dialogs/chat/mass-detail.component'; +import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; +import { TranslateService } from '@ngx-translate/core'; +import { TranslatePipe } from 'projects/ucap-webmessenger-ui/src/lib/pipes/translate.pipe'; +import { TranslateService as UiTranslateService } from '@ucap-webmessenger/ui'; + +import { + ClipboardDialogComponent, + ClipboardDialogData, + ClipboardDialogResult +} from '../dialogs/chat/clipboard.dialog.component'; +import { AppFileService } from '@app/services/file.service'; + +@Component({ + selector: 'app-layout-messenger-messages', + templateUrl: './messages.component.html', + styleUrls: ['./messages.component.scss'], + animations: ucapAnimations, + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit { + @Output() + openProfile = new EventEmitter<{ + userSeq: number; + }>(); + @Output() + closeRightDrawer = new EventEmitter(); + + @ViewChild('chatForm', { static: false }) + private chatForm: UCapUiChatFormComponent; + + @ViewChild('messageContextMenuTrigger', { static: true }) + messageContextMenuTrigger: MatMenuTrigger; + messageContextMenuPosition = { x: '0px', y: '0px' }; + + @ViewChild('fileUploadQueue', { static: true }) + fileUploadQueue: FileUploadQueueComponent; + + @ViewChild('chatMessages', { static: true }) + chatMessages: UCapUiChatMessagesComponent; + + environmentsInfo: EnvironmentsInfo; + + loginResSubscription: Subscription; + loginResSubject = new BehaviorSubject(undefined); + + roomInfoSubscription: Subscription; + roomInfoSubject = new BehaviorSubject(undefined); + + userInfoListSubscription: Subscription; + userInfoListSubject = new BehaviorSubject(undefined); + + eventListSubscription: Subscription; + eventListSubject = new BehaviorSubject[]>(undefined); + eventListNewSubject = new BehaviorSubject[]>(undefined); + + eventInfoStatusSubscription: Subscription; + eventInfoStatusSubject = new BehaviorSubject(undefined); + + eventRemainedSubscription: Subscription; + eventRemainedSubject = new BehaviorSubject(false); // 이전대화가 남아 있는지 여부 + + lockSubject = new BehaviorSubject(false); + + sessionVerInfo: VersionInfo2Response; + + baseEventSeq = 0; + eventListProcessing$: Observable; + searchEventListProcessing: boolean; + searchEventListProcessingSubscription: Subscription; + + isRecalledMessage = isRecalled; + isCopyableMessage = isCopyable; + isRecallableMessage = isRecallable; + isForwardableMessage = isForwardable; + + /** Timer 대화방의 대화 삭제를 위한 interval */ + interval: any; + + /** About Searching */ + isShowSearchArea = false; + moreSearchProcessing = false; + searchText = ''; + searchedListSubject = new BehaviorSubject[]>(undefined); + searchedFocusEvent: Info; + searchTotalCount = 0; + searchCurrentIndex = 0; + + /** About Sticker */ + isShowStickerSelector = false; + selectedSticker: StickerFilesInfo; + + /** 번역기능 비활성화 2020-02-07 + * About Translation + **/ + // isTranslationProcess = false; + // isShowTranslation = false; + // translationSimpleview = false; + // translationPreview = false; + // destLocale = 'en'; // default English :: en + // translationPreviewInfo: { + // previewInfo: TranslationSaveResponse | null; + // translationType: EventType.Translation | EventType.MassTranslation; + // }; + + /** About ReadHere */ + firstCheckReadHere = true; + clearReadHere = false; + initRoomLastEventSeq: number; + + snackBarPreviewEvent: MatSnackBarRef; + + RoomType = RoomType; + environment = environment; + + constructor( + private store: Store, + private sessionStorageService: SessionStorageService, + private localStorageService: LocalStorageService, + private commonApiService: CommonApiService, + private clipboardService: ClipboardService, + private uiTranslateService: UiTranslateService, + private translateService: TranslateService, + private changeDetectorRef: ChangeDetectorRef, + private dialogService: DialogService, + private snackBarService: SnackBarService, + @Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService, + private appFileService: AppFileService, + private logger: NGXLogger + ) { + this.sessionVerInfo = this.sessionStorageService.get( + KEY_VER_INFO + ); + + this.environmentsInfo = this.sessionStorageService.get( + KEY_ENVIRONMENTS_INFO + ); + } + + ngOnInit() { + this.loginResSubscription = this.store + .pipe(select(AppStore.AccountSelector.AuthenticationSelector.loginRes)) + .subscribe(loginRes => { + this.loginResSubject.next(loginRes); + }); + + this.roomInfoSubscription = this.store + .pipe(select(AppStore.MessengerSelector.RoomSelector.roomInfo)) + .subscribe(roomInfo => { + if ( + !this.roomInfoSubject.value || + (!!this.roomInfoSubject.value && + !!roomInfo && + this.roomInfoSubject.value.roomSeq !== roomInfo.roomSeq) + ) { + this.clearView(); + + if (!!this.roomInfoSubject.value && !!this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + + if ( + !!this.roomInfoSubject.value && + !!this.roomInfoSubject.value.isTimeRoom + ) { + this.interval = setInterval(() => { + this.store.dispatch(EventStore.infoIntervalClear({})); + }, 1000); + } + + this.readyToReply(); + } + + this.roomInfoSubject.next(roomInfo); + }); + + this.userInfoListSubscription = this.store + .pipe(select(AppStore.MessengerSelector.RoomSelector.selectUserinfolist)) + .subscribe(userInfoList => { + this.userInfoListSubject.next(userInfoList); + }); + + this.eventListProcessing$ = this.store.pipe( + select(AppStore.MessengerSelector.EventSelector.infoListProcessing) + ); + + this.searchEventListProcessingSubscription = this.store + .pipe( + select( + AppStore.MessengerSelector.EventSelector.infoSearchListProcessing + ) + ) + .subscribe(process => { + this.lockSubject.next(process); + this.searchEventListProcessing = process; + if (!process && this.isShowSearchArea) { + this.doSearchTextInEvent(this.searchText); + this.snackBarService.open( + this.translateService.instant('chat.searchEventByTextEnd'), + this.translateService.instant('common.messages.confirm'), + { + duration: 3000, + verticalPosition: 'top', + horizontalPosition: 'center' + } + ); + } + }); + + this.eventRemainedSubscription = this.store + .pipe(select(AppStore.MessengerSelector.EventSelector.remainInfo)) + .subscribe(remained => { + this.eventRemainedSubject.next(remained); + }); + + // [Daesang] + this.eventListSubscription = this.store + .pipe(select(AppStore.MessengerSelector.EventSelector.selectAllInfoList)) + .subscribe(infoList => { + if ( + !!this.eventListSubject.value && + this.eventListSubject.value.length > 0 + ) { + this.eventListNewSubject.next( + infoList.filter(info => { + if ( + info.seq <= + this.eventListSubject.value[ + this.eventListSubject.value.length - 1 + ].seq + ) { + return false; + } + return true; + }) + ); + + if ( + !!infoList && + infoList.length > 0 && + !!this.roomInfoSubject.value && + !!this.roomInfoSubject.value.lastReadEventSeq && + this.baseEventSeq <= this.roomInfoSubject.value.lastReadEventSeq + ) { + // 조회된 내용중에 read here 가 있을 경우. + this.firstCheckReadHere = false; + } + } + + this.eventListSubject.next(infoList); + + if (this.moreSearchProcessing) { + const baseseq = this.baseEventSeq; + // setTimeout(() => { + // this.doSearchTextInEvent(this.searchText, baseseq); + // }, 800); + this.baseEventSeq = infoList[0].seq; + } else { + if (!!infoList && infoList.length > 0) { + this.baseEventSeq = infoList[0].seq; + } + } + }); + + // // [GROUP] + // this.eventListSubscription = this.store + // .pipe( + // select(AppStore.MessengerSelector.EventSelector.selectAllInfoList), + // tap(infoList => { + // if (!!this.eventList && this.eventList.length > 0) { + // this.eventListNew = infoList.filter(info => { + // if (info.seq <= this.eventList[this.eventList.length - 1].seq) { + // return false; + // } + // return true; + // }); + // } + // this.eventList = infoList; + + // if (!!infoList && infoList.length > 0) { + // this.baseEventSeq = infoList[0].seq; + // this.readyToReply(); + // } + // }) + // ) + // .subscribe(); + + this.eventInfoStatusSubscription = this.store + .pipe(select(AppStore.MessengerSelector.EventSelector.infoStatus)) + .subscribe(res => { + this.eventInfoStatusSubject.next(res); + + if (!!res) { + const elist = this.eventListSubject.value; + if (res.baseSeq === 0 && elist.length > 0) { + this.initRoomLastEventSeq = elist[elist.length - 1].seq; + } + } + }); + } + + ngOnDestroy(): void { + if (!!this.loginResSubscription) { + this.loginResSubscription.unsubscribe(); + } + if (!!this.roomInfoSubscription) { + this.roomInfoSubscription.unsubscribe(); + } + if (!!this.userInfoListSubscription) { + this.userInfoListSubscription.unsubscribe(); + } + if (!!this.eventListSubscription) { + this.eventListSubscription.unsubscribe(); + } + if (!!this.eventInfoStatusSubscription) { + this.eventInfoStatusSubscription.unsubscribe(); + } + if (!!this.eventRemainedSubscription) { + this.eventRemainedSubscription.unsubscribe(); + } + if (!!this.searchEventListProcessingSubscription) { + this.searchEventListProcessingSubscription.unsubscribe(); + } + + if (!!this.interval) { + clearInterval(this.interval); + } + } + + ngAfterViewInit(): void { + // this.readyToReply(); + } + + /** + * 채팅방의 여러 팝업들을 닫아준다. + */ + clearView() { + // Right Drawer closed.. + this.closeRightDrawer.emit(); + + // Sticker Selector Clear.. + this.isShowStickerSelector = false; + this.selectedSticker = undefined; + + // Chat Search Clear.. + this.onCloseSearchArea(); + + // 번역기능 비활성화 2020-02-07 + // Translate Clear.. + // this.isTranslationProcess = false; + // this.isShowTranslation = false; + // this.translationSimpleview = false; + // this.translationPreview = false; + // this.destLocale = 'en'; // default English :: en + // this.translationPreviewInfo = null; + + // Read here Clear.. + this.firstCheckReadHere = true; + + // Chat Formfield Clear.. + if (!!this.chatForm) { + this.chatForm.replyInput.nativeElement.value = ''; + } + } + + get _roomUserInfos() { + if (this.roomInfoSubject.value.roomType === RoomType.Single) { + return this.userInfoListSubject.value.filter(roomUserInfo => { + return this.loginResSubject.value.userSeq !== roomUserInfo.seq; + }); + } else { + return this.userInfoListSubject.value + .filter(roomUserInfo => { + return ( + this.loginResSubject.value.userSeq !== roomUserInfo.seq && + roomUserInfo.isJoinRoom + ); + }) + .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); + } + } + + getRoomNameByRoomUser(roomUserInfos: (UserInfo | UserInfoShort)[]) { + let roomName = new TranslatePipe( + this.uiTranslateService, + this.changeDetectorRef + ).transform(roomUserInfos, 'name', ','); + + if (!roomName || roomName.trim().length === 0) { + roomName = this.translateService.instant('chat.noRoomUser'); + } + return roomName; + } + + /** 대화전송 가능한 방인지 판단 */ + getEnableSend() { + if (!this.roomInfoSubject.value) { + return false; + } + + if ( + [ + RoomType.Bot, + RoomType.Allim, + RoomType.Link, + RoomType.Allim_Elephant, + RoomType.Allim_TMS + ].some(v => v === this.roomInfoSubject.value.roomType) + ) { + return false; + } + + return true; + } + + getConvertTimer(timerInterval: number, unit: number = 1) { + if (timerInterval >= 0 && timerInterval < 60 * unit) { + return Math.floor((timerInterval / 1) * unit) + ' 초'; + } else if (timerInterval >= 60 * unit && timerInterval < 3600 * unit) { + return Math.floor(((timerInterval / 1) * unit) / 60) + ' 분'; + } else if (timerInterval >= 3600 * unit && timerInterval <= 86400 * unit) { + return Math.floor(((timerInterval / 1) * unit) / 60 / 60) + ' 시간'; + } else { + return ''; + } + } + + getShowContextMenu(menuType: string) { + if ( + ['OPEN_ROOM_USER', 'ADD_MEMBER', 'ADD_GROUP', 'EDIT_ROOM'].some( + v => v === menuType + ) + ) { + if ( + !this.roomInfoSubject.value || + !this.roomInfoSubject.value.roomType || + [ + RoomType.Mytalk, + RoomType.Allim, + RoomType.Bot, + RoomType.Link, + RoomType.Allim_Elephant, + RoomType.Allim_TMS + ].some(v => v === this.roomInfoSubject.value.roomType) + ) { + return false; + } + } + + return true; + } + + getShowUnreadCount(): boolean { + if ( + !this.roomInfoSubject.value || + this.roomInfoSubject.value === undefined + ) { + return true; + } + if ( + [ + RoomType.Mytalk, + RoomType.Allim, + RoomType.Bot, + RoomType.Link, + RoomType.Allim_Elephant, + RoomType.Allim_TMS + ].some(v => v === this.roomInfoSubject.value.roomType) + ) { + return false; + } + + return true; + } + + readyToReply(): void { + setTimeout(() => { + this.focusReplyInput(); + }); + } + + focusReplyInput(): void { + setTimeout(() => { + if (!!this.chatForm) { + this.chatForm.focus(); + } + }); + } + + onScrollupMessages(event: any) {} + onYReachStartMessages(event: any) { + // 자동 스크롤이 아닌 버튼 방식으로 변경. + // this.onMoreEvent(this.baseEventSeq); + } + onYReachEndMessages(event: any) { + this.chatMessages.initEventMore(); + if (!!this.snackBarPreviewEvent) { + this.snackBarPreviewEvent.dismiss(); + } + + // // clear readHere object.. 정책상 클리어 하지 않도록 함. + // if (!this.firstCheckReadHere) { + // this.clearReadHere = true; + // } + } + + /** More Event */ + onMoreEvent(seq: number) { + this.store.dispatch( + EventStore.info({ + roomSeq: this.roomInfoSubject.value.roomSeq, + baseSeq: seq, + requestCount: + environment.productConfig.CommonSetting.eventRequestDefaultCount + }) + ); + } + + /** Send Event */ + async onSendMessage(message: string) { + this.chatMessages.initEventMore(); + + if (!this.selectedSticker) { + if (!message || message.trim().length === 0) { + const result = await this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: this.translateService.instant('chat.errors.label'), + message: this.translateService.instant( + 'chat.errors.inputChatMessage' + ) + } + }); + return; + } + } + + // 번역기능 비활성화 2020-02-07 + // if (!!this.isShowTranslation && this.destLocale.trim().length > 0) { + // /** CASE : Translation */ + // // 번역할 대화 없이 스티커만 전송할 경우. + // if (!message || message.trim().length === 0) { + // this.sendMessageOfSticker(message); + // } else { + // this.sendMessageOfTranslate(message); + // } + // } else + + if (!!this.selectedSticker) { + /** CASE : Sticker */ + this.sendMessageOfSticker(message); + } else if ( + message.trim().length > + environment.productConfig.CommonSetting.masstextLength + ) { + /** CASE : MASS TEXT */ + this.sendMessageOfMassText(message); + } else { + /** CASE : Normal Text */ + this.sendMessageOfNormal(message); + } + } + + /** Send Normal message */ + sendMessageOfNormal(message: string) { + this.store.dispatch( + EventStore.send({ + senderSeq: this.loginResSubject.value.userSeq, + req: { + roomSeq: this.roomInfoSubject.value.roomSeq, + eventType: EventType.Character, + sentMessage: StringUtil.escapeHtml(message) + } + }) + ); + } + /** Send Sticker message */ + async sendMessageOfSticker(message: string, isCheck: boolean = true) { + if ( + !!isCheck && + !!message && + message.trim().length > + environment.productConfig.CommonSetting.masstextLength + ) { + const result = await this.dialogService.open< + AlertDialogComponent, + AlertDialogData, + AlertDialogResult + >(AlertDialogComponent, { + width: '360px', + data: { + title: this.translateService.instant('chat.errors.label'), + message: this.translateService.instant( + 'chat.errors.maxLengthOfMassText', + { + maxLength: environment.productConfig.CommonSetting.masstextLength + } + ) + } + }); + return; + } + + const stickerJson: StickerEventJson = { + name: '스티커', + file: this.selectedSticker.index, + chat: !!message ? StringUtil.escapeHtml(message.trim()) : '' + }; + this.store.dispatch( + EventStore.send({ + senderSeq: this.loginResSubject.value.userSeq, + req: { + roomSeq: this.roomInfoSubject.value.roomSeq, + eventType: EventType.Sticker, + sentMessage: JSON.stringify(stickerJson) + } + }) + ); + this.isShowStickerSelector = false; + this.setStickerHistory(this.selectedSticker); + this.selectedSticker = null; + } + /** Send Masstext message */ + sendMessageOfMassText(message: string) { + this.store.dispatch( + EventStore.sendMass({ + senderSeq: this.loginResSubject.value.userSeq, + req: { + roomSeq: this.roomInfoSubject.value.roomSeq, + eventType: EventType.MassText, + // sentMessage: message.replace(/\n/gi, '\r\n') + sentMessage: StringUtil.escapeHtml(message) + } + }) + ); + } + /** + * 번역기능 비활성화 2020-02-07 + * Send Translation message + **/ + // sendMessageOfTranslate(message: string) { + // const destLocale = this.destLocale; + // const original = StringUtil.escapeHtml(message); + // const roomSeq = this.roomInfoSubject.value.roomSeq; + + // if (!!this.isTranslationProcess) { + // return; + // } + + // this.isTranslationProcess = true; + // this.commonApiService + // .translationSave({ + // userSeq: this.loginResSubject.value.userSeq, + // deviceType: this.environmentsInfo.deviceType, + // token: this.loginResSubject.value.tokenString, + // roomSeq, + // original, + // srcLocale: '', + // destLocale + // } as TranslationSaveRequest) + // .pipe( + // take(1), + // map(res => { + // if (res.statusCode === StatusCode.Success) { + // let sentMessage = ''; + // let eventType = EventType.Translation; + // let previewObject: TranslationEventJson | MassTranslationEventJson; + + // if (res.translationSeq > 0) { + // // Mass Text Translation + // previewObject = res; + // sentMessage = res.returnJson; + // eventType = EventType.MassTranslation; + // } else { + // // Normal Text Translation + // previewObject = { + // locale: destLocale, + // original, + // translation: res.translation, + // stickername: '', + // stickerfile: !!this.selectedSticker + // ? this.selectedSticker.index + // : '' + // }; + + // sentMessage = JSON.stringify(previewObject); + // eventType = EventType.Translation; + // } + + // if (!!this.translationPreview) { + // // preview + // this.translationPreviewInfo = { + // previewInfo: res, + // translationType: eventType + // }; + // } else { + // // direct send + // this.store.dispatch( + // EventStore.send({ + // senderSeq: this.loginResSubject.value.userSeq, + // req: { + // roomSeq, + // eventType, + // sentMessage + // } + // }) + // ); + + // if (!!this.translationPreviewInfo) { + // this.translationPreviewInfo = null; + // } + // } + + // if (!!this.selectedSticker) { + // this.isShowStickerSelector = false; + // this.setStickerHistory(this.selectedSticker); + // this.selectedSticker = null; + // } + // } else { + // this.isTranslationProcess = false; + // this.dialogService.open< + // AlertDialogComponent, + // AlertDialogData, + // AlertDialogResult + // >(AlertDialogComponent, { + // width: '360px', + // data: { + // title: '', + // message: this.translateService.instant( + // 'chat.error.translateServerError' + // ) + // } + // }); + // this.logger.error('res', res); + // } + // }), + // catchError(error => { + // this.isTranslationProcess = false; + // this.dialogService.open< + // AlertDialogComponent, + // AlertDialogData, + // AlertDialogResult + // >(AlertDialogComponent, { + // width: '360px', + // data: { + // title: '', + // message: this.translateService.instant( + // 'chat.error.translateServerError' + // ) + // } + // }); + // return of(this.logger.error('error', error)); + // }) + // ) + // .subscribe(() => { + // this.isTranslationProcess = false; + // }); + // } + + onClickReceiveAlarm() { + this.store.dispatch( + RoomStore.updateOnlyAlarm({ roomInfo: this.roomInfoSubject.value }) + ); + } + + /** MassText Detail View */ + onMassDetail(value: number) { + this.store.dispatch( + ChatStore.selectedMassDetail({ + massEventSeq: value + }) + ); + } + + onMassTranslationDetail(params: { + message: Info; + contentsType: string; + }) { + this.commonApiService + .transMassTalkDownload({ + userSeq: this.loginResSubject.value.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginResSubject.value.tokenString, + eventTransSeq: params.message.sentMessageJson.translationSeq.toString() + }) + .pipe(take(1)) + .subscribe(res => { + let contents = ''; + if (res.statusCode === StatusCode.Success) { + contents = + params.contentsType === 'T' ? res.translation : res.original; + } else { + contents = + params.contentsType === 'T' + ? params.message.sentMessageJson.translation + : params.message.sentMessageJson.original; + } + + this.dialogService.open( + MassDetailComponent, + { + disableClose: false, + width: '550px', + data: { + title: this.translateService.instant('chat.detailView'), + contents + } + } + ); + }); + } + + async onFileViewer(fileInfo: FileEventJson) { + const result = await this.dialogService.open< + FileViewerDialogComponent, + FileViewerDialogData, + FileViewerDialogResult + >(FileViewerDialogComponent, { + position: { + top: '50px' + }, + maxWidth: '100vw', + maxHeight: '100vh', + height: 'calc(100% - 50px)', + width: '100%', + hasBackdrop: false, + panelClass: 'app-dialog-full', + data: { + fileInfo, + downloadUrl: this.sessionVerInfo.downloadUrl, + deviceType: this.environmentsInfo.deviceType, + token: this.loginResSubject.value.tokenString, + userSeq: this.loginResSubject.value.userSeq + } + }); + } + + /** File Save, Save As */ + onSave(value: { + fileInfo: FileEventJson; + fileDownloadItem: FileDownloadItem; + type: string; + }) { + this.logger.debug('fileSave', value); + if (value.type === 'saveAs') { + this.nativeService + .selectSaveFilePath(value.fileInfo.fileName) + .then(result => { + if (!result) { + return; + } + + if (result.canceled) { + // this.snackBarService.open( + // this.translateService.instant('common.file.results.canceled'), + // this.translateService.instant('common.file.errors.label'), + // { + // duration: 1000 + // } + // ); + } else { + this.saveFile(value, result.filePath); + } + }) + .catch(reason => { + this.snackBarService.open( + this.translateService.instant( + 'common.file.errors.failToSpecifyPath' + ), + this.translateService.instant('common.file.errors.label') + ); + }); + } else { + this.saveFile(value); + } + } + + onFileDragEnter(items: DataTransferItemList) { + this.clearView(); + this.logger.debug('onFileDragEnter', items); + } + + onFileDragOver() { + this.logger.debug('onFileDragOver'); + } + + onFileDragLeave() { + this.logger.debug('onFileDragLeave'); + } + + onExistNewMessage(info: Info) { + let message = ''; + + const contents = StringUtil.convertFinalEventMessage( + info.type, + info.sentMessageJson || info.sentMessage + ); + + if (!!contents) { + const senderUser = this.userInfoListSubject.value.filter( + user => user.seq === info.senderSeq + ); + if (!!senderUser && senderUser.length > 0) { + message += `${senderUser[0].name} : `; + } + message += contents; + + this.snackBarPreviewEvent = this.snackBarService.open( + message, + this.translateService.instant('common.messages.confirm'), + { + // duration: 3000, + verticalPosition: 'bottom', + horizontalPosition: 'center', + panelClass: ['chat-snackbar-class'] + } + ); + this.snackBarPreviewEvent.onAction().subscribe(() => { + this.chatMessages.initEventMore(); + this.chatMessages.scrollToBottom(); + this.snackBarPreviewEvent.dismiss(); + }); + } + } + + saveFile( + value: { + fileInfo: FileEventJson; + fileDownloadItem: FileDownloadItem; + type: string; + }, + savePath?: string + ) { + this.appFileService.fileTalkDownlod({ + req: { + userSeq: this.loginResSubject.value.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginResSubject.value.tokenString, + attachmentsSeq: value.fileInfo.attachmentSeq, + fileDownloadItem: value.fileDownloadItem + }, + fileName: value.fileInfo.fileName, + savePath + }); + } + + async onFileSelected(fileUploadItems: FileUploadItem[]) { + this.logger.debug('onFileSelected', fileUploadItems); + this.clearView(); + + const info = { + senderSeq: this.loginResSubject.value.userSeq, + roomSeq: this.roomInfoSubject.value.roomSeq + }; + + const allObservables: Observable[] = []; + + const fileAllowSize = + !!this.sessionVerInfo && this.sessionVerInfo.fileAllowSize + ? this.sessionVerInfo.fileAllowSize + : environment.productConfig.CommonSetting.defaultFileAllowSize; + + if (fileAllowSize > 0) { + if ( + fileUploadItems.filter( + fui => fui.file.size > fileAllowSize * 1024 * 1024 + ).length + ) { + this.snackBarService.open( + this.translateService.instant('common.file.errors.oversize', { + maxSize: fileAllowSize + }), + '', + { + duration: 1000, + verticalPosition: 'bottom', + horizontalPosition: 'center' + } + ); + + if (!!this.fileUploadQueue) { + this.fileUploadQueue.onUploadComplete(); + } + return; + } + } + + const checkExt = this.commonApiService.acceptableExtensionForFileTalk( + fileUploadItems.map(fui => FileUtil.getExtension(fui.file.name)) + ); + if (!checkExt.accept) { + if (!!this.fileUploadQueue) { + this.fileUploadQueue.onUploadComplete(); + } + + this.snackBarService.open( + this.translateService.instant('common.file.errors.notSupporedType', { + supporedType: + checkExt.reject.length > 0 ? checkExt.reject.join(',') : '' + }), + '', + { + duration: 1000, + verticalPosition: 'bottom', + horizontalPosition: 'center' + } + ); + + return; + } + // 멀티 파일 업로드 + const isFileTalkSaveMulti = this.getSaveFileMulti(fileUploadItems); + + if (!!isFileTalkSaveMulti) { + this.fileTalkSaveMulti(fileUploadItems); + return; + } + // 일반 업로드 + for (const fileUploadItem of fileUploadItems) { + let thumbnail: File; + if ( + -1 !== + [ + '3gp', + 'avi', + 'm4v', + 'mkv', + 'mov', + 'mp4', + 'mpeg', + 'mpg', + 'rv', + 'ts', + 'webm', + 'wmv' + ].indexOf(FileUtil.getExtension(fileUploadItem.file.name)) + ) { + thumbnail = await FileUtil.thumbnail(fileUploadItem.file); + this.logger.debug('thumbnail', thumbnail); + } + + const req: FileTalkSaveRequest = { + userSeq: this.loginResSubject.value.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginResSubject.value.tokenString, + roomSeq: this.roomInfoSubject.value.roomSeq, + file: fileUploadItem.file, + fileName: fileUploadItem.file.name, + thumb: thumbnail, + fileUploadItem + }; + + allObservables.push( + this.commonApiService.fileTalkSave(req, null).pipe( + map(res => { + if (!res) { + return; + } + if (StatusCode.Success === res.statusCode) { + return res; + } else { + throw res; + } + }) + ) + ); + } + + forkJoin(allObservables) + .pipe(take(1)) + .subscribe( + resList => { + for (const res of resList) { + this.store.dispatch( + EventStore.send({ + senderSeq: info.senderSeq, + req: { + roomSeq: info.roomSeq, + eventType: EventType.File, + sentMessage: res.returnJson + } + }) + ); + } + }, + error => { + this.logger.debug('onFileSelected error', error); + this.snackBarService.open( + this.translateService.instant('common.file.errors.failToUpload'), + this.translateService.instant('common.file.errors.label') + ); + + if (!!this.fileUploadQueue) { + this.fileUploadQueue.onUploadComplete(); + } + }, + () => { + this.fileUploadQueue.onUploadComplete(); + } + ); + } + + private fileTalkSaveMulti(fileUploadItemList: FileUploadItem[]) { + const fileList: File[] = []; + const info = { + senderSeq: this.loginResSubject.value.userSeq, + roomSeq: this.roomInfoSubject.value.roomSeq + }; + for (const fileUploadItem of fileUploadItemList) { + fileList.push(fileUploadItem.file); + } + const req: FileTalkSaveMultiRequest = { + userSeq: this.loginResSubject.value.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginResSubject.value.tokenString, + roomSeq: this.roomInfoSubject.value.roomSeq, + fileUploadItems: fileUploadItemList, + files: fileList, + type: 'b' + }; + + this.commonApiService + .fileTalkSaveMulti(req, null) + .pipe( + map(res => { + if (!res) { + return; + } + if (StatusCode.Success === res.statusCode) { + return res; + } else { + throw res; + } + }) + ) + .subscribe( + res => { + this.logger.debug('fileTalkSaveMulti', res); + + this.store.dispatch( + EventStore.send({ + senderSeq: info.senderSeq, + req: { + roomSeq: info.roomSeq, + eventType: EventType.BundleImage, + sentMessage: res.returnJson + } + }) + ); + }, + error => { + this.logger.debug('onFileSelected error', error); + this.snackBarService.open( + this.translateService.instant('common.file.errors.failToUpload'), + this.translateService.instant('common.file.errors.label') + ); + + if (!!this.fileUploadQueue) { + this.fileUploadQueue.onUploadComplete(); + } + }, + () => { + this.fileUploadQueue.onUploadComplete(); + } + ); + } + + private getSaveFileMulti(fileUploadItems: FileUploadItem[]): boolean { + for (const fileUploadItem of fileUploadItems) { + if ( + fileUploadItems.length <= 1 || + -1 === + ['jpg', 'jpeg', 'jpe', 'png', 'gif'].indexOf( + FileUtil.getExtension(fileUploadItem.file.name) + ) + ) { + return false; + } + } + + return true; + } + + onContextMenuMessage(params: { + event: MouseEvent; + message: Info; + type?: string; + }) { + params.event.preventDefault(); + params.event.stopPropagation(); + + this.messageContextMenuPosition.x = params.event.clientX + 'px'; + this.messageContextMenuPosition.y = params.event.clientY + 'px'; + this.messageContextMenuTrigger.menu.focusFirstItem('mouse'); + this.messageContextMenuTrigger.menuData = { + message: params.message, + loginRes: this.loginResSubject.value, + clicktype: params.type + }; + this.messageContextMenuTrigger.openMenu(); + } + + async onClickMessageContextMenu( + menuType: string, + message: Info, + clicktype?: string + ) { + switch (menuType) { + case 'COPY': + { + switch (message.type) { + case EventType.Character: + { + if ( + this.clipboardService.copyFromContent( + (message as Info).sentMessage + ) + ) { + this.snackBarService.open( + this.translateService.instant( + 'common.clipboard.results.copied' + ), + '', + { + duration: 3000, + verticalPosition: 'top', + horizontalPosition: 'center' + } + ); + } + } + break; + case EventType.MassText: + { + this.commonApiService + .massTalkDownload({ + userSeq: this.loginResSubject.value.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginResSubject.value.tokenString, + eventMassSeq: message.seq + }) + .pipe(take(1)) + .subscribe(res => { + if (this.clipboardService.copyFromContent(res.content)) { + this.snackBarService.open( + this.translateService.instant( + 'common.clipboard.results.copied' + ), + '', + { + duration: 3000, + verticalPosition: 'top', + horizontalPosition: 'center' + } + ); + } + }); + } + break; + case EventType.Translation: + { + let trgtStr = ''; + if (clicktype === 'translation') { + // translation + trgtStr = (message.sentMessageJson as TranslationEventJson) + .translation; + } else { + // original + trgtStr = (message.sentMessageJson as TranslationEventJson) + .original; + } + + if (this.clipboardService.copyFromContent(trgtStr)) { + this.snackBarService.open( + this.translateService.instant( + 'common.clipboard.results.copied' + ), + '', + { + duration: 3000, + verticalPosition: 'top', + horizontalPosition: 'center' + } + ); + } + } + break; + case EventType.MassTranslation: + { + const sentMessageJson: MassTranslationEventJson = message.sentMessageJson as MassTranslationEventJson; + this.commonApiService + .transMassTalkDownload({ + userSeq: this.loginResSubject.value.userSeq, + deviceType: this.environmentsInfo.deviceType, + token: this.loginResSubject.value.tokenString, + eventTransSeq: sentMessageJson.translationSeq.toString() + }) + .pipe(take(1)) + .subscribe(res => { + let contents = ''; + if (res.statusCode === StatusCode.Success) { + contents = + clicktype === 'translation' + ? res.translation + : res.original; + } else { + contents = + clicktype === 'translation' + ? sentMessageJson.translation + : sentMessageJson.original; + } + + if (this.clipboardService.copyFromContent(contents)) { + this.snackBarService.open( + this.translateService.instant( + 'common.clipboard.results.copied' + ), + '', + { + duration: 3000, + verticalPosition: 'top', + horizontalPosition: 'center' + } + ); + } + }); + } + break; + default: + break; + } + } + break; + case 'FORWARD': + { + const result = await this.dialogService.open< + CreateChatDialogComponent, + CreateChatDialogData, + CreateChatDialogResult + >(CreateChatDialogComponent, { + width: '600px', + data: { + type: UserSelectDialogType.MessageForward, + title: this.translateService.instant('chat.forwardEventTo'), + ignoreRoom: [this.roomInfoSubject.value] + } + }); + + if (!!result && !!result.choice && result.choice) { + const userSeqs: number[] = []; + let roomSeq = ''; + if ( + !!result.selectedUserList && + result.selectedUserList.length > 0 + ) { + result.selectedUserList.map(user => userSeqs.push(user.seq)); + } + + if (!!result.selectedRoom) { + roomSeq = result.selectedRoom.roomSeq; + } + + if (userSeqs.length > 0 || roomSeq.trim().length > 0) { + this.store.dispatch( + EventStore.forward({ + senderSeq: this.loginResSubject.value.userSeq, + req: { + roomSeq: '-999', + eventType: message.type, + sentMessage: message.sentMessage + }, + trgtUserSeqs: userSeqs, + trgtRoomSeq: roomSeq + }) + ); + } + } + } + break; + case 'FORWARD_TO_ME': + { + if (this.loginResSubject.value.talkWithMeBotSeq > -1) { + this.store.dispatch( + EventStore.forward({ + senderSeq: this.loginResSubject.value.userSeq, + req: { + roomSeq: '-999', + eventType: message.type, + sentMessage: message.sentMessage + }, + trgtUserSeqs: [this.loginResSubject.value.talkWithMeBotSeq] + }) + ); + } + } + break; + case 'DELETE': + { + const result = await this.dialogService.open< + ConfirmDialogComponent, + ConfirmDialogData, + ConfirmDialogResult + >(ConfirmDialogComponent, { + width: '400px', + data: { + title: this.translateService.instant('chat.removeEvent'), + html: this.translateService.instant('chat.confirmRemoveEvent') + } + }); + + if (!!result && !!result.choice && result.choice) { + this.store.dispatch( + EventStore.del({ + roomSeq: this.roomInfoSubject.value.roomSeq, + eventSeq: message.seq + }) + ); + } + } + break; + case 'RECALL': + { + const result = await this.dialogService.open< + ConfirmDialogComponent, + ConfirmDialogData, + ConfirmDialogResult + >(ConfirmDialogComponent, { + width: '400px', + data: { + title: this.translateService.instant('chat.recallEvent'), + html: this.translateService.instant('chat.confirmRecallEvent') + } + }); + + if (!!result && !!result.choice && result.choice) { + this.store.dispatch( + EventStore.cancel({ + roomSeq: this.roomInfoSubject.value.roomSeq, + eventSeq: message.seq, + deviceType: this.environmentsInfo.deviceType + }) + ); + } + } + break; + default: + break; + } + } + + async onClickContextMenu(menuType: string) { + switch (menuType) { + case 'OPEN_ALBUM_LIST': + { + this.store.dispatch( + ChatStore.selectedRightDrawer({ + req: RightDrawer.AlbumBox + }) + ); + } + break; + case 'OPEN_FILE_LIST': + { + this.store.dispatch( + ChatStore.selectedRightDrawer({ + req: RightDrawer.FileBox + }) + ); + } + break; + case 'CHAT_SEARCH': + { + this.onShowToggleSearchArea(); + } + break; + case 'OPEN_ROOM_USER': + { + this.store.dispatch( + ChatStore.selectedRightDrawer({ + req: RightDrawer.RoomUser + }) + ); + } + break; + case 'ADD_MEMBER': + { + const curRoomUser = this.userInfoListSubject.value.filter( + user => + user.seq !== this.loginResSubject.value.userSeq && user.isJoinRoom + ); + + const result = await this.dialogService.open< + CreateChatDialogComponent, + CreateChatDialogData, + CreateChatDialogResult + >(CreateChatDialogComponent, { + width: '600px', + data: { + type: UserSelectDialogType.EditChatMember, + title: this.translateService.instant('chat.modifyRoomMember'), + curRoomUser + } + }); + + if (!!result && !!result.choice && result.choice) { + if ( + !!result.selectedUserList && + result.selectedUserList.length > 0 && + curRoomUser + .map(user => user.seq) + .sort() + .join('|') === + result.selectedUserList + .map(user => user.seq) + .sort() + .join('|') + ) { + // 변경된 것이 없다면 중지. + return; + } + + // include me here.. + const userSeqs: number[] = this.userInfoListSubject.value + .filter(userInfo => userInfo.isJoinRoom) + .map(userInfo => userInfo.seq); + + if ( + !!result.selectedUserList && + result.selectedUserList.length > 0 + ) { + result.selectedUserList.forEach(user => { + if (userSeqs.indexOf(user.seq) < 0) { + userSeqs.push(user.seq); + } + }); + } + + if (userSeqs.length > 0) { + // include me + const myUserSeq = this.loginResSubject.value.userSeq; + if (!!myUserSeq && userSeqs.indexOf(myUserSeq) < 0) { + userSeqs.push(myUserSeq); + } + + this.store.dispatch( + RoomStore.inviteOrOpen({ + req: { + divCd: 'Invite', + userSeqs + } + }) + ); + } + } + } + break; + case 'ADD_GROUP': + { + const result = await this.dialogService.open< + SelectGroupDialogComponent, + SelectGroupDialogData, + SelectGroupDialogResult + >(SelectGroupDialogComponent, { + width: '600px', + data: { + title: this.translateService.instant('chat.addMemberToGroup') + } + }); + + if (!!result && !!result.choice && result.choice) { + if (!!result.group) { + const oldGroup: GroupDetailData = result.group; + const trgtUserSeq: number[] = []; + result.group.userSeqs.map(seq => trgtUserSeq.push(seq)); + this.userInfoListSubject.value + .filter(v => v.isJoinRoom) + .filter(v => result.group.userSeqs.indexOf(v.seq) < 0) + .forEach(user => { + trgtUserSeq.push(user.seq); + }); + + this.store.dispatch( + SyncStore.updateGroupMember({ + oldGroup, + trgtUserSeq + }) + ); + } + } + } + break; + case 'EDIT_ROOM': + { + const result = await this.dialogService.open< + EditChatRoomDialogComponent, + EditChatRoomDialogData, + EditChatRoomDialogResult + >(EditChatRoomDialogComponent, { + width: '600px', + data: { + title: this.translateService.instant('chat.settingsOfRoom'), + roomInfo: this.roomInfoSubject.value + } + }); + + if (!!result && !!result.choice && result.choice) { + const roomName: string = result.roomName; + const roomNameChangeTarget: string = result.roomNameChangeTarget; + const timeRoomInterval: number = result.timeRoomInterval; + const roomInfo: RoomInfo = result.roomInfo; + + // 방제목 업데이트. + this.store.dispatch( + RoomStore.update({ + req: { + roomSeq: roomInfo.roomSeq, + roomName, + receiveAlarm: roomInfo.receiveAlarm, + syncAll: + roomNameChangeTarget.toUpperCase() === 'ALL' ? true : false + } + }) + ); + + if ( + roomInfo.isTimeRoom && + timeRoomInterval > 0 && + roomInfo.timeRoomInterval !== timeRoomInterval + ) { + this.store.dispatch( + RoomStore.updateTimeRoomInterval({ + roomSeq: roomInfo.roomSeq, + timerInterval: timeRoomInterval + }) + ); + } + } + } + break; + case 'CLOSE_ROOM': + { + this.store.dispatch(ChatStore.clearSelectedRoom()); + } + break; + default: + break; + } + } + + onClickOpenProfile(userSeq: number) { + const roomType = this.roomInfoSubject.value.roomType; + if ( + roomType !== RoomType.Allim && + roomType !== RoomType.Bot && + roomType !== RoomType.Link && + roomType !== RoomType.Allim_Elephant && + roomType !== RoomType.Allim_TMS + ) { + this.openProfile.emit({ userSeq }); + } + } + + /** About Sticker */ + onShowToggleStickerSelector() { + this.isShowStickerSelector = !this.isShowStickerSelector; + if (!this.isShowStickerSelector) { + this.selectedSticker = null; + } + } + onSelectedSticker(stickerInfo: StickerFilesInfo) { + this.selectedSticker = stickerInfo; + } + setStickerHistory(sticker: StickerFilesInfo) { + const history = this.localStorageService.get(KEY_STICKER_HISTORY); + + if (!!history && history.length > 0) { + const stickers: string[] = []; + [sticker.index, ...history.filter(hist => hist !== sticker.index)].map( + (s, i) => { + if (i < 10) { + stickers.push(s); + } + } + ); + + this.localStorageService.set(KEY_STICKER_HISTORY, stickers); + } else { + this.localStorageService.set(KEY_STICKER_HISTORY, [ + sticker.index + ]); + } + } + getStickerHistory(): string[] { + return this.localStorageService.get(KEY_STICKER_HISTORY); + } + + /** About Chat Search */ + onShowToggleSearchArea() { + this.isShowSearchArea = !this.isShowSearchArea; + + if (!this.isShowSearchArea) { + this.searchedListSubject.next([]); + this.searchedFocusEvent = null; + this.searchText = ''; + } + } + onCloseSearchArea() { + this.isShowSearchArea = false; + this.searchedListSubject.next([]); + this.searchedFocusEvent = null; + this.searchText = ''; + + this.moreSearchProcessing = false; + this.searchTotalCount = 0; + this.searchCurrentIndex = 0; + + this.store.dispatch(EventStore.infoForSearchEnd({})); + } + onSearchChat(searchText: string, baseSeq?: number) { + this.searchText = searchText; + + // CASE :: searching text after retrieve All event Infos. + this.store.dispatch( + EventStore.infoAll({ + req: { + roomSeq: this.roomInfoSubject.value.roomSeq, + baseSeq: this.eventListSubject.value[0].seq, + requestCount: + environment.productConfig.CommonSetting.eventRequestDefaultCount * 2 + }, + infoList: undefined + }) + ); + + // this.doSearchTextInEvent(searchText); + } + doSearchTextInEvent(searchText: string, baseSeq?: number): void { + this.searchedListSubject.next( + this.eventListSubject.value.filter(event => { + let contents = ''; + if (event.type === EventType.Character) { + contents = event.sentMessage; + } else if ( + event.type === EventType.Sticker && + !!event.sentMessageJson + ) { + contents = (event.sentMessageJson as StickerEventJson).chat; + } else if (event.type === EventType.File && !!event.sentMessageJson) { + contents = (event.sentMessageJson as FileEventJson).fileName; + } else if ( + event.type === EventType.MassText && + !!event.sentMessageJson + ) { + contents = (event.sentMessageJson as MassTextEventJson).content; + } else if ( + (event.type === EventType.Translation || + event.type === EventType.MassTranslation) && + !!event.sentMessageJson + ) { + contents = (event.sentMessageJson as TranslationEventJson).original; + contents += (event.sentMessageJson as TranslationEventJson) + .translation; + } + + return contents.indexOf(searchText) > -1; + }) + ); + + if ( + !!this.searchedListSubject.value && + this.searchedListSubject.value.length > 0 + ) { + this.searchTotalCount = this.searchedListSubject.value.length; + + if (!!baseSeq && baseSeq > 0) { + this.searchedListSubject.value.forEach((searched, index) => { + if (searched.seq <= baseSeq) { + this.searchCurrentIndex = index + 1; + this.searchedFocusEvent = searched; + } + }); + } else { + this.searchCurrentIndex = this.searchedListSubject.value.length; + this.searchedFocusEvent = this.searchedListSubject.value[ + this.searchedListSubject.value.length - 1 + ]; + } + + this.store.dispatch(EventStore.infoForSearchEnd({})); + + // this.chatMessages.gotoPosition(this.searchedFocusEvent.seq); + this.chatMessages.initEventMore(this.searchedFocusEvent.seq); + } else { + this.searchTotalCount = 0; + this.searchCurrentIndex = 0; + + this.searchedFocusEvent = null; + } + } + onPrevSearch() { + this.searchedListSubject.value.forEach((event, index) => { + if (event.seq === this.searchedFocusEvent.seq && index > 0) { + this.searchCurrentIndex = this.searchCurrentIndex - 1; + this.searchedFocusEvent = this.searchedListSubject.value[index - 1]; + this.chatMessages.gotoPosition(this.searchedFocusEvent.seq); + } + }); + } + onNextSearch() { + // let exist = false; + // this.searchedList.forEach((event, index) => { + // if ( + // event.seq === this.searchedFocusEvent.seq && + // index < this.searchedList.length - 2 && + // !exist + // ) { + // exist = true; + // this.searchCurrentIndex = this.searchCurrentIndex + 1; + // this.searchedFocusEvent = this.searchedList[index + 1]; + // this.goSearchPosition(this.searchedFocusEvent.seq); + // } + // }); + + if (this.searchCurrentIndex < this.searchedListSubject.value.length) { + this.searchedFocusEvent = this.searchedListSubject.value[ + this.searchCurrentIndex + ]; + this.searchCurrentIndex = this.searchCurrentIndex + 1; + this.chatMessages.gotoPosition(this.searchedFocusEvent.seq); + } + } + onSearchAndPrev() { + if ( + !!this.searchText && + this.searchText.trim().length > 0 && + this.eventRemainedSubject.value + ) { + this.moreSearchProcessing = true; + this.chatMessages.storeScrollPosition(); + + // Case :: retrieve event infos step by step until include searchtext in event.. + this.store.dispatch( + EventStore.infoForSearch({ + req: { + roomSeq: this.roomInfoSubject.value.roomSeq, + baseSeq: this.eventListSubject.value[0].seq, + requestCount: + environment.productConfig.CommonSetting.eventRequestDefaultCount + }, + searchText: this.searchText + }) + ); + } + } + + /** + * 번역기능 비활성화 2020-02-07 + * About Translation */ + // onShowToggleTranslation() { + // this.isShowTranslation = !this.isShowTranslation; + // if (!this.isShowTranslation) { + // } + // } + // onChangeTranslationSimpleView(value: boolean) { + // this.translationSimpleview = value; + // } + // onChangeTranslationPreView(value: boolean) { + // this.translationPreview = value; + // } + // onChangeDestLocale(destLocale: string) { + // this.destLocale = destLocale; + // } + // onCancelTranslation() { + // this.isTranslationProcess = false; + // this.translationPreviewInfo = null; + // } + + // onSendTranslationMessage(params: { + // previewInfo: TranslationSaveResponse | null; + // translationType: EventType.Translation | EventType.MassTranslation; + // }) { + // let sentMessage = ''; + // if (params.translationType === EventType.MassTranslation) { + // // Mass Text Translation + // sentMessage = params.previewInfo.returnJson; + // } else { + // // Normal Text Translation + // sentMessage = JSON.stringify({ + // locale: params.previewInfo.destLocale, + // original: params.previewInfo.original, + // translation: params.previewInfo.translation, + // stickername: '', + // stickerfile: !!this.selectedSticker ? this.selectedSticker.index : '' + // }); + // } + + // this.store.dispatch( + // EventStore.send({ + // senderSeq: this.loginResSubject.value.userSeq, + // req: { + // roomSeq: this.roomInfoSubject.value.roomSeq, + // eventType: params.translationType, + // sentMessage + // } + // }) + // ); + + // this.isTranslationProcess = false; + // this.translationPreviewInfo = null; + // } + + onClipboardPaste(event: ClipboardEvent) { + this.nativeService.readFromClipboard().then(async data => { + if (!!data.image && !!data.text) { + const result = await this.dialogService.open< + ClipboardDialogComponent, + ClipboardDialogData, + ClipboardDialogResult + >(ClipboardDialogComponent, { + width: '800px', + maxWidth: '800px', + height: '800px', + minHeight: '800px', + disableClose: false, + data: { + content: data + } + }); + + if (result.selected.text) { + this.onSendMessage(data.text.replace(/\t/g, ' ')); + } + + if (result.selected.image) { + const fileUploadItems = FileUploadItem.fromDataUrls( + 'clipboard', + data.imageDataUrl + ); + + this.onFileSelected(fileUploadItems); + } + } else if (!!data.image && !data.text) { + const fileUploadItems = FileUploadItem.fromDataUrls( + 'clipboard', + data.imageDataUrl + ); + + this.onFileSelected(fileUploadItems); + } else { + const v = this.chatForm.replyInput.nativeElement.value; + const selectionStart = this.chatForm.replyInput.nativeElement + .selectionStart; + const selectionEnd = this.chatForm.replyInput.nativeElement + .selectionEnd; + + const start = v.substr(0, selectionStart); + const end = v.substr(selectionEnd); + + this.chatForm.replyInput.nativeElement.value = `${start}${data.text}${end}`; + + event.preventDefault(); + } + }); + } +} diff --git a/documents/업무/2월/2째주/묶음파일변경사항.txt b/documents/업무/2월/2째주/묶음파일변경사항.txt new file mode 100644 index 0000000..71ae742 --- /dev/null +++ b/documents/업무/2월/2째주/묶음파일변경사항.txt @@ -0,0 +1 @@ +c:\projects\work\next-ucap-messenger\projects\ucap-webmessenger-api-common\src\lib\services\common-api.service.ts \ No newline at end of file diff --git a/weekly-report/주간보고_박병은_2020.0110.pptx b/weekly-report/1월/주간보고_박병은_2020.0110.pptx similarity index 100% rename from weekly-report/주간보고_박병은_2020.0110.pptx rename to weekly-report/1월/주간보고_박병은_2020.0110.pptx diff --git a/weekly-report/주간보고_박병은_2020.0117.pptx b/weekly-report/1월/주간보고_박병은_2020.0117.pptx similarity index 100% rename from weekly-report/주간보고_박병은_2020.0117.pptx rename to weekly-report/1월/주간보고_박병은_2020.0117.pptx diff --git a/weekly-report/주간보고_박병은_2020.0123.pptx b/weekly-report/1월/주간보고_박병은_2020.0123.pptx similarity index 100% rename from weekly-report/주간보고_박병은_2020.0123.pptx rename to weekly-report/1월/주간보고_박병은_2020.0123.pptx diff --git a/weekly-report/주간보고_박병은_2020.0131.pptx b/weekly-report/1월/주간보고_박병은_2020.0131.pptx similarity index 100% rename from weekly-report/주간보고_박병은_2020.0131.pptx rename to weekly-report/1월/주간보고_박병은_2020.0131.pptx diff --git a/weekly-report/주간보고_박병은_2020.0207.pptx b/weekly-report/2월/주간보고_박병은_2020.0207.pptx similarity index 100% rename from weekly-report/주간보고_박병은_2020.0207.pptx rename to weekly-report/2월/주간보고_박병은_2020.0207.pptx diff --git a/weekly-report/2월/주간보고_박병은_2020.0214.pptx b/weekly-report/2월/주간보고_박병은_2020.0214.pptx new file mode 100644 index 0000000..99572b9 Binary files /dev/null and b/weekly-report/2월/주간보고_박병은_2020.0214.pptx differ