diff --git a/projects/ucap-webmessenger-api-message/src/lib/apis/send.ts b/projects/ucap-webmessenger-api-message/src/lib/apis/send.ts index 15bde9b9..39d9559f 100644 --- a/projects/ucap-webmessenger-api-message/src/lib/apis/send.ts +++ b/projects/ucap-webmessenger-api-message/src/lib/apis/send.ts @@ -23,14 +23,14 @@ export interface SendRequest extends APIRequest { title: string; titleYn: boolean; listOrder: ContentType[]; - reservationTime: string; - smsYn: boolean; + reservationTime?: string; + smsYn?: boolean; textContent: { text: string }[]; recvUserList: { userSeq: number; userName: string }[]; - files: File[]; - fileUploadItem: FileUploadItem; + files?: File[]; + fileUploadItem?: FileUploadItem; } export interface SendResponse extends MessageAPIResponse {} @@ -72,26 +72,10 @@ export const encodeSend: APIFormDataEncoder = ( extraParams.listOrder = s; } if (!!req.textContent) { - let s = ''; - req.textContent.forEach(v => { - if ('' !== s) { - s = s + `,${JSON.stringify(v)}`; - } else { - s = s + `${JSON.stringify(v)}`; - } - }); - extraParams.textContent = `[${s}]`; + extraParams.textContent = JSON.stringify(req.textContent); } if (!!req.recvUserList) { - let s = ''; - req.recvUserList.forEach(v => { - if ('' !== s) { - s = s + `,${JSON.stringify(v)}`; - } else { - s = s + `${JSON.stringify(v)}`; - } - }); - extraParams.recvUserList = `[${s}]`; + extraParams.recvUserList = JSON.stringify(req.recvUserList); } return ParameterUtil.encodeFormData(sendEncodeMap, req, extraParams); }; diff --git a/projects/ucap-webmessenger-api/src/lib/models/file-upload-item.ts b/projects/ucap-webmessenger-api/src/lib/models/file-upload-item.ts index 0239627b..72e09741 100644 --- a/projects/ucap-webmessenger-api/src/lib/models/file-upload-item.ts +++ b/projects/ucap-webmessenger-api/src/lib/models/file-upload-item.ts @@ -9,7 +9,7 @@ export class FileUploadItem { private uploadingProgress: Subject; private uploadStartTime: number; - private constructor(file: File) { + private constructor(file?: File) { this.file = file; } @@ -24,6 +24,10 @@ export class FileUploadItem { return fileItems; } + static from(): FileUploadItem { + return new FileUploadItem(); + } + uploadStart(): Subject { this.uploadStartTime = new Date().getTime(); this.uploadingProgress = new Subject(); diff --git a/projects/ucap-webmessenger-api/src/lib/utils/parameter.util.ts b/projects/ucap-webmessenger-api/src/lib/utils/parameter.util.ts index a8d646e3..52760d92 100644 --- a/projects/ucap-webmessenger-api/src/lib/utils/parameter.util.ts +++ b/projects/ucap-webmessenger-api/src/lib/utils/parameter.util.ts @@ -56,6 +56,10 @@ export class ParameterUtil { if (!!v) { if (v instanceof File) { formData.append(parameterMap[key], v, v.name); + } else if (Array.isArray(v)) { + v.forEach(f => { + formData.append(parameterMap[key], f, f.name); + }); } else { formData.append(parameterMap[key], v); } diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts index 368186cd..f3bf9f58 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/components/left-side.component.ts @@ -44,6 +44,7 @@ import { MessageWriteDialogResult, MessageWriteDialogData } from '../dialogs/message/message-write.dialog.component'; +import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types'; export enum MainMenu { Group = 'GROUP', @@ -85,6 +86,7 @@ export class LeftSideComponent implements OnInit, OnDestroy { MainMenu = MainMenu; sessionVerinfo: VersionInfo2Response; + environmentsInfo: EnvironmentsInfo; loginRes: LoginResponse; loginResSubscription: Subscription; @@ -98,6 +100,10 @@ export class LeftSideComponent implements OnInit, OnDestroy { this.sessionVerinfo = this.sessionStorageService.get( KEY_VER_INFO ); + + this.environmentsInfo = this.sessionStorageService.get( + KEY_ENVIRONMENTS_INFO + ); } ngOnInit() { @@ -216,7 +222,8 @@ export class LeftSideComponent implements OnInit, OnDestroy { width: '600px', height: '600px', data: { - loginRes: this.loginRes + loginRes: this.loginRes, + environmentsInfo: this.environmentsInfo } }); diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html index ff79b843..8a9c93cd 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.html @@ -5,6 +5,10 @@ - + diff --git a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts index 7b81594b..d24ada79 100644 --- a/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts +++ b/projects/ucap-webmessenger-app/src/app/layouts/messenger/dialogs/message/message-write.dialog.component.ts @@ -12,10 +12,22 @@ import { import { LoginResponse } from '@ucap-webmessenger/protocol-authentication'; import { NGXLogger } from 'ngx-logger'; import { MessageStatusCode } from '@ucap-webmessenger/api'; -import { WriteComponent as UCapMessageWriteComponent } from '@ucap-webmessenger/ui-message'; +import { + WriteComponent as UCapMessageWriteComponent, + Message +} from '@ucap-webmessenger/ui-message'; +import { UserInfo } from '@ucap-webmessenger/protocol-sync'; +import { + CreateChatDialogComponent, + CreateChatDialogData, + CreateChatDialogResult +} from '../chat/create-chat.dialog.component'; +import { UserSelectDialogType, EnvironmentsInfo } from '@app/types'; +import { take } from 'rxjs/operators'; export interface MessageWriteDialogData { loginRes: LoginResponse; + environmentsInfo: EnvironmentsInfo; detail?: DetailResponse; } @@ -49,15 +61,52 @@ export class MessageWriteDialogComponent implements OnInit { ngOnInit(): void {} + async onSelectReceiver(receiverList: UserInfo[]) { + const result = await this.dialogService.open< + CreateChatDialogComponent, + CreateChatDialogData, + CreateChatDialogResult + >(CreateChatDialogComponent, { + width: '600px', + data: { + type: UserSelectDialogType.EditChatMember, + title: '쪽지 수신자 선택', + curRoomUser: receiverList + } + }); + + if (!!result && !!result.choice && result.choice) { + while (receiverList.length) { + receiverList.pop(); + } + result.selectedUserList.forEach(v => { + receiverList.push(v as UserInfo); + }); + } + } + + onSend(message: Message) { + this.messageApiService + .sendMessage({ + ...message, + userSeq: this.data.loginRes.userInfo.seq, + userName: this.data.loginRes.userInfo.name, + deviceType: this.data.environmentsInfo.deviceType, + tokenKey: this.data.loginRes.tokenString, + titleYn: '' !== message.title ? true : false + }) + .pipe(take(1)) + .subscribe( + res => { + this.logger.debug('onSend', res); + }, + error => { + this.logger.debug('onSend', error); + } + ); + } + getBtnValid() { return true; } - - onClickChoice(choice: boolean): void { - this.dialogRef.close(); - } - - onClickTest() { - this.messageWrite.printEditor(); - } } diff --git a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html index f2ab4005..f17172ea 100644 --- a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html +++ b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.html @@ -1,19 +1,5 @@
- - - - {{ receiver }} - - - - - @@ -32,23 +18,68 @@ + + + + + {{ receiver.name }} + + + + + + - - - - +
+ + +
+
+
+ + + + + +
+
+
+ + +
diff --git a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.scss b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.scss index 1fb4ec55..46760a67 100644 --- a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.scss +++ b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.scss @@ -20,5 +20,36 @@ height: 100%; min-height: 270px; } + + [contenteditable] { + transition: padding 0.3s ease-in-out; + } + + [contenteditable]:hover, + [contenteditable]:focus { + padding: 0.25em; + } + + [contenteditable]:hover { + background: #fafafa; + } + + [contenteditable]:focus { + background: #efefef; + } + } + + mat-card-actions { + display: flex; + width: 100%; + + .editor-tools { + padding-left: 0px; + } + .editor-actions-spacer { + flex: 1 1 auto; + } + .editor-actions { + } } } diff --git a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts index 2fc468ec..d4d8ddbb 100644 --- a/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts +++ b/projects/ucap-webmessenger-ui-message/src/lib/components/write.component.ts @@ -1,12 +1,9 @@ import { Component, OnInit, - Input, Output, EventEmitter, ViewChild, - ContentChild, - TemplateRef, AfterViewInit, ChangeDetectorRef, OnDestroy, @@ -18,10 +15,32 @@ import { ucapAnimations } from '@ucap-webmessenger/ui'; import { NGXLogger } from 'ngx-logger'; import { FileUtil } from '@ucap-webmessenger/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; +import { + ContentType, + CategoryType, + MessageType +} from '@ucap-webmessenger/api-message'; +import { FileUploadItem } from '@ucap-webmessenger/api'; +import { UserInfo } from '@ucap-webmessenger/protocol-sync'; + +const ATTR_FILE = 'UCAP_ATTR_FILE'; interface Content { - contentType: 'text' | 'image' | 'attachment'; - content: string; + contentType: ContentType; + content: string | File; +} + +export interface Message { + category: CategoryType; + type: MessageType; + title: string; + listOrder: ContentType[]; + textContent: { text: string }[]; + recvUserList: { userSeq: number; userName: string }[]; + files?: File[]; + fileUploadItem?: FileUploadItem; + reservationTime?: string; + smsYn?: boolean; } @Component({ @@ -31,6 +50,12 @@ interface Content { animations: ucapAnimations }) export class WriteComponent implements OnInit, OnDestroy, AfterViewInit { + @Output() + send = new EventEmitter(); + + @Output() + selectReceiver = new EventEmitter(); + @ViewChild('editor', { static: true }) editor: ElementRef; @@ -38,8 +63,9 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit { fileInput: ElementRef; messageWriteForm: FormGroup; - receiverList: string[] = ['이진호', '강희경', '이유진']; attachmentList: File[]; + fileUploadItem: FileUploadItem; + receiverList: UserInfo[] = []; constructor( private formBuilder: FormBuilder, @@ -70,6 +96,7 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit { const dataUrl = await FileUtil.fromBlobToDataUrl(file); const img = document.createElement('img'); img.src = dataUrl as string; + img[ATTR_FILE] = file; self.insertNode(img); } @@ -107,17 +134,85 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit { return false; } - onClickSend() {} + onRemovedReceiver(receiver: UserInfo) { + const index = this.receiverList.indexOf(receiver); + + if (index >= 0) { + this.receiverList.splice(index, 1); + } + } + + onClickReceiverList() { + this.selectReceiver.emit(this.receiverList); + } + + onClickSend() { + const contentList: Content[] = this.generateContent(); + if (!contentList || 0 === contentList.length) { + return; + } + + const listOrder: ContentType[] = []; + const textContent: { text: string }[] = []; + const recvUserList: { userSeq: number; userName: string }[] = []; + const files: File[] = []; + const title = this.messageWriteForm.get('title').value; + + contentList.forEach(v => { + listOrder.push(v.contentType); + switch (v.contentType) { + case ContentType.Text: + textContent.push({ text: v.content as string }); + break; + case ContentType.Image: + case ContentType.AttachFile: + files.push(v.content as File); + break; + default: + break; + } + }); + + this.receiverList.forEach(v => { + recvUserList.push({ + userSeq: v.seq, + userName: v.name + }); + }); + + this.fileUploadItem = FileUploadItem.from(); + + this.send.emit({ + category: CategoryType.General, + type: MessageType.Send, + title, + listOrder, + textContent, + recvUserList, + files, + fileUploadItem: this.fileUploadItem + }); + } + onClickCancel() {} - printEditor(): void { + private generateContent(): Content[] { const contentList: Content[] = []; this.editor.nativeElement.childNodes.forEach((v, k) => { this.parseNode(contentList, v); }); - this.logger.debug('printEditor', contentList); + if (!!this.attachmentList && 0 < this.attachmentList.length) { + this.attachmentList.forEach(v => { + contentList.push({ + contentType: ContentType.AttachFile, + content: v + }); + }); + } + + return contentList; } private parseNode(contentList: Content[], node: ChildNode) { @@ -140,16 +235,17 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit { }); } else { if ('IMG' === node.nodeName) { - this.appendNode(contentList, 'image', node.textContent); + const img: HTMLImageElement = node as HTMLImageElement; + this.appendNode(contentList, ContentType.Image, img[ATTR_FILE]); } else if ('BR' === node.nodeName) { - this.appendNode(contentList, 'text', `\n`); + this.appendNode(contentList, ContentType.Text, `\n`); } else { } } } break; case Node.TEXT_NODE: - this.appendNode(contentList, 'text', node.textContent); + this.appendNode(contentList, ContentType.Text, node.textContent); break; default: @@ -159,17 +255,17 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit { private appendNode( contentList: Content[], - contentType: 'text' | 'image' | 'attachment', - content: string + contentType: ContentType, + content: string | File ) { const prevContent = contentList[contentList.length - 1]; switch (contentType) { - case 'text': - if (!!prevContent && 'text' === prevContent.contentType) { + case ContentType.Text: + if (!!prevContent && ContentType.Text === prevContent.contentType) { prevContent.content = `${prevContent.content}${content}`; } else { contentList.push({ - contentType: 'text', + contentType: ContentType.Text, content }); } diff --git a/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts b/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts index 24dcf59e..5d6067ad 100644 --- a/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts +++ b/projects/ucap-webmessenger-ui-message/src/lib/ucap-ui-message.module.ts @@ -9,6 +9,7 @@ import { ScrollingModule } from '@angular/cdk/scrolling'; import { MatButtonModule } from '@angular/material/button'; import { MatCardModule } from '@angular/material/card'; import { MatChipsModule } from '@angular/material/chips'; +import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDividerModule } from '@angular/material/divider'; import { MatFormFieldModule } from '@angular/material/form-field'; import { MatGridListModule } from '@angular/material/grid-list'; @@ -16,6 +17,8 @@ import { MatIconModule } from '@angular/material/icon'; import { MatInputModule } from '@angular/material/input'; import { MatListModule } from '@angular/material/list'; +import { MatMomentDateModule } from '@angular/material-moment-adapter'; + import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar'; import { WriteComponent } from './components/write.component'; @@ -36,12 +39,14 @@ const SERVICES = []; MatButtonModule, MatCardModule, MatChipsModule, + MatDatepickerModule, MatDividerModule, MatFormFieldModule, MatGridListModule, MatIconModule, MatInputModule, MatListModule, + MatMomentDateModule, PerfectScrollbarModule ],