667 lines
17 KiB
TypeScript
Raw Normal View History

2019-12-03 18:59:11 +09:00
import {
Component,
OnInit,
Output,
EventEmitter,
ViewChild,
AfterViewInit,
ChangeDetectorRef,
OnDestroy,
2019-12-11 08:18:32 +09:00
ElementRef,
2020-02-05 17:57:18 +09:00
Input,
NgZone
2019-12-03 18:59:11 +09:00
} from '@angular/core';
import {
ucapAnimations,
DialogService,
AlertDialogResult,
AlertDialogComponent,
2020-02-05 17:57:18 +09:00
AlertDialogData,
SnackBarService,
AlertSnackbarComponent,
AlertSnackbarData
} from '@ucap-webmessenger/ui';
2019-12-03 18:59:11 +09:00
import { NGXLogger } from 'ngx-logger';
2019-12-06 17:53:19 +09:00
import moment from 'moment';
2019-12-03 18:59:11 +09:00
import { FileUtil } from '@ucap-webmessenger/core';
2020-02-06 10:29:48 +09:00
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
2019-12-04 17:58:59 +09:00
import {
ContentType,
CategoryType,
2019-12-11 08:18:32 +09:00
MessageType,
DetailResponse,
2019-12-12 15:11:49 +09:00
DetailReceiver,
DetailContent
2019-12-04 17:58:59 +09:00
} from '@ucap-webmessenger/api-message';
import { FileUploadItem } from '@ucap-webmessenger/api';
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
2019-12-06 17:53:19 +09:00
import {
ScheduleSendDialogComponent,
ScheduleSendDialogData,
ScheduleSendDialogResult
} from '../dialogs/schedule-send.dialog.component';
2019-12-11 08:18:32 +09:00
import { RoleCode } from '@ucap-webmessenger/protocol-authentication';
import { EmployeeType } from '@ucap-webmessenger/protocol-room';
import { TranslateService } from '@ngx-translate/core';
import { CommonApiService } from '@ucap-webmessenger/api-common';
2019-12-04 17:58:59 +09:00
const ATTR_FILE = 'UCAP_ATTR_FILE';
2019-12-03 18:59:11 +09:00
interface Content {
2019-12-04 17:58:59 +09:00
contentType: ContentType;
content: string | File;
2019-12-12 15:11:49 +09:00
resSeq: number;
2019-12-04 17:58:59 +09:00
}
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;
2019-12-03 18:59:11 +09:00
}
2019-12-12 15:11:49 +09:00
export interface MessageModify extends Message {
resSeqList: number[];
reservationTime: string;
msgId: number;
}
2019-12-03 18:59:11 +09:00
@Component({
selector: 'ucap-message-write',
templateUrl: './write.component.html',
styleUrls: ['./write.component.scss'],
animations: ucapAnimations
})
export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
2019-12-11 08:18:32 +09:00
@Input()
set curReceiverList(userInfoList: UserInfo[]) {
if (!!userInfoList && userInfoList.length > 0) {
this.receiverList = userInfoList;
}
}
2019-12-11 08:18:32 +09:00
@Input()
detail?: DetailResponse;
2019-12-12 15:11:49 +09:00
@Input()
detailContents?: string;
@Input()
isModify = false;
@Input()
fileAllowSize: number;
@Input()
sendProcessing = false;
2019-12-11 08:18:32 +09:00
2019-12-04 17:58:59 +09:00
@Output()
2019-12-12 15:11:49 +09:00
send = new EventEmitter<Message | MessageModify>();
2019-12-06 17:53:19 +09:00
@Output()
cancel = new EventEmitter<void>();
2019-12-04 17:58:59 +09:00
@Output()
selectReceiver = new EventEmitter<UserInfo[]>();
2019-12-03 18:59:11 +09:00
@ViewChild('editor', { static: true })
editor: ElementRef<HTMLDivElement>;
@ViewChild('fileInput', { static: true })
fileInput: ElementRef<HTMLInputElement>;
messageWriteForm: FormGroup;
2019-12-12 15:11:49 +09:00
oldAttachmentList: DetailContent[] = [];
2019-12-03 18:59:11 +09:00
attachmentList: File[];
2019-12-04 17:58:59 +09:00
fileUploadItem: FileUploadItem;
receiverList: UserInfo[] = [];
2019-12-09 10:53:25 +09:00
contentLength = 0;
2019-12-03 18:59:11 +09:00
constructor(
private formBuilder: FormBuilder,
2019-12-06 17:53:19 +09:00
private dialogService: DialogService,
2019-12-03 18:59:11 +09:00
private changeDetectorRef: ChangeDetectorRef,
private translateService: TranslateService,
private commonApiService: CommonApiService,
2020-02-05 17:57:18 +09:00
private snackBarService: SnackBarService,
private readonly ngZone: NgZone,
2019-12-03 18:59:11 +09:00
private logger: NGXLogger
) {}
ngOnInit() {
this.messageWriteForm = this.formBuilder.group({
2020-02-06 10:29:48 +09:00
title: ['', [Validators.required]]
2019-12-03 18:59:11 +09:00
});
2019-12-11 08:18:32 +09:00
2019-12-12 15:11:49 +09:00
if (this.isModify) {
2019-12-11 08:18:32 +09:00
if (!!this.detail.msgInfo.title) {
this.messageWriteForm.setValue({ title: this.detail.msgInfo.title });
}
2019-12-12 15:11:49 +09:00
if (!!this.detailContents) {
this.editor.nativeElement.innerHTML = this.detailContents;
this.onInputEditor();
2019-12-11 08:18:32 +09:00
}
2019-12-12 15:11:49 +09:00
this.detail.contents.forEach(content => {
if (content.resType === ContentType.AttachFile) {
this.oldAttachmentList.push(content);
}
});
2019-12-11 08:18:32 +09:00
}
2019-12-03 18:59:11 +09:00
}
ngOnDestroy(): void {}
ngAfterViewInit(): void {}
2019-12-12 15:11:49 +09:00
onClickDeleteOldAttachment(oldAttachment: DetailContent) {
this.oldAttachmentList = this.oldAttachmentList.filter(
detailContent => detailContent.resSeq !== oldAttachment.resSeq
);
}
onClickDelelteAttachment(attachment: File) {
this.attachmentList = this.attachmentList.filter(
attFile => attFile !== attachment
);
}
2019-12-12 15:11:49 +09:00
2019-12-03 18:59:11 +09:00
onClickImage() {
this.fileInput.nativeElement.setAttribute('accept', 'image/*');
2019-12-03 18:59:11 +09:00
this.fileInput.nativeElement.click();
const self = this;
2019-12-03 19:06:29 +09:00
this.fileInput.nativeElement.onchange = async () => {
2019-12-03 18:59:11 +09:00
const fileList: FileList = self.fileInput.nativeElement.files;
if (!this.validUploadFile(fileList)) {
return;
}
2020-02-17 13:12:42 +09:00
this.validUploadFile(fileList).then(async result => {
if (!result) {
return;
} else {
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.setAttribute('style', 'max-height:250px; max-width:250px;');
img[ATTR_FILE] = file;
self.insertNode(img);
const empty = document.createElement('div');
empty.innerHTML = '&nbsp;<br/>';
self.insertNode(empty, true);
}
2019-12-03 18:59:11 +09:00
2020-02-17 13:12:42 +09:00
self.fileInput.nativeElement.value = '';
self.fileInput.nativeElement.onchange = undefined;
2020-02-12 13:00:43 +09:00
2020-02-17 13:12:42 +09:00
self.checkContentLength();
self.changeDetectorRef.detectChanges();
}
});
2019-12-03 18:59:11 +09:00
};
}
onClickAttachment() {
this.fileInput.nativeElement.removeAttribute('accept');
2019-12-03 18:59:11 +09:00
this.fileInput.nativeElement.click();
const self = this;
this.fileInput.nativeElement.onchange = () => {
const fileList: FileList = this.fileInput.nativeElement.files;
if (!this.validUploadFile(fileList)) {
2020-02-05 17:57:18 +09:00
self.fileInput.nativeElement.value = '';
return;
}
2019-12-03 18:59:11 +09:00
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 = '';
};
}
2020-02-17 13:12:42 +09:00
async validUploadFile(fileList: FileList): Promise<boolean> {
let valid = true;
if (this.fileAllowSize > 0) {
for (let i = 0; i < fileList.length; i++) {
const file = fileList.item(i);
if (file.size > this.fileAllowSize * 1024 * 1024) {
valid = false;
break;
}
}
if (!valid) {
2020-02-05 17:57:18 +09:00
this.ngZone.run(() => {
this.snackBarService.open(
this.translateService.instant('common.file.errors.oversize', {
maxSize: this.fileAllowSize
}),
'',
{
duration: 1000,
verticalPosition: 'bottom',
horizontalPosition: 'center'
}
);
});
return valid;
}
}
const checkExt = this.commonApiService.acceptableExtensionForFileTalk(
FileUtil.getExtensions(fileList)
);
2020-02-17 14:39:38 +09:00
if (!!checkExt) {
2020-02-05 17:57:18 +09:00
this.ngZone.run(() => {
this.snackBarService.openFromComponent<
AlertSnackbarComponent,
AlertSnackbarData
>(AlertSnackbarComponent, {
duration: 1000,
verticalPosition: 'bottom',
horizontalPosition: 'center',
data: {
html: this.translateService.instant(
'common.file.errors.notSupporedType',
{
2020-02-17 14:39:38 +09:00
supporedType: checkExt.join(',')
}
)
2020-02-05 17:57:18 +09:00
}
});
});
2020-02-13 15:12:28 +09:00
valid = false;
return valid;
}
2020-02-17 13:12:42 +09:00
const files: File[] = [];
for (let i = 0; i < fileList.length; i++) {
files.push(fileList.item(i));
}
const fakeMedia = await this.commonApiService.checkInvalidMediaMimeForFileTalk(
files
);
if (!fakeMedia.accept) {
this.ngZone.run(() => {
this.snackBarService.openFromComponent<
AlertSnackbarComponent,
AlertSnackbarData
>(AlertSnackbarComponent, {
duration: 1000,
verticalPosition: 'bottom',
horizontalPosition: 'center',
data: {
html: this.translateService.instant(
'common.file.errors.notAcceptableMime',
{
supporedType:
fakeMedia.rejected.length > 0
? fakeMedia.rejected.join(',')
: ''
}
)
}
});
});
valid = false;
return valid;
}
return valid;
}
2019-12-03 18:59:11 +09:00
onPasteEditor(event: ClipboardEvent) {
const text = document.createTextNode(
event.clipboardData.getData('text/plain')
);
this.insertNode(text, true);
2019-12-09 10:53:25 +09:00
this.checkContentLength();
2019-12-03 18:59:11 +09:00
return false;
}
2019-12-09 10:53:25 +09:00
onInputEditor() {
this.checkContentLength();
}
2019-12-04 17:58:59 +09:00
onRemovedReceiver(receiver: UserInfo) {
const index = this.receiverList.indexOf(receiver);
if (index >= 0) {
this.receiverList.splice(index, 1);
}
}
onClickReceiverList() {
this.selectReceiver.emit(this.receiverList);
}
onClickSend() {
2019-12-06 17:53:19 +09:00
this.sendMessage();
}
onClickCancel() {
this.cancel.emit();
}
async onClickSendSchedule() {
2019-12-12 15:11:49 +09:00
const reservationDate = this.isModify
? moment(this.detail.msgInfo.reservationTime)
: undefined;
2019-12-06 17:53:19 +09:00
const result = await this.dialogService.open<
ScheduleSendDialogComponent,
ScheduleSendDialogData,
ScheduleSendDialogResult
>(ScheduleSendDialogComponent, {
width: '600px',
height: '600px',
disableClose: true,
2019-12-12 15:11:49 +09:00
data: {
reservationDate
}
2019-12-06 17:53:19 +09:00
});
if (!!result && !!result.scheduleSendDate) {
this.sendMessage(result.scheduleSendDate);
}
}
2019-12-09 10:53:25 +09:00
private checkContentLength() {
const result = this.parseContent();
if (!result) {
this.contentLength = 0;
2019-12-09 10:53:25 +09:00
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;
}
}
2019-12-06 17:53:19 +09:00
private sendMessage(reservationDate?: moment.Moment) {
2019-12-09 10:53:25 +09:00
const result = this.parseContent();
if (!result) {
return;
}
2019-12-12 15:11:49 +09:00
const { listOrder, textContent, files, resSeqList } = result;
2019-12-09 10:53:25 +09:00
if (!listOrder || 0 === listOrder.length) {
2019-12-04 17:58:59 +09:00
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();
2019-12-12 15:11:49 +09:00
if (!this.isModify) {
const message: Message = {
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
};
this.send.emit(message);
} else {
const message: MessageModify = {
category: CategoryType.General,
type: !!reservationDate ? MessageType.Reservation : MessageType.Send,
msgId: this.detail.msgInfo.msgId,
title,
listOrder,
resSeqList,
textContent,
recvUserList,
reservationTime: !!reservationDate
? `${reservationDate.format('YYYY-MM-DD HH:mm')}:00`
: undefined,
files,
fileUploadItem: this.fileUploadItem
};
this.send.emit(message);
}
2019-12-04 17:58:59 +09:00
}
2019-12-09 10:53:25 +09:00
private parseContent():
| {
listOrder: ContentType[];
textContent: { text: string }[];
files: File[];
2019-12-12 15:11:49 +09:00
resSeqList: number[];
2019-12-09 10:53:25 +09:00
}
| undefined {
const contentList: Content[] = this.generateContent();
if (!contentList || 0 === contentList.length) {
return;
}
const listOrder: ContentType[] = [];
const textContent: { text: string }[] = [];
const files: File[] = [];
2019-12-12 15:11:49 +09:00
const resSeqList: number[] = [];
2019-12-09 10:53:25 +09:00
contentList.forEach(v => {
listOrder.push(v.contentType);
2019-12-12 15:11:49 +09:00
resSeqList.push(v.resSeq);
2019-12-09 10:53:25 +09:00
switch (v.contentType) {
case ContentType.Text:
let content = v.content as string;
if ('\n' === content.charAt(content.length - 1)) {
2019-12-12 15:11:49 +09:00
content = content.substring(0, content.length - 1);
2019-12-09 10:53:25 +09:00
}
textContent.push({ text: content });
break;
case ContentType.Image:
case ContentType.AttachFile:
2019-12-12 15:11:49 +09:00
{
if (v.resSeq === 0) {
files.push(v.content as File);
}
}
2019-12-09 10:53:25 +09:00
break;
default:
break;
}
});
return {
listOrder,
textContent,
2019-12-12 15:11:49 +09:00
files,
resSeqList
2019-12-09 10:53:25 +09:00
};
}
2019-12-04 17:58:59 +09:00
private generateContent(): Content[] {
2019-12-03 18:59:11 +09:00
const contentList: Content[] = [];
this.editor.nativeElement.childNodes.forEach((v, k) => {
this.parseNode(contentList, v);
});
2019-12-12 15:11:49 +09:00
if (!!this.oldAttachmentList && 0 < this.oldAttachmentList.length) {
this.oldAttachmentList.forEach(v => {
contentList.push({
contentType: ContentType.AttachFile,
content: v.resContent,
resSeq: v.resSeq
});
});
}
2019-12-04 17:58:59 +09:00
if (!!this.attachmentList && 0 < this.attachmentList.length) {
this.attachmentList.forEach(v => {
contentList.push({
contentType: ContentType.AttachFile,
2019-12-12 15:11:49 +09:00
content: v,
resSeq: 0
2019-12-04 17:58:59 +09:00
});
});
}
return contentList;
2019-12-03 18:59:11 +09:00
}
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) {
2019-12-04 17:58:59 +09:00
const img: HTMLImageElement = node as HTMLImageElement;
2019-12-12 15:11:49 +09:00
this.appendNode(
contentList,
ContentType.Image,
img[ATTR_FILE],
!!img.getAttribute('id') ? Number(img.getAttribute('id')) : 0
);
2019-12-03 18:59:11 +09:00
} else if ('BR' === node.nodeName) {
2019-12-04 17:58:59 +09:00
this.appendNode(contentList, ContentType.Text, `\n`);
2019-12-03 18:59:11 +09:00
} else {
}
}
}
break;
case Node.TEXT_NODE:
2019-12-04 17:58:59 +09:00
this.appendNode(contentList, ContentType.Text, node.textContent);
2019-12-03 18:59:11 +09:00
break;
default:
break;
}
}
private appendNode(
contentList: Content[],
2019-12-04 17:58:59 +09:00
contentType: ContentType,
2019-12-12 15:11:49 +09:00
content: string | File,
resSeq: number = 0
2019-12-03 18:59:11 +09:00
) {
const prevContent = contentList[contentList.length - 1];
switch (contentType) {
2019-12-04 17:58:59 +09:00
case ContentType.Text:
if (!!prevContent && ContentType.Text === prevContent.contentType) {
2019-12-03 18:59:11 +09:00
prevContent.content = `${prevContent.content}${content}`;
} else {
contentList.push({
2019-12-04 17:58:59 +09:00
contentType: ContentType.Text,
2019-12-12 15:11:49 +09:00
content,
resSeq
2019-12-03 18:59:11 +09:00
});
}
break;
default:
2019-12-12 15:11:49 +09:00
contentList.push({ contentType, content, resSeq });
2019-12-03 18:59:11 +09:00
break;
}
}
private insertNode(node: Node, removeSelected: boolean = false) {
const selection: Selection = document.getSelection();
2020-02-12 13:00:43 +09:00
if (!!selection && !!selection.anchorNode) {
const range: Range = selection.getRangeAt(0);
let inEditor = false;
if (removeSelected) {
// onPaste
selection.empty();
inEditor = true;
} else {
inEditor = this.inEditor(selection.anchorNode);
}
2019-12-03 18:59:11 +09:00
2020-02-12 13:00:43 +09:00
if (inEditor) {
range.insertNode(node);
} else {
this.editor.nativeElement.appendChild(node);
}
} else {
this.editor.nativeElement.appendChild(node);
}
}
private inEditor(el) {
if (!!el.classList) {
let root = false;
(el.classList as DOMTokenList).forEach(className => {
if (className === 'ucap-message-write') {
root = true;
}
});
if (!!root) {
return false;
}
}
if (el.tagName === 'BODY') {
return false;
}
if (
!!el.className &&
el.className === 'ucap-message-write-editor' &&
el.getAttribute('contenteditable')
) {
return true;
} else {
return this.inEditor(el.parentNode);
}
2019-12-03 18:59:11 +09:00
}
}