file upload zone is added

This commit is contained in:
병준 박 2019-10-21 13:36:58 +09:00
parent f3f8059564
commit 1147fe830d
14 changed files with 230 additions and 84 deletions

View File

@ -53,7 +53,16 @@
mode="indeterminate" mode="indeterminate"
></mat-progress-bar> ></mat-progress-bar>
<!-- CHAT CONTENT --> <!-- CHAT CONTENT -->
<div fxFlex="1 1 auto" class="chat-content" #messageBoxContainer> <div
fxFlex="1 1 auto"
class="chat-content"
#messageBoxContainer
ucapUiFileUploadFor
(fileSelected)="onFileSelected($event)"
(fileDragEnter)="onFileDragEnter()"
(fileDragOver)="onFileDragOver()"
(fileDragLeave)="onFileDragLeave()"
>
<!-- Timer Room Info --> <!-- Timer Room Info -->
<span *ngIf="roomInfo && roomInfo.isTimeRoom">비밀 대화방입니다.</span> <span *ngIf="roomInfo && roomInfo.isTimeRoom">비밀 대화방입니다.</span>
<!-- Timer Room Info --> <!-- Timer Room Info -->
@ -71,6 +80,17 @@
> >
</ucap-chat-messages> </ucap-chat-messages>
<!-- CHAT MESSAGES --> <!-- CHAT MESSAGES -->
<div
*ngIf="fileDragOver || (files && 0 < files.length)"
class="file-drop-zone-container"
>
<div class="file-drop-zone">
<ucap-ui-file-upload-queue
[(files)]="files"
></ucap-ui-file-upload-queue>
</div>
</div>
</div> </div>
<!-- / CHAT CONTENT --> <!-- / CHAT CONTENT -->

View File

@ -22,6 +22,22 @@
background: transparent; background: transparent;
overflow: auto; overflow: auto;
-webkit-overflow-scrolling: touch; -webkit-overflow-scrolling: touch;
.file-drop-zone-container {
position: relative;
.file-drop-zone {
position: absolute;
border: 2px solid gray;
background-color: white;
width: 600px;
height: 150px;
bottom: 0px;
margin: auto;
left: 0px;
right: 0px;
}
}
} }
} }
} }

View File

@ -88,6 +88,9 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
isCopyableMessage = isCopyable; isCopyableMessage = isCopyable;
isRecallableMessage = isRecallable; isRecallableMessage = isRecallable;
fileDragOver = false;
files: File[];
constructor( constructor(
private store: Store<any>, private store: Store<any>,
private sessionStorageService: SessionStorageService, private sessionStorageService: SessionStorageService,
@ -198,6 +201,30 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewChecked {
this.logger.debug('fileSave', value); this.logger.debug('fileSave', value);
} }
onFileDragEnter() {
this.logger.debug('onFileDragEnter');
this.fileDragOver = true;
}
onFileDragOver() {
this.logger.debug('onFileDragOver');
}
onFileDragLeave() {
this.logger.debug('onFileDragLeave');
this.fileDragOver = false;
}
onFileSelected(files: File[]) {
this.logger.debug('onFileSelected', files);
if (!this.files) {
this.files = [];
}
this.files.push(...files);
this.fileDragOver = false;
}
onContextMenuMessage(params: { event: MouseEvent; message: Info }) { onContextMenuMessage(params: { event: MouseEvent; message: Info }) {
params.event.preventDefault(); params.event.preventDefault();
params.event.stopPropagation(); params.event.stopPropagation();

View File

@ -17,6 +17,7 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input'; import { MatInputModule } from '@angular/material/input';
import { DragDropModule } from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop';
import { OverlayModule } from '@angular/cdk/overlay';
import { UCapUiModule } from '@ucap-webmessenger/ui'; import { UCapUiModule } from '@ucap-webmessenger/ui';
import { UCapUiChatModule } from '@ucap-webmessenger/ui-chat'; import { UCapUiChatModule } from '@ucap-webmessenger/ui-chat';
@ -35,6 +36,7 @@ import { ReactiveFormsModule } from '@angular/forms';
FlexLayoutModule, FlexLayoutModule,
DragDropModule, DragDropModule,
ReactiveFormsModule, ReactiveFormsModule,
OverlayModule,
MatFormFieldModule, MatFormFieldModule,
MatInputModule, MatInputModule,
MatBadgeModule, MatBadgeModule,

View File

@ -2,3 +2,7 @@
// Import app.theme.scss // Import app.theme.scss
@import 'app/app.theme'; @import 'app/app.theme';
.file-upload-backdrop {
background-color: aqua;
}

View File

@ -19,6 +19,7 @@ import {
UserInfoF, UserInfoF,
UserInfoDN UserInfoDN
} from '@ucap-webmessenger/protocol-query'; } from '@ucap-webmessenger/protocol-query';
import { NGXLogger } from 'ngx-logger';
@Component({ @Component({
selector: 'ucap-group-expansion-panel', selector: 'ucap-group-expansion-panel',
@ -57,7 +58,7 @@ export class ExpansionPanelComponent implements OnInit {
@ViewChild('groupAccordion', { static: true }) groupAccordion: MatAccordion; @ViewChild('groupAccordion', { static: true }) groupAccordion: MatAccordion;
constructor() {} constructor(private logger: NGXLogger) {}
ngOnInit() {} ngOnInit() {}

View File

@ -0,0 +1,21 @@
<div
fxLayout="row wrap"
fxFlex="100"
class="ucap-ui-file-upload-queue-container"
>
<div
fxLayout="row"
fxFlex="100"
fxFlex.gt-xs="50"
fxFlex.gt-md="25"
*ngFor="let file of uploadFiles"
>
<div>
<mat-icon>image</mat-icon>
</div>
<div>{{ file.name }}</div>
<div (click)="onClickClear(file)">
<mat-icon>clear</mat-icon>
</div>
</div>
</div>

View File

@ -0,0 +1,4 @@
.ucap-ui-file-upload-queue-container {
width: 100%;
height: 100%;
}

View File

@ -0,0 +1,29 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { NGXLogger } from 'ngx-logger';
@Component({
selector: 'ucap-ui-file-upload-queue',
templateUrl: './file-upload-queue.component.html',
styleUrls: ['./file-upload-queue.component.scss']
})
export class FileUploadQueueComponent implements OnInit {
@Output()
filesChange = new EventEmitter<File[]>();
@Input() set files(files: File[]) {
this.uploadFiles = files;
}
uploadFiles: File[];
constructor(private logger: NGXLogger) {}
ngOnInit() {}
onClickClear(file: File) {
this.uploadFiles = this.uploadFiles.filter(f => {
return f.name !== file.name && f.path !== file.path;
});
this.filesChange.emit(this.uploadFiles);
}
}

View File

@ -0,0 +1,69 @@
import {
Directive,
ElementRef,
EventEmitter,
HostListener,
Output
} from '@angular/core';
import { NGXLogger } from 'ngx-logger';
@Directive({
selector: 'input[ucapUiFileUploadFor], div[ucapUiFileUploadFor]'
})
export class FileUploadForDirective {
@Output()
public fileDragEnter: EventEmitter<any> = new EventEmitter();
@Output()
public fileDragOver: EventEmitter<any> = new EventEmitter();
@Output()
public fileDragLeave: EventEmitter<any> = new EventEmitter();
@Output()
public fileSelected: EventEmitter<File[]> = new EventEmitter<File[]>();
dragOver = false;
constructor(private elementRef: ElementRef, private logger: NGXLogger) {}
@HostListener('window:dragenter', ['$event'])
public onDragEnter(event: any): any {
const files = event.dataTransfer.files;
if (!this.dragOver) {
this.fileDragEnter.emit(files);
this.dragOver = true;
}
}
@HostListener('window:dragover', ['$event'])
public onDragOver(event: any): any {
event.preventDefault();
}
@HostListener('window:dragleave', ['$event'])
public onDragLeave(event: any): any {
if (event && event.pageX === 0 && event.pageY === 0) {
this.fileDragLeave.emit();
this.dragOver = false;
}
}
@HostListener('change')
public onChange(): any {
const files = this.elementRef.nativeElement.files;
this.fileSelected.emit(files);
this.elementRef.nativeElement.value = '';
}
@HostListener('window:drop', ['$event'])
public onDrop(event: any): any {
const files = event.dataTransfer.files;
this.fileSelected.emit(files);
event.preventDefault();
event.stopPropagation();
this.elementRef.nativeElement.value = '';
this.dragOver = false;
}
}

View File

@ -0,0 +1,20 @@
import { Pipe, PipeTransform } from '@angular/core';
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
@Pipe({ name: 'ucapBytes' })
export class BytesPipe implements PipeTransform {
public transform(bytes: number): string {
if (isNaN(parseFloat('' + bytes)) || !isFinite(bytes)) {
return '-';
}
if (bytes <= 0) {
return '0';
}
const num = Math.floor(Math.log(bytes) / Math.log(1024));
return (
(bytes / Math.pow(1024, Math.floor(num))).toFixed(1) + ' ' + units[num]
);
}
}

View File

@ -1,76 +0,0 @@
import { PipeTransform, Pipe } from '@angular/core';
import { of, Observable } from 'rxjs';
import { NGXLogger } from 'ngx-logger';
import { HttpClient } from '@angular/common/http';
import { take, map, catchError, switchMap, tap } from 'rxjs/operators';
export interface ImagePipeParameter {
path?: string;
validation?: boolean;
default?: string;
}
@Pipe({ name: 'ucapUiImaage' })
export class ImagePipe implements PipeTransform {
constructor(private httpClient: HttpClient, private logger: NGXLogger) {}
transform(
base: string,
params?: ImagePipeParameter
): Observable<string | ArrayBuffer> {
params = !!params ? params : {};
params.validation = !!params.validation ? params.validation : true;
if (params.validation) {
if (
!base ||
'' === base.trim() ||
!params.path ||
'' === params.path.trim()
) {
return of(params.default);
}
}
const imageUrl = `${base}${params.path}`;
return new Observable<string | ArrayBuffer>(subscriber => {
subscriber.next(params.default);
this.httpClient
.get(imageUrl, { responseType: 'blob' })
.pipe(
take(1),
tap(blob => {
const reader = new FileReader();
reader.onloadend = ev => {
subscriber.next(reader.result);
};
reader.readAsDataURL(blob);
}),
catchError(err => {
this.logger.error(err);
return params.default;
})
)
.subscribe();
});
return this.httpClient.get(imageUrl, { responseType: 'blob' }).pipe(
take(1),
switchMap(blob => {
return new Observable<string | ArrayBuffer>(subscriber => {
const reader = new FileReader();
reader.onloadend = ev => {
subscriber.next(reader.result);
};
reader.readAsDataURL(blob);
});
}),
catchError(err => {
this.logger.error(err);
return params.default;
})
);
}
}

View File

@ -1,15 +1,19 @@
import { NgModule, ModuleWithProviders } from '@angular/core'; import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatButtonModule } from '@angular/material/button'; import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDialogModule } from '@angular/material/dialog'; import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatSnackBarModule } from '@angular/material/snack-bar'; import { MatSnackBarModule } from '@angular/material/snack-bar';
import { DragDropModule } from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop';
import { FileUploadQueueComponent } from './components/file-upload-queue.component';
import { ImageComponent } from './components/image.component'; import { ImageComponent } from './components/image.component';
const COMPONENTS = [ImageComponent]; const COMPONENTS = [ImageComponent, FileUploadQueueComponent];
import { BottomSheetService } from './services/bottom-sheet.service'; import { BottomSheetService } from './services/bottom-sheet.service';
import { ClipboardService } from './services/clipboard.service'; import { ClipboardService } from './services/clipboard.service';
@ -22,24 +26,29 @@ const SERVICES = [
SnackBarService SnackBarService
]; ];
import { FileUploadForDirective } from './directives/file-upload-for.directive';
const DIRECTIVES = [FileUploadForDirective];
import { AlertDialogComponent } from './dialogs/alert.dialog.component'; import { AlertDialogComponent } from './dialogs/alert.dialog.component';
import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component'; import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component';
const DIALOGS = [AlertDialogComponent, ConfirmDialogComponent]; const DIALOGS = [AlertDialogComponent, ConfirmDialogComponent];
import { ImagePipe } from './pipes/image.pipe'; import { BytesPipe } from './pipes/bytes.pipe';
const PIPES = [ImagePipe]; const PIPES = [BytesPipe];
@NgModule({ @NgModule({
imports: [ imports: [
CommonModule, CommonModule,
FlexLayoutModule,
MatButtonModule, MatButtonModule,
MatCardModule, MatCardModule,
MatDialogModule, MatDialogModule,
MatIconModule,
MatSnackBarModule, MatSnackBarModule,
DragDropModule DragDropModule
], ],
exports: [...COMPONENTS, ...PIPES], exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
declarations: [...COMPONENTS, ...DIALOGS, ...PIPES], declarations: [...COMPONENTS, ...DIALOGS, ...DIRECTIVES, ...PIPES],
entryComponents: [...DIALOGS] entryComponents: [...DIALOGS]
}) })
export class UCapUiModule { export class UCapUiModule {

View File

@ -7,7 +7,7 @@ export * from './lib/animations';
export * from './lib/dialogs/alert.dialog.component'; export * from './lib/dialogs/alert.dialog.component';
export * from './lib/dialogs/confirm.dialog.component'; export * from './lib/dialogs/confirm.dialog.component';
export * from './lib/pipes/image.pipe'; export * from './lib/directives/file-upload-for.directive';
export * from './lib/services/bottom-sheet.service'; export * from './lib/services/bottom-sheet.service';
export * from './lib/services/clipboard.service'; export * from './lib/services/clipboard.service';