write of message is implemented
This commit is contained in:
parent
8dbd747f16
commit
4f6b1dd6ca
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,7 +18,28 @@
|
|||
</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>
|
||||
<div class="editor-tools">
|
||||
<button mat-icon-button aria-label="이미지" (click)="onClickImage()">
|
||||
<span class="mdi mdi-camera mdi-24px"></span>
|
||||
</button>
|
||||
|
@ -43,12 +50,36 @@
|
|||
>
|
||||
<span class="mdi mdi-attachment mdi-rotate-90 mdi-24px"></span>
|
||||
</button>
|
||||
<button mat-stroked-button (click)="onClickCancel()" class="mat-primary">
|
||||
</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">
|
||||
<button
|
||||
mat-flat-button
|
||||
(click)="onClickSend()"
|
||||
class="mat-primary"
|
||||
style="margin-left: 3px;"
|
||||
>
|
||||
보내기
|
||||
</button>
|
||||
</div>
|
||||
</mat-card-actions>
|
||||
</form>
|
||||
</mat-card>
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue
Block a user