This commit is contained in:
병준 박 2019-12-02 18:46:14 +09:00
commit 05a52dd1d5
12 changed files with 507 additions and 20 deletions

View File

@ -39,6 +39,11 @@ import { DeviceType } from '@ucap-webmessenger/core';
import { UnreadCountRequest } from 'projects/ucap-webmessenger-api-message/src/lib/apis/unread-count'; import { UnreadCountRequest } from 'projects/ucap-webmessenger-api-message/src/lib/apis/unread-count';
import { map, catchError, tap } from 'rxjs/operators'; import { map, catchError, tap } from 'rxjs/operators';
import { MessageStatusCode } from '@ucap-webmessenger/api'; import { MessageStatusCode } from '@ucap-webmessenger/api';
import {
EditMessageDialogComponent,
EditMessageDialogResult,
EditMessageDialogData
} from '../dialogs/message/edit-message.dialog.component';
export enum MainMenu { export enum MainMenu {
Group = 'GROUP', Group = 'GROUP',
@ -202,6 +207,34 @@ export class LeftSideComponent implements OnInit, OnDestroy {
} }
} }
async onClickNewMessage() {
const result = await this.dialogService.open<
EditMessageDialogComponent,
EditMessageDialogData,
EditMessageDialogResult
>(EditMessageDialogComponent, {
width: '600px',
data: {
loginRes: this.loginRes
}
});
// if (!!result && !!result.choice && result.choice) {
// if (!!result.selectedUserList && result.selectedUserList.length > 0) {
// const userSeqs: number[] = [];
// result.selectedUserList.map(user => userSeqs.push(user.seq));
// if (type === 'NORMAL') {
// this.store.dispatch(ChatStore.openRoom({ userSeqList: userSeqs }));
// } else if (type === 'TIMER') {
// this.store.dispatch(
// ChatStore.openRoom({ userSeqList: userSeqs, isTimeRoom: true })
// );
// }
// }
// }
}
onClickOpenProfile(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) { onClickOpenProfile(userInfo: UserInfo | UserInfoSS | UserInfoF | UserInfoDN) {
this.openProfile.emit(userInfo); this.openProfile.emit(userInfo);
} }
@ -249,16 +282,28 @@ export class LeftSideComponent implements OnInit, OnDestroy {
]; ];
} }
break; break;
// case MainMenu.Organization: case MainMenu.Organization:
// { {
this.fabButtonShow = false;
// } }
// break; break;
// case MainMenu.Call: case MainMenu.Message:
// { {
this.fabButtonShow = true;
// } this.fabButtons = [
// break; {
icon: 'add',
tooltip: 'New Message',
divisionType: 'MESSAGE_NEW'
}
];
}
break;
case MainMenu.Call:
{
this.fabButtonShow = false;
}
break;
default: { default: {
this.fabButtonShow = false; this.fabButtonShow = false;
@ -343,6 +388,12 @@ export class LeftSideComponent implements OnInit, OnDestroy {
this.onClickNewChat('TIMER'); this.onClickNewChat('TIMER');
} }
break; break;
case 'MESSAGE_NEW':
{
this.onClickNewMessage();
}
break;
} }
} }

View File

@ -0,0 +1,32 @@
<mat-card class="confirm-card mat-elevation-z">
<mat-card-header>
<mat-card-title>
쪽지 보내기
</mat-card-title>
</mat-card-header>
<mat-card-content>
새쪽지를 보내요.
<ucap-message-editor></ucap-message-editor>
</mat-card-content>
<mat-card-actions class="button-farm flex-row">
<button mat-stroked-button (click)="onClickTest()" class="mat-primary">
테스트
</button>
<button
mat-stroked-button
(click)="onClickChoice(false)"
class="mat-primary"
>
취소
</button>
<button
mat-flat-button
[disabled]="getBtnValid()"
(click)="onClickChoice(true)"
class="mat-primary"
>
보내기
</button>
</mat-card-actions>
</mat-card>

View File

@ -0,0 +1,27 @@
::ng-deep .mat-card-header-tex {
margin: 0;
}
.confirm-card {
min-width: 500px;
.mat-card-content {
}
.button-farm {
text-align: right;
.mat-primary {
margin-left: 4px;
}
}
}
::ng-deep .mat-mini-fab .mat-button-wrapper {
padding: 0;
}
.contents {
height: 380px;
.thumbnail {
max-width: 250px;
max-height: 250px;
}
}

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EditMessageDialogComponent } from './edit-message.dialog.component';
describe('app::layouts::messenger::EditMessageDialogComponent', () => {
let component: EditMessageDialogComponent;
let fixture: ComponentFixture<EditMessageDialogComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [EditMessageDialogComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditMessageDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,99 @@
import {
Component,
OnInit,
Inject,
ViewChild,
ElementRef
} from '@angular/core';
import {
MatDialogRef,
MAT_DIALOG_DATA,
MatSelectionList,
MatSelectionListChange,
MatDrawer
} from '@angular/material';
import { Observable, combineLatest, of } from 'rxjs';
import { Store, select } from '@ngrx/store';
import { map, catchError, take } from 'rxjs/operators';
import * as AppStore from '@app/store';
import * as SyncStore from '@app/store/messenger/sync';
import {
DialogService,
ConfirmDialogComponent,
ConfirmDialogData,
ConfirmDialogResult,
SnackBarService,
AlertDialogComponent,
AlertDialogResult,
AlertDialogData
} from '@ucap-webmessenger/ui';
import { GroupDetailData, UserInfo } from '@ucap-webmessenger/protocol-sync';
import {
DetailResponse,
MessageType,
DetailContent,
DetailReceiver,
ContentType,
MessageDetailInfo,
MessageApiService,
RetrieveResourceFileRequest,
CancelRequest
} from '@ucap-webmessenger/api-message';
import { DeviceType, MimeUtil, FileUtil } from '@ucap-webmessenger/core';
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { NGXLogger } from 'ngx-logger';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { MessageStatusCode } from '@ucap-webmessenger/api';
import { MessageEditorComponent } from 'projects/ucap-webmessenger-ui/src/lib/components/message-editor.component';
export interface EditMessageDialogData {
loginRes: LoginResponse;
detail?: DetailResponse;
}
// tslint:disable-next-line: no-empty-interface
export interface EditMessageDialogResult {}
export interface DownloadQueueForMessage extends DetailContent {
downloadType: string;
}
@Component({
selector: 'app-layout-messenger-edit-message',
templateUrl: './edit-message.dialog.component.html',
styleUrls: ['./edit-message.dialog.component.scss']
})
export class EditMessageDialogComponent implements OnInit {
@ViewChild(MessageEditorComponent, { static: false })
editor?: MessageEditorComponent;
constructor(
public dialogRef: MatDialogRef<
EditMessageDialogData,
EditMessageDialogResult
>,
@Inject(MAT_DIALOG_DATA) public data: EditMessageDialogData,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private messageApiService: MessageApiService,
private snackBarService: SnackBarService,
private logger: NGXLogger,
private store: Store<any>,
private dialogService: DialogService
) {}
ngOnInit(): void {}
getBtnValid() {
return true;
}
onClickChoice(choice: boolean): void {
this.dialogRef.close();
}
onClickTest() {
console.log(this.editor.getContents());
}
}

View File

@ -1,3 +1,7 @@
import { MessageDetailDialogComponent } from './message-detail.dialog.component'; import { MessageDetailDialogComponent } from './message-detail.dialog.component';
import { EditMessageDialogComponent } from './edit-message.dialog.component';
export const DIALOGS = [MessageDetailDialogComponent]; export const DIALOGS = [
MessageDetailDialogComponent,
EditMessageDialogComponent
];

View File

@ -3,16 +3,10 @@ import {
MatDialogRef, MatDialogRef,
MAT_DIALOG_DATA, MAT_DIALOG_DATA,
MatSelectionList, MatSelectionList,
MatSelectionListChange,
MatDrawer MatDrawer
} from '@angular/material'; } from '@angular/material';
import { Observable, combineLatest, of } from 'rxjs';
import { Store, select } from '@ngrx/store';
import { map, catchError, take } from 'rxjs/operators'; import { map, catchError, take } from 'rxjs/operators';
import * as AppStore from '@app/store';
import * as SyncStore from '@app/store/messenger/sync';
import { import {
DialogService, DialogService,
ConfirmDialogComponent, ConfirmDialogComponent,
@ -23,7 +17,6 @@ import {
AlertDialogResult, AlertDialogResult,
AlertDialogData AlertDialogData
} from '@ucap-webmessenger/ui'; } from '@ucap-webmessenger/ui';
import { GroupDetailData, UserInfo } from '@ucap-webmessenger/protocol-sync';
import { import {
DetailResponse, DetailResponse,
MessageType, MessageType,
@ -40,13 +33,13 @@ import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native'; import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { MessageStatusCode } from '@ucap-webmessenger/api'; import { MessageStatusCode } from '@ucap-webmessenger/api';
import { of } from 'rxjs';
export interface MessageDetailDialogData { export interface MessageDetailDialogData {
detail: DetailResponse; detail: DetailResponse;
loginRes: LoginResponse; loginRes: LoginResponse;
} }
// tslint:disable-next-line: no-empty-interface
export interface MessageDetailDialogResult { export interface MessageDetailDialogResult {
returnType: string; returnType: string;
messageInfo?: MessageDetailInfo; messageInfo?: MessageDetailInfo;
@ -91,7 +84,6 @@ export class MessageDetailDialogComponent implements OnInit {
private messageApiService: MessageApiService, private messageApiService: MessageApiService,
private snackBarService: SnackBarService, private snackBarService: SnackBarService,
private logger: NGXLogger, private logger: NGXLogger,
private store: Store<any>,
private dialogService: DialogService private dialogService: DialogService
) {} ) {}

View File

@ -0,0 +1,25 @@
<div class="toolbar">
<button mat-stroked-button (click)="onClickFileInput('attach')">
<span class="mdi mdi-attachment"></span>
</button>
<button mat-stroked-button (click)="onClickFileInput('image')">
<span class="mdi mdi-image"></span>
</button>
<input
type="file"
#fileInput
style="display: none"
(change)="onChangeFileInput()"
/>
<button mat-stroked-button (click)="test()">
test
</button>
</div>
<div class="container">
<div
#contentArea
id="contentArea"
contentEditable="true"
class="editor"
></div>
</div>

View File

@ -0,0 +1,9 @@
.container {
height: 500px;
overflow: auto;
.editor {
height: 100%;
width: 100%;
}
}

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MessageEditorComponent } from './message-editor.component';
describe('MessageEditorComponent', () => {
let component: MessageEditorComponent;
let fixture: ComponentFixture<MessageEditorComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ MessageEditorComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MessageEditorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,197 @@
import {
Component,
OnInit,
ElementRef,
ViewChild,
AfterViewInit,
HostListener
} from '@angular/core';
@Component({
selector: 'ucap-message-editor',
templateUrl: './message-editor.component.html',
styleUrls: ['./message-editor.component.scss']
})
export class MessageEditorComponent implements OnInit, AfterViewInit {
@ViewChild('fileInput', { static: false })
fileInput: ElementRef<HTMLInputElement>;
@ViewChild('contentArea', { static: true }) contentArea: ElementRef;
attachFileType: string;
fileIndex = 0;
attachFiles: { file: File; idx: number }[] = [];
imageFiles: { file: File; idx: number }[] = [];
constructor() {}
ngOnInit() {}
ngAfterViewInit(): void {
setTimeout(() => {
this.contentArea.nativeElement.focus();
document
.querySelector('#contentArea')
.addEventListener('paste', (event: ClipboardEvent) => {
event.stopPropagation();
event.preventDefault();
const clipboardData = event.clipboardData;
let pastedData = clipboardData.getData('Text');
pastedData = this.convertHtmltoEntity(pastedData);
clipboardData.setData('Text', pastedData);
const div = document.createElement('div');
div.innerHTML = pastedData;
this.attachElementNextFocused(div);
});
document
.querySelector('#contentArea')
.addEventListener('keyup', event => {
if (
document.querySelector('#contentArea').innerHTML === '' ||
document.querySelector('#contentArea').innerHTML === '<br>'
) {
const div = document.createElement('div');
div.innerHTML = '&nbsp;<br>';
document.querySelector('#contentArea').innerHTML = '';
this.attachElementNextFocused(div);
}
});
// init..
if (document.querySelector('#contentArea').innerHTML === '') {
const div = document.createElement('div');
div.innerHTML = '&nbsp;<br>';
this.attachElementNextFocused(div);
}
}, 700);
}
onClickFileInput(type: string) {
this.attachFileType = type;
if (type === 'attach') {
this.fileInput.nativeElement.setAttribute('multiple', 'true');
this.fileInput.nativeElement.setAttribute('accept', '*.*');
} else {
this.fileInput.nativeElement.removeAttribute('multiple');
this.fileInput.nativeElement.setAttribute('accept', 'image/*');
}
this.fileInput.nativeElement.click();
}
onChangeFileInput() {
const files: FileList = this.fileInput.nativeElement.files;
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < files.length; i++) {
if (this.attachFileType === 'attach') {
this.attachFiles.push({ file: files[i], idx: this.fileIndex });
} else {
this.imageFiles.push({ file: files[i], idx: this.fileIndex });
this.addImageInInputField(files[i], this.fileIndex);
}
this.fileIndex++;
}
this.fileInput.nativeElement.value = '';
}
getContents(): string {
return 'this is contents';
}
addImageInInputField(image: File, index: number) {
const reader = new FileReader();
reader.readAsDataURL(image);
reader.onloadend = () => {
const img = document.createElement('img');
img.setAttribute('src', reader.result.toString());
img.setAttribute('data-seq', index.toString());
this.attachElementNextFocused(img);
};
}
attachElementNextFocused(el: HTMLElement) {
if (window.getSelection) {
const sel = window.getSelection();
const selAnchorNode: Node = sel.anchorNode;
const focusedEl = selAnchorNode.parentElement;
if (this.isParentIdCheck(focusedEl, 'contentArea')) {
if (sel.rangeCount) {
const range = sel.getRangeAt(0);
if (selAnchorNode.nodeType === Node.TEXT_NODE) {
const content: string = focusedEl.textContent;
focusedEl.innerText =
content.substr(0, range.startOffset) +
el.textContent +
content.substr(range.endOffset);
} else if (
selAnchorNode.nodeType === Node.ELEMENT_NODE &&
focusedEl.id === 'contentArea'
) {
const selEl = selAnchorNode as HTMLElement;
const content: string = selEl.textContent;
selEl.innerText =
content.substr(0, range.startOffset) +
el.textContent +
content.substr(range.endOffset);
} else {
focusedEl.append(el);
}
} else {
focusedEl.append(el);
}
} else {
this.contentArea.nativeElement.appendChild(el);
}
} else {
this.contentArea.nativeElement.appendChild(el);
}
}
isParentIdCheck(el: HTMLElement, id: string): boolean {
if (el.id === id) {
return true;
} else {
if (el.tagName === 'BODY' || el.tagName === 'body') {
return false;
} else {
return this.isParentIdCheck(el.parentElement, id);
}
}
}
test(): void {
if (!!this.contentArea) {
const els: HTMLCollection = this.contentArea.nativeElement.children;
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < els.length; i++) {
console.log(this.convertEntitytoHtml(els[i].textContent));
}
}
}
convertHtmltoEntity(str: string): string {
return str
.replace(/ /g, '&nbsp; ')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/\n/g, '<br>');
}
convertEntitytoHtml(str: string): string {
return str
.replace(/&nbsp;/g, ' ')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/<br>/g, ' \n');
}
}

View File

@ -53,6 +53,7 @@ import {
import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe'; import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe';
import { LinkyPipe } from './pipes/linky.pipe'; import { LinkyPipe } from './pipes/linky.pipe';
import { StickerSelectorComponent } from './components/sticker-selector.component'; import { StickerSelectorComponent } from './components/sticker-selector.component';
import { MessageEditorComponent } from './components/message-editor.component';
import { MatTabsModule } from '@angular/material'; import { MatTabsModule } from '@angular/material';
const COMPONENTS = [ const COMPONENTS = [
@ -61,6 +62,7 @@ const COMPONENTS = [
FileViewerComponent, FileViewerComponent,
ExpansionPanelComponent, ExpansionPanelComponent,
StickerSelectorComponent, StickerSelectorComponent,
MessageEditorComponent,
BinaryViewerComponent, BinaryViewerComponent,
DocumentViewerComponent, DocumentViewerComponent,