message is added

This commit is contained in:
병준 박 2019-12-03 18:59:11 +09:00
parent 05a52dd1d5
commit ccf7ea0092
50 changed files with 900 additions and 363 deletions

View File

@ -729,6 +729,40 @@
}
}
},
"ucap-webmessenger-ui-message": {
"projectType": "library",
"root": "projects/ucap-webmessenger-ui-message",
"sourceRoot": "projects/ucap-webmessenger-ui-message/src",
"prefix": "ucap-message",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ucap-webmessenger-ui-message/tsconfig.lib.json",
"project": "projects/ucap-webmessenger-ui-message/ng-package.json"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ucap-webmessenger-ui-message/src/test.ts",
"tsConfig": "projects/ucap-webmessenger-ui-message/tsconfig.spec.json",
"karmaConfig": "projects/ucap-webmessenger-ui-message/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ucap-webmessenger-ui-message/tsconfig.lib.json",
"projects/ucap-webmessenger-ui-message/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"ucap-webmessenger-ui-settings": {
"projectType": "library",
"root": "projects/ucap-webmessenger-ui-settings",

View File

@ -8,7 +8,7 @@ import {
JsonAnalization,
StatusCode
} from '@ucap-webmessenger/api';
import { FileUploadItem } from '../models/file-upload-item';
import { FileUploadItem } from '../../../../ucap-webmessenger-api/src/lib/models/file-upload-item';
export interface FileProfileSaveRequest extends APIRequest {
userSeq: number;

View File

@ -5,7 +5,7 @@ import {
ParameterUtil,
APIFormDataEncoder
} from '@ucap-webmessenger/api';
import { FileDownloadItem } from '../models/file-download-item';
import { FileDownloadItem } from '../../../../ucap-webmessenger-api/src/lib/models/file-download-item';
export interface FileTalkDownloadRequest extends APIRequest {
userSeq: number;
@ -33,9 +33,9 @@ export const encodeFileTalkDownload: APIEncoder<FileTalkDownloadRequest> = (
return ParameterUtil.encode(fileTalkDownloadEncodeMap, req, extraParams);
};
export const encodeFormDataFileTalkDownload: APIFormDataEncoder<
FileTalkDownloadRequest
> = (req: FileTalkDownloadRequest) => {
export const encodeFormDataFileTalkDownload: APIFormDataEncoder<FileTalkDownloadRequest> = (
req: FileTalkDownloadRequest
) => {
const extraParams: any = {};
extraParams.userSeq = String(req.userSeq);

View File

@ -9,7 +9,7 @@ import {
JsonAnalization,
APIFormDataEncoder
} from '@ucap-webmessenger/api';
import { FileUploadItem } from '../models/file-upload-item';
import { FileUploadItem } from '../../../../ucap-webmessenger-api/src/lib/models/file-upload-item';
export interface FileTalkSaveRequest extends APIRequest {
userSeq: number;

View File

@ -13,9 +13,6 @@ export * from './lib/apis/trans-mass-talk-save';
export * from './lib/apis/translation-req';
export * from './lib/apis/translation-save';
export * from './lib/models/file-download-item';
export * from './lib/models/file-upload-item';
export * from './lib/services/common-api.service';
export * from './lib/ucap-common-api.module';

View File

@ -2,12 +2,15 @@ import {
APIRequest,
MessageAPIResponse,
APIJsonEncoder,
APIDecoder
APIDecoder,
APIFormDataEncoder,
ParameterUtil
} from '@ucap-webmessenger/api';
import { DeviceType } from '@ucap-webmessenger/core';
import { MessageType } from '../types/message.type';
import { CategoryType } from '../types/category.type';
import { ContentType } from '../types/content.type';
import { FileUploadItem } from '@ucap-webmessenger/api';
export interface SendRequest extends APIRequest {
userSeq: number;
@ -25,12 +28,72 @@ export interface SendRequest extends APIRequest {
textContent: { text: string }[];
recvUserList: { userSeq: number; userName: string }[];
files: File[];
fileUploadItem: FileUploadItem;
}
export interface SendResponse extends MessageAPIResponse {}
export const encodeSend: APIJsonEncoder<SendRequest> = (req: SendRequest) => {
return JSON.stringify(req);
const sendEncodeMap = {
userSeq: 'userSeq',
deviceType: 'deviceType',
tokenKey: 'tokenKey',
type: 'type',
userName: 'userName',
category: 'category',
title: 'title',
titleYn: 'titleYn',
listOrder: 'listOrder',
reservationTime: 'reservationTime',
smsYn: 'smsYn',
textContent: 'textContent',
recvUserList: 'recvUserList',
files: 'files'
};
export const encodeSend: APIFormDataEncoder<SendRequest> = (
req: SendRequest
) => {
const extraParams: any = {};
extraParams.userSeq = String(req.userSeq);
if (!!req.titleYn) {
extraParams.titleYn = req.titleYn ? 'Y' : 'N';
}
if (!!req.smsYn) {
extraParams.smsYn = req.smsYn ? 'Y' : 'N';
}
if (!!req.listOrder) {
let s = '';
req.listOrder.forEach(v => {
s = s + String(v);
});
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}]`;
}
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}]`;
}
return ParameterUtil.encodeFormData(sendEncodeMap, req, extraParams);
};
export const decodeSend: APIDecoder<SendResponse> = (res: any) => {

View File

@ -158,11 +158,29 @@ export class MessageApiService {
/** send */
public sendMessage(req: SendRequest): Observable<SendResponse> {
return this.httpClient
.post<any>(this.urls.sendNewMessage, encodeSend(req), {
headers: this.headers
const httpReq = new HttpRequest(
'POST',
this.urls.sendNewMessage,
encodeSend(req),
{ reportProgress: true, responseType: 'text' as 'json' }
);
const progress = req.fileUploadItem.uploadStart();
return this.httpClient.request(httpReq).pipe(
filter(event => {
if (event instanceof HttpResponse) {
return true;
} else if (HttpEventType.UploadProgress === event.type) {
progress.next(Math.round((100 * event.loaded) / event.total));
}
return false;
}),
map((event: HttpResponse<any>) => {
req.fileUploadItem.uploadComplete();
return decodeSend(event.body);
})
.pipe(map(res => decodeSend(res)));
);
}
/** detail */

View File

@ -7,6 +7,9 @@ export * from './lib/apis/api';
export * from './lib/types/message-status-code.type';
export * from './lib/types/status-code.type';
export * from './lib/models/file-download-item';
export * from './lib/models/file-upload-item';
export * from './lib/utils/json.util';
export * from './lib/utils/parameter.util';
export * from './lib/utils/url.util';

View File

@ -3,7 +3,7 @@ import {
OnInit,
OnDestroy,
Inject,
EventEmitter,
EventEmitter
} from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
@ -13,10 +13,8 @@ import { DeviceType, FileUtil, MimeUtil } from '@ucap-webmessenger/core';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { take, map, finalize, tap } from 'rxjs/operators';
import { SnackBarService } from '@ucap-webmessenger/ui';
import {
FileDownloadItem,
CommonApiService,
} from '@ucap-webmessenger/api-common';
import { FileDownloadItem } from '@ucap-webmessenger/api';
import { CommonApiService } from '@ucap-webmessenger/api-common';
export interface FileViewerDialogData {
fileInfo: FileEventJson;
@ -31,7 +29,7 @@ export interface FileViewerDialogResult {}
@Component({
selector: 'app-layout-common-file-viewer',
templateUrl: './file-viewer.dialog.component.html',
styleUrls: ['./file-viewer.dialog.component.scss'],
styleUrls: ['./file-viewer.dialog.component.scss']
})
export class FileViewerDialogComponent implements OnInit, OnDestroy {
fileInfo: FileEventJson;
@ -64,7 +62,7 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
userSeq: this.userSeq,
deviceType: this.deviceType,
token: this.token,
attachmentsSeq: this.fileInfo.attachmentSeq,
attachmentsSeq: this.fileInfo.attachmentSeq
},
this.downloadUrl
);
@ -82,7 +80,7 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
deviceType: this.deviceType,
token: this.token,
attachmentsSeq: this.fileInfo.attachmentSeq,
fileDownloadItem,
fileDownloadItem
},
this.downloadUrl
)
@ -103,7 +101,7 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
'',
{
duration: 3000,
verticalPosition: 'bottom',
verticalPosition: 'bottom'
}
);
} else {

View File

@ -40,10 +40,10 @@ import { UnreadCountRequest } from 'projects/ucap-webmessenger-api-message/src/l
import { map, catchError, tap } from 'rxjs/operators';
import { MessageStatusCode } from '@ucap-webmessenger/api';
import {
EditMessageDialogComponent,
EditMessageDialogResult,
EditMessageDialogData
} from '../dialogs/message/edit-message.dialog.component';
MessageWriteDialogComponent,
MessageWriteDialogResult,
MessageWriteDialogData
} from '../dialogs/message/message-write.dialog.component';
export enum MainMenu {
Group = 'GROUP',
@ -209,11 +209,12 @@ export class LeftSideComponent implements OnInit, OnDestroy {
async onClickNewMessage() {
const result = await this.dialogService.open<
EditMessageDialogComponent,
EditMessageDialogData,
EditMessageDialogResult
>(EditMessageDialogComponent, {
MessageWriteDialogComponent,
MessageWriteDialogData,
MessageWriteDialogResult
>(MessageWriteDialogComponent, {
width: '600px',
height: '600px',
data: {
loginRes: this.loginRes
}

View File

@ -67,9 +67,9 @@ import {
MatSnackBarRef,
SimpleSnackBar
} from '@angular/material';
import { FileUploadItem } from '@ucap-webmessenger/api';
import {
CommonApiService,
FileUploadItem,
FileTalkSaveRequest,
FileTalkSaveResponse
} from '@ucap-webmessenger/api-common';

View File

@ -1,32 +0,0 @@
<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

@ -1,27 +0,0 @@
::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

@ -1,24 +0,0 @@
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

@ -1,99 +0,0 @@
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,7 +1,7 @@
import { MessageDetailDialogComponent } from './message-detail.dialog.component';
import { EditMessageDialogComponent } from './edit-message.dialog.component';
import { MessageWriteDialogComponent } from './message-write.dialog.component';
export const DIALOGS = [
MessageDetailDialogComponent,
EditMessageDialogComponent
MessageWriteDialogComponent
];

View File

@ -0,0 +1,10 @@
<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-write #messageWrite></ucap-message-write>
</mat-card-content>
</mat-card>

View File

@ -0,0 +1,8 @@
.confirm-card {
min-width: 500px;
height: 100%;
mat-card-content {
height: calc(100% - 40px);
}
}

View File

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

View File

@ -0,0 +1,63 @@
import { Component, OnInit, Inject, ViewChild } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { DialogService, SnackBarService } from '@ucap-webmessenger/ui';
import {
DetailResponse,
DetailContent,
MessageApiService
} from '@ucap-webmessenger/api-message';
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';
export interface MessageWriteDialogData {
loginRes: LoginResponse;
detail?: DetailResponse;
}
// tslint:disable-next-line: no-empty-interface
export interface MessageWriteDialogResult {}
export interface DownloadQueueForMessage extends DetailContent {
downloadType: string;
}
@Component({
selector: 'app-layout-messenger-message-write',
templateUrl: './message-write.dialog.component.html',
styleUrls: ['./message-write.dialog.component.scss']
})
export class MessageWriteDialogComponent implements OnInit {
@ViewChild('messageWrite', { static: true })
messageWrite: UCapMessageWriteComponent;
constructor(
public dialogRef: MatDialogRef<
MessageWriteDialogData,
MessageWriteDialogResult
>,
@Inject(MAT_DIALOG_DATA) public data: MessageWriteDialogData,
private messageApiService: MessageApiService,
private snackBarService: SnackBarService,
private logger: NGXLogger,
private dialogService: DialogService
) {}
ngOnInit(): void {}
getBtnValid() {
return true;
}
onClickChoice(choice: boolean): void {
this.dialogRef.close();
}
onClickTest() {
this.messageWrite.printEditor();
}
}

View File

@ -32,10 +32,8 @@ import {
SelectGroupDialogData,
SelectGroupDialogResult
} from '../group/select-group.dialog.component';
import {
FileUploadItem,
CommonApiService
} from '@ucap-webmessenger/api-common';
import { FileUploadItem } from '@ucap-webmessenger/api';
import { CommonApiService } from '@ucap-webmessenger/api-common';
import { EnvironmentsInfo, KEY_ENVIRONMENTS_INFO } from '@app/types';
import { StatusCode } from '@ucap-webmessenger/api';

View File

@ -1,14 +1,26 @@
import { MatSelectModule } from '@angular/material/select';
import { MatRadioModule } from '@angular/material/radio';
import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
import { ScrollingModule } from '@angular/cdk/scrolling';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { OverlayModule } from '@angular/cdk/overlay';
import {
MatCheckboxModule,
MatTableModule,
MatPaginatorModule,
MatRippleModule,
MatSortModule,
MatTooltipModule,
MatSidenavModule
} from '@angular/material';
import { MatSelectModule } from '@angular/material/select';
import { MatRadioModule } from '@angular/material/radio';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { MatBadgeModule } from '@angular/material/badge';
import { MatButtonModule } from '@angular/material/button';
@ -22,21 +34,9 @@ import { MatTabsModule } from '@angular/material/tabs';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import {
MatCheckboxModule,
MatTableModule,
MatPaginatorModule,
MatRippleModule,
MatSortModule,
MatTooltipModule,
MatSidenavModule
} from '@angular/material';
import { MatListModule } from '@angular/material/list';
import { MatChipsModule } from '@angular/material/chips';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { OverlayModule } from '@angular/cdk/overlay';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { UCapUiModule } from '@ucap-webmessenger/ui';
@ -45,6 +45,7 @@ import { UCapUiChatModule } from '@ucap-webmessenger/ui-chat';
import { UCapUiRoomModule } from '@ucap-webmessenger/ui-room';
import { UCapUiProfileModule } from '@ucap-webmessenger/ui-profile';
import { UCapUiGroupModule } from '@ucap-webmessenger/ui-group';
import { UCapUiMessageModule } from '@ucap-webmessenger/ui-message';
import { UCapUiOrganizationModule } from '@ucap-webmessenger/ui-organization';
import { UCapUiSettingsModule } from '@ucap-webmessenger/ui-settings';
@ -92,6 +93,7 @@ import { DIALOGS } from './dialogs';
UCapUiModule,
UCapUiAccountModule,
UCapUiChatModule,
UCapUiMessageModule,
UCapUiRoomModule,
UCapUiProfileModule,
UCapUiGroupModule,

View File

@ -8,7 +8,7 @@ import {
Input
} from '@angular/core';
import { NgForm } from '@angular/forms';
import { FileUploadItem } from '@ucap-webmessenger/api-common';
import { FileUploadItem } from '@ucap-webmessenger/api';
import { FileUploadQueueComponent } from '@ucap-webmessenger/ui';
@Component({

View File

@ -0,0 +1,24 @@
# UcapWebmessengerUiMessage
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.11.
## Code scaffolding
Run `ng generate component component-name --project ucap-webmessenger-ui-message` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ucap-webmessenger-ui-message`.
> Note: Don't forget to add `--project ucap-webmessenger-ui-message` or else it will be added to the default project in your `angular.json` file.
## Build
Run `ng build ucap-webmessenger-ui-message` to build the project. The build artifacts will be stored in the `dist/` directory.
## Publishing
After building your library with `ng build ucap-webmessenger-ui-message`, go to the dist folder `cd dist/ucap-webmessenger-ui-message` and run `npm publish`.
## Running unit tests
Run `ng test ucap-webmessenger-ui-message` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Further help
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View File

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, '../../coverage/ucap-webmessenger-ui-message'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View File

@ -0,0 +1,7 @@
{
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/ucap-webmessenger-ui-message",
"lib": {
"entryFile": "src/public-api.ts"
}
}

View File

@ -0,0 +1,8 @@
{
"name": "@ucap-webmessenger/ui-message",
"version": "0.0.1",
"peerDependencies": {
"@angular/common": "^8.2.11",
"@angular/core": "^8.2.11"
}
}

View File

@ -0,0 +1,54 @@
<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>
<mat-card-content class="message-write-body">
<div
#editor
class="ucap-message-write-editor"
contenteditable="true"
(paste)="onPasteEditor($event)"
></div>
<input type="file" #fileInput style="display: none" multiple />
<mat-list>
<mat-list-item *ngFor="let attachment of attachmentList">
{{ attachment.name }}
</mat-list-item>
</mat-list>
</mat-card-content>
<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>
</mat-card-actions>
</form>
</mat-card>

View File

@ -0,0 +1,24 @@
.ucap-message-write {
width: 100%;
height: 100%;
.message-receiver-list {
width: 100%;
}
.message-title {
width: 100%;
}
.message-write-body {
overflow: auto;
height: 270px;
.ucap-message-write-editor {
display: inline-block;
width: 100%;
height: 100%;
min-height: 270px;
}
}
}

View File

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

View File

@ -0,0 +1,190 @@
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ViewChild,
ContentChild,
TemplateRef,
AfterViewInit,
ChangeDetectorRef,
OnDestroy,
ElementRef
} from '@angular/core';
import { ucapAnimations } from '@ucap-webmessenger/ui';
import { NGXLogger } from 'ngx-logger';
import { FileUtil } from '@ucap-webmessenger/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
interface Content {
contentType: 'text' | 'image' | 'attachment';
content: string;
}
@Component({
selector: 'ucap-message-write',
templateUrl: './write.component.html',
styleUrls: ['./write.component.scss'],
animations: ucapAnimations
})
export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
@ViewChild('editor', { static: true })
editor: ElementRef<HTMLDivElement>;
@ViewChild('fileInput', { static: true })
fileInput: ElementRef<HTMLInputElement>;
messageWriteForm: FormGroup;
receiverList: string[] = ['이진호', '강희경', '이유진'];
attachmentList: File[];
constructor(
private formBuilder: FormBuilder,
private changeDetectorRef: ChangeDetectorRef,
private logger: NGXLogger
) {}
ngOnInit() {
this.messageWriteForm = this.formBuilder.group({
receiverList: ['', [Validators.required]],
title: ['', [Validators.required]]
});
}
ngOnDestroy(): void {}
ngAfterViewInit(): void {}
onClickImage() {
this.fileInput.nativeElement.click();
const self = this;
this.fileInput.nativeElement.onchange = () => {
const fileList: FileList = self.fileInput.nativeElement.files;
FileUtil.fromBlobToDataUrl(fileList.item(0)).then(dataUrl => {
const img = document.createElement('img');
img.src = dataUrl as string;
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);
return false;
}
onClickSend() {}
onClickCancel() {}
printEditor(): void {
const contentList: Content[] = [];
this.editor.nativeElement.childNodes.forEach((v, k) => {
this.parseNode(contentList, v);
});
this.logger.debug('printEditor', 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) {
this.appendNode(contentList, 'image', node.textContent);
} else if ('BR' === node.nodeName) {
this.appendNode(contentList, 'text', `\n`);
} else {
}
}
}
break;
case Node.TEXT_NODE:
this.appendNode(contentList, 'text', node.textContent);
break;
default:
break;
}
}
private appendNode(
contentList: Content[],
contentType: 'text' | 'image' | 'attachment',
content: string
) {
const prevContent = contentList[contentList.length - 1];
switch (contentType) {
case 'text':
if (!!prevContent && 'text' === prevContent.contentType) {
prevContent.content = `${prevContent.content}${content}`;
} else {
contentList.push({
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);
}
}

View File

@ -0,0 +1,59 @@
import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
import { FlexLayoutModule } from '@angular/flex-layout';
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 { MatDividerModule } from '@angular/material/divider';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatGridListModule } from '@angular/material/grid-list';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatListModule } from '@angular/material/list';
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
import { WriteComponent } from './components/write.component';
const COMPONENTS = [WriteComponent];
const DIALOGS = [];
const DIRECTIVES = [];
const SERVICES = [];
@NgModule({
imports: [
CommonModule,
ReactiveFormsModule,
FlexLayoutModule,
ScrollingModule,
MatButtonModule,
MatCardModule,
MatChipsModule,
MatDividerModule,
MatFormFieldModule,
MatGridListModule,
MatIconModule,
MatInputModule,
MatListModule,
PerfectScrollbarModule
],
exports: [...COMPONENTS, ...DIRECTIVES],
declarations: [...COMPONENTS, ...DIRECTIVES, ...DIALOGS],
entryComponents: [...DIALOGS]
})
export class UCapUiMessageModule {
public static forRoot(): ModuleWithProviders<UCapUiMessageModule> {
return {
ngModule: UCapUiMessageModule,
providers: [...SERVICES]
};
}
}

View File

@ -0,0 +1,7 @@
/*
* Public API Surface of ucap-webmessenger-ui-message
*/
export * from './lib/components/write.component';
export * from './lib/ucap-ui-message.module';

View File

@ -0,0 +1,21 @@
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';
import { getTestBed } from '@angular/core/testing';
import {
BrowserDynamicTestingModule,
platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing';
declare const require: any;
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

View File

@ -0,0 +1,26 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"angularCompilerOptions": {
"annotateForClosureCompiler": true,
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine",
"node"
]
},
"files": [
"src/test.ts"
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}

View File

@ -0,0 +1,17 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"ucapMessage",
"camelCase"
],
"component-selector": [
true,
"element",
"ucap-message",
"kebab-case"
]
}
}

View File

@ -10,7 +10,7 @@ import {
import { UserInfo } from '@ucap-webmessenger/protocol-sync';
import { UserInfoF } from '@ucap-webmessenger/protocol-query';
import { FileUploadItem } from '@ucap-webmessenger/api-common';
import { FileUploadItem } from '@ucap-webmessenger/api';
import { FormControl } from '@angular/forms';
@Component({

View File

@ -7,7 +7,7 @@ import {
} from '@angular/core';
import { NGXLogger } from 'ngx-logger';
import { FileUploadItem } from '@ucap-webmessenger/api-common';
import { FileUploadItem } from '@ucap-webmessenger/api';
@Component({
selector: 'ucap-file-upload-queue',

View File

@ -3,13 +3,13 @@ import { ucapAnimations } from '../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { FileViewerType } from '../types/file-viewer.type';
import { FileType } from '@ucap-webmessenger/protocol-file';
import { FileDownloadItem } from '@ucap-webmessenger/api-common';
import { FileDownloadItem } from '@ucap-webmessenger/api';
@Component({
selector: 'ucap-file-viewer',
templateUrl: './file-viewer.component.html',
styleUrls: ['./file-viewer.component.scss'],
animations: ucapAnimations,
animations: ucapAnimations
})
export class FileViewerComponent implements OnInit {
@Input()

View File

@ -1,14 +1,13 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { DeviceType } from '@ucap-webmessenger/core';
import { FileDownloadItem } from '@ucap-webmessenger/api-common';
import { FileDownloadItem } from '@ucap-webmessenger/api';
@Component({
selector: 'ucap-binary-viewer',
templateUrl: './binary-viewer.component.html',
styleUrls: ['./binary-viewer.component.scss'],
animations: ucapAnimations,
animations: ucapAnimations
})
export class BinaryViewerComponent implements OnInit {
@Input()

View File

@ -1,14 +1,13 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { DeviceType } from '@ucap-webmessenger/core';
import { FileDownloadItem } from '@ucap-webmessenger/api-common';
import { FileDownloadItem } from '@ucap-webmessenger/api';
@Component({
selector: 'ucap-document-viewer',
templateUrl: './document-viewer.component.html',
styleUrls: ['./document-viewer.component.scss'],
animations: ucapAnimations,
animations: ucapAnimations
})
export class DocumentViewerComponent implements OnInit {
@Input()

View File

@ -1,17 +1,13 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { DeviceType } from '@ucap-webmessenger/core';
import {
CommonApiService,
FileDownloadItem,
} from '@ucap-webmessenger/api-common';
import { FileDownloadItem } from '@ucap-webmessenger/api';
@Component({
selector: 'ucap-image-viewer',
templateUrl: './image-viewer.component.html',
styleUrls: ['./image-viewer.component.scss'],
animations: ucapAnimations,
animations: ucapAnimations
})
export class ImageViewerComponent implements OnInit {
@Input()

View File

@ -5,18 +5,18 @@ import {
Output,
EventEmitter,
ViewChild,
ElementRef,
ElementRef
} from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { MatSlider, MatSliderChange } from '@angular/material';
import { FileDownloadItem } from '@ucap-webmessenger/api-common';
import { FileDownloadItem } from '@ucap-webmessenger/api';
@Component({
selector: 'ucap-sound-viewer',
templateUrl: './sound-viewer.component.html',
styleUrls: ['./sound-viewer.component.scss'],
animations: ucapAnimations,
animations: ucapAnimations
})
export class SoundViewerComponent implements OnInit {
@Input()

View File

@ -5,18 +5,18 @@ import {
Output,
EventEmitter,
ViewChild,
ElementRef,
ElementRef
} from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { MatSlider, MatSliderChange } from '@angular/material';
import { FileDownloadItem } from '@ucap-webmessenger/api-common';
import { FileDownloadItem } from '@ucap-webmessenger/api';
@Component({
selector: 'ucap-video-viewer',
templateUrl: './video-viewer.component.html',
styleUrls: ['./video-viewer.component.scss'],
animations: ucapAnimations,
animations: ucapAnimations
})
export class VideoViewerComponent implements OnInit {
@Input()

View File

@ -29,46 +29,41 @@ export class MessageEditorComponent implements OnInit, AfterViewInit {
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);
// 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) {
@ -92,7 +87,7 @@ export class MessageEditorComponent implements OnInit, AfterViewInit {
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.addImageInInputField(files[i], this.fileIndex);
}
this.fileIndex++;
}
@ -103,71 +98,71 @@ export class MessageEditorComponent implements OnInit, AfterViewInit {
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());
// 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);
};
}
// this.attachElementNextFocused(img);
// };
// }
attachElementNextFocused(el: HTMLElement) {
if (window.getSelection) {
const sel = window.getSelection();
const selAnchorNode: Node = sel.anchorNode;
const focusedEl = selAnchorNode.parentElement;
// 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 (this.isParentIdCheck(focusedEl, 'contentArea')) {
// if (sel.rangeCount) {
// const range = sel.getRangeAt(0);
if (selAnchorNode.nodeType === Node.TEXT_NODE) {
const content: string = focusedEl.textContent;
// 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;
// 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);
}
}
// 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);
}
}
}
// 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) {

View File

@ -10,10 +10,8 @@ import {
import { NGXLogger } from 'ngx-logger';
import { FileUploadQueueComponent } from '../components/file-upload-queue.component';
import {
FileUploadItem,
CommonApiService
} from '@ucap-webmessenger/api-common';
import { FileUploadItem } from '@ucap-webmessenger/api';
import { CommonApiService } from '@ucap-webmessenger/api-common';
import { FileUtil } from '@ucap-webmessenger/core';
import { DialogService } from '../services/dialog.service';
import {

View File

@ -50,6 +50,9 @@
"@ucap-webmessenger/ui-messenger": [
"projects/ucap-webmessenger-ui-messenger/src/public-api"
],
"@ucap-webmessenger/ui-message": [
"projects/ucap-webmessenger-ui-message/src/public-api"
],
"@ucap-webmessenger/ui-organization": [
"projects/ucap-webmessenger-ui-organization/src/public-api"
],