write of message is implemented

This commit is contained in:
병준 박 2019-12-04 17:58:59 +09:00
parent 8dbd747f16
commit 4f6b1dd6ca
10 changed files with 296 additions and 81 deletions

View File

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

View File

@ -9,7 +9,7 @@ export class FileUploadItem {
private uploadingProgress: Subject<number>;
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<number> {
this.uploadStartTime = new Date().getTime();
this.uploadingProgress = new Subject<number>();

View File

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

View File

@ -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<VersionInfo2Response>(
KEY_VER_INFO
);
this.environmentsInfo = this.sessionStorageService.get<EnvironmentsInfo>(
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
}
});

View File

@ -5,6 +5,10 @@
</mat-card-title>
</mat-card-header>
<mat-card-content>
<ucap-message-write #messageWrite></ucap-message-write>
<ucap-message-write
#messageWrite
(send)="onSend($event)"
(selectReceiver)="onSelectReceiver($event)"
></ucap-message-write>
</mat-card-content>
</mat-card>

View File

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

View File

@ -1,19 +1,5 @@
<mat-card class="mat-elevation-z0 ucap-message-write">
<form name="messageWriteForm" [formGroup]="messageWriteForm" novalidate>
<mat-form-field class="message-receiver-list">
<mat-chip-list #chipList aria-label="Fruit selection">
<mat-chip *ngFor="let receiver of receiverList">
{{ receiver }}
<span matChipRemove class="mdi mdi-close"></span>
</mat-chip>
<input
matInput
placeholder="수신자"
formControlName="receiverList"
[matChipInputFor]="chipList"
/>
</mat-chip-list>
</mat-form-field>
<mat-form-field class="message-title">
<input matInput formControlName="title" placeholder="제목" />
</mat-form-field>
@ -32,23 +18,68 @@
</mat-list-item>
</mat-list>
</mat-card-content>
<mat-form-field class="message-receiver-list">
<mat-chip-list #chipList aria-label="receiver selection">
<mat-chip
*ngFor="let receiver of receiverList"
removable="true"
(removed)="onRemovedReceiver(receiver)"
>
{{ receiver.name }}
<span matChipRemove class="mdi mdi-close"></span>
</mat-chip>
<input
[matChipInputFor]="chipList"
placeholder="수신자"
readonly
(click)="onClickReceiverList()"
/>
</mat-chip-list>
</mat-form-field>
<mat-card-actions>
<button mat-icon-button aria-label="이미지" (click)="onClickImage()">
<span class="mdi mdi-camera mdi-24px"></span>
</button>
<button
mat-icon-button
aria-label="첨부파일"
(click)="onClickAttachment()"
>
<span class="mdi mdi-attachment mdi-rotate-90 mdi-24px"></span>
</button>
<button mat-stroked-button (click)="onClickCancel()" class="mat-primary">
취소
</button>
<button mat-flat-button (click)="onClickSend()" class="mat-primary">
보내기
</button>
<div class="editor-tools">
<button mat-icon-button aria-label="이미지" (click)="onClickImage()">
<span class="mdi mdi-camera mdi-24px"></span>
</button>
<button
mat-icon-button
aria-label="첨부파일"
(click)="onClickAttachment()"
>
<span class="mdi mdi-attachment mdi-rotate-90 mdi-24px"></span>
</button>
</div>
<div class="editor-actions-spacer"></div>
<div>
<mat-form-field>
<input matInput [matDatepicker]="picker" placeholder="발송일자" />
<mat-datepicker-toggle
matSuffix
[for]="picker"
></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
</div>
<div class="editor-actions-spacer"></div>
<div class="editor-actions">
<button
mat-stroked-button
(click)="onClickCancel()"
class="mat-primary"
>
취소
</button>
<button
mat-flat-button
(click)="onClickSend()"
class="mat-primary"
style="margin-left: 3px;"
>
보내기
</button>
</div>
</mat-card-actions>
</form>
</mat-card>

View File

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

View File

@ -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<Message>();
@Output()
selectReceiver = new EventEmitter<UserInfo[]>();
@ViewChild('editor', { static: true })
editor: ElementRef<HTMLDivElement>;
@ -38,8 +63,9 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
fileInput: ElementRef<HTMLInputElement>;
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
});
}

View File

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