383 lines
9.1 KiB
TypeScript

import {
Component,
OnInit,
Output,
EventEmitter,
ViewChild,
AfterViewInit,
ChangeDetectorRef,
OnDestroy,
ElementRef
} from '@angular/core';
import { ucapAnimations, DialogService } from '@ucap-webmessenger/ui';
import { NGXLogger } from 'ngx-logger';
import moment from 'moment';
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';
import {
ScheduleSendDialogComponent,
ScheduleSendDialogData,
ScheduleSendDialogResult
} from '../dialogs/schedule-send.dialog.component';
const ATTR_FILE = 'UCAP_ATTR_FILE';
interface Content {
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({
selector: 'ucap-message-write',
templateUrl: './write.component.html',
styleUrls: ['./write.component.scss'],
animations: ucapAnimations
})
export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
@Output()
send = new EventEmitter<Message>();
@Output()
cancel = new EventEmitter<void>();
@Output()
selectReceiver = new EventEmitter<UserInfo[]>();
@ViewChild('editor', { static: true })
editor: ElementRef<HTMLDivElement>;
@ViewChild('fileInput', { static: true })
fileInput: ElementRef<HTMLInputElement>;
messageWriteForm: FormGroup;
attachmentList: File[];
fileUploadItem: FileUploadItem;
receiverList: UserInfo[] = [];
contentLength = 0;
constructor(
private formBuilder: FormBuilder,
private dialogService: DialogService,
private changeDetectorRef: ChangeDetectorRef,
private logger: NGXLogger
) {}
ngOnInit() {
this.messageWriteForm = this.formBuilder.group({
title: ['', []]
});
}
ngOnDestroy(): void {}
ngAfterViewInit(): void {}
onClickImage() {
this.fileInput.nativeElement.click();
const self = this;
this.fileInput.nativeElement.onchange = async () => {
const fileList: FileList = self.fileInput.nativeElement.files;
for (let i = 0; i < fileList.length; i++) {
const file = fileList.item(i);
const dataUrl = await FileUtil.fromBlobToDataUrl(file);
const img = document.createElement('img');
img.src = dataUrl as string;
img[ATTR_FILE] = file;
self.insertNode(img);
}
self.fileInput.nativeElement.value = '';
};
}
onClickAttachment() {
this.fileInput.nativeElement.click();
const self = this;
this.fileInput.nativeElement.onchange = () => {
const fileList: FileList = this.fileInput.nativeElement.files;
if (!self.attachmentList) {
self.attachmentList = [];
}
for (let i = 0; i < fileList.length; i++) {
const file = fileList.item(i);
self.attachmentList.push(file);
}
self.changeDetectorRef.detectChanges();
self.fileInput.nativeElement.value = '';
};
}
onPasteEditor(event: ClipboardEvent) {
const text = document.createTextNode(
event.clipboardData.getData('text/plain')
);
this.insertNode(text, true);
this.checkContentLength();
return false;
}
onInputEditor() {
this.checkContentLength();
}
onRemovedReceiver(receiver: UserInfo) {
const index = this.receiverList.indexOf(receiver);
if (index >= 0) {
this.receiverList.splice(index, 1);
}
}
onClickReceiverList() {
this.selectReceiver.emit(this.receiverList);
}
onClickSend() {
this.sendMessage();
}
onClickCancel() {
this.cancel.emit();
}
async onClickSendSchedule() {
const result = await this.dialogService.open<
ScheduleSendDialogComponent,
ScheduleSendDialogData,
ScheduleSendDialogResult
>(ScheduleSendDialogComponent, {
width: '600px',
height: '600px',
disableClose: true,
data: {}
});
if (!!result && !!result.scheduleSendDate) {
this.sendMessage(result.scheduleSendDate);
}
}
private checkContentLength() {
const result = this.parseContent();
if (!result) {
return;
}
const { textContent } = result;
if (!textContent || 0 === textContent.length) {
this.contentLength = 0;
} else {
let len = 0;
textContent.forEach(c => {
len += c.text.length;
});
this.contentLength = len;
}
}
private sendMessage(reservationDate?: moment.Moment) {
const result = this.parseContent();
if (!result) {
return;
}
const { listOrder, textContent, files } = result;
if (!listOrder || 0 === listOrder.length) {
return;
}
const recvUserList: { userSeq: number; userName: string }[] = [];
const title = this.messageWriteForm.get('title').value;
this.receiverList.forEach(v => {
recvUserList.push({
userSeq: v.seq,
userName: v.name
});
});
this.fileUploadItem = FileUploadItem.from();
this.send.emit({
category: CategoryType.General,
type: !!reservationDate ? MessageType.Reservation : MessageType.Send,
title,
listOrder,
textContent,
recvUserList,
reservationTime: !!reservationDate
? `${reservationDate.format('YYYY-MM-DD HH:mm')}:00`
: undefined,
files,
fileUploadItem: this.fileUploadItem
});
}
private parseContent():
| {
listOrder: ContentType[];
textContent: { text: string }[];
files: File[];
}
| undefined {
const contentList: Content[] = this.generateContent();
if (!contentList || 0 === contentList.length) {
return;
}
const listOrder: ContentType[] = [];
const textContent: { text: string }[] = [];
const files: File[] = [];
contentList.forEach(v => {
listOrder.push(v.contentType);
switch (v.contentType) {
case ContentType.Text:
let content = v.content as string;
if ('\n' === content.charAt(content.length - 1)) {
content = content.substring(0, content.length - 2);
}
textContent.push({ text: content });
break;
case ContentType.Image:
case ContentType.AttachFile:
files.push(v.content as File);
break;
default:
break;
}
});
return {
listOrder,
textContent,
files
};
}
private generateContent(): Content[] {
const contentList: Content[] = [];
this.editor.nativeElement.childNodes.forEach((v, k) => {
this.parseNode(contentList, v);
});
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) {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
{
if (0 < node.childNodes.length) {
let prevNode: ChildNode;
node.childNodes.forEach(v => {
if (
!!prevNode &&
'IMG' === prevNode.nodeName &&
'BR' === v.nodeName
) {
prevNode = v;
return;
}
prevNode = v;
this.parseNode(contentList, v);
});
} else {
if ('IMG' === node.nodeName) {
const img: HTMLImageElement = node as HTMLImageElement;
this.appendNode(contentList, ContentType.Image, img[ATTR_FILE]);
} else if ('BR' === node.nodeName) {
this.appendNode(contentList, ContentType.Text, `\n`);
} else {
}
}
}
break;
case Node.TEXT_NODE:
this.appendNode(contentList, ContentType.Text, node.textContent);
break;
default:
break;
}
}
private appendNode(
contentList: Content[],
contentType: ContentType,
content: string | File
) {
const prevContent = contentList[contentList.length - 1];
switch (contentType) {
case ContentType.Text:
if (!!prevContent && ContentType.Text === prevContent.contentType) {
prevContent.content = `${prevContent.content}${content}`;
} else {
contentList.push({
contentType: ContentType.Text,
content
});
}
break;
default:
contentList.push({ contentType, content });
break;
}
}
private insertNode(node: Node, removeSelected: boolean = false) {
const selection: Selection = document.getSelection();
const range: Range = selection.getRangeAt(0);
if (removeSelected) {
selection.empty();
}
range.insertNode(node);
}
}