refactoring : by file upload
This commit is contained in:
parent
c589053b88
commit
0cacc482b1
|
@ -219,6 +219,15 @@ export class CommonApiService {
|
||||||
return 0 === rejected.length ? undefined : rejected;
|
return 0 === rejected.length ? undefined : rejected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public checkInvalidMediaMimeForFileTalkForFileList(
|
||||||
|
fileList: FileList
|
||||||
|
): Promise<string[]> {
|
||||||
|
const files: File[] = [];
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
|
files.push(fileList.item(i));
|
||||||
|
}
|
||||||
|
return this.checkInvalidMediaMimeForFileTalk(files);
|
||||||
|
}
|
||||||
public checkInvalidMediaMimeForFileTalk(files: File[]): Promise<string[]> {
|
public checkInvalidMediaMimeForFileTalk(files: File[]): Promise<string[]> {
|
||||||
return new Promise<string[]>(async (resolve, reject) => {
|
return new Promise<string[]>(async (resolve, reject) => {
|
||||||
const mediaFiles = this.mediaFiles(files);
|
const mediaFiles = this.mediaFiles(files);
|
||||||
|
|
|
@ -1075,10 +1075,13 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
roomSeq: this.roomInfoSubject.value.roomSeq
|
roomSeq: this.roomInfoSubject.value.roomSeq
|
||||||
};
|
};
|
||||||
|
|
||||||
const files: File[] = fileUploadItems.map(fui => fui.file);
|
|
||||||
|
|
||||||
const allObservables: Observable<FileTalkSaveResponse>[] = [];
|
const allObservables: Observable<FileTalkSaveResponse>[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 확장자, real mime 체크는 이전 단계에서 진행하고 여기서는 oversize 만 체크함.
|
||||||
|
* cf) acceptableExtensionForFileTalk, checkInvalidMediaMimeForFileTalkForFileList
|
||||||
|
*/
|
||||||
|
const files: File[] = fileUploadItems.map(fui => fui.file);
|
||||||
const fileAllowSize =
|
const fileAllowSize =
|
||||||
!!this.sessionVerInfo && this.sessionVerInfo.fileAllowSize
|
!!this.sessionVerInfo && this.sessionVerInfo.fileAllowSize
|
||||||
? this.sessionVerInfo.fileAllowSize
|
? this.sessionVerInfo.fileAllowSize
|
||||||
|
@ -1105,66 +1108,6 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.commonApiService.mimeCheckForImageAndVideoFiles(
|
|
||||||
// fileUploadItems.map(fui => fui.file)
|
|
||||||
// );
|
|
||||||
|
|
||||||
const checkExt = this.commonApiService.acceptableExtensionForFileTalk(
|
|
||||||
files.map(f => FileUtil.getExtension(f.name))
|
|
||||||
);
|
|
||||||
if (!!checkExt) {
|
|
||||||
if (!!this.fileUploadQueue) {
|
|
||||||
this.fileUploadQueue.onUploadComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.snackBarService.openFromComponent<
|
|
||||||
AlertSnackbarComponent,
|
|
||||||
AlertSnackbarData
|
|
||||||
>(AlertSnackbarComponent, {
|
|
||||||
duration: 1000,
|
|
||||||
verticalPosition: 'bottom',
|
|
||||||
horizontalPosition: 'center',
|
|
||||||
data: {
|
|
||||||
html: this.translateService.instant(
|
|
||||||
'common.file.errors.notSupporedType',
|
|
||||||
{
|
|
||||||
supporedType: checkExt.join(',')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fakeMedias = await this.commonApiService.checkInvalidMediaMimeForFileTalk(
|
|
||||||
files
|
|
||||||
);
|
|
||||||
if (!!fakeMedias) {
|
|
||||||
if (!!this.fileUploadQueue) {
|
|
||||||
this.fileUploadQueue.onUploadComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.snackBarService.openFromComponent<
|
|
||||||
AlertSnackbarComponent,
|
|
||||||
AlertSnackbarData
|
|
||||||
>(AlertSnackbarComponent, {
|
|
||||||
duration: 3000,
|
|
||||||
verticalPosition: 'bottom',
|
|
||||||
horizontalPosition: 'center',
|
|
||||||
data: {
|
|
||||||
html: this.translateService.instant(
|
|
||||||
'common.file.errors.notAcceptableMime',
|
|
||||||
{
|
|
||||||
supporedType: fakeMedias.length > 0 ? fakeMedias.join(',') : ''
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const fileUploadItem of fileUploadItems) {
|
for (const fileUploadItem of fileUploadItems) {
|
||||||
let thumbnail: File;
|
let thumbnail: File;
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -5,11 +5,21 @@ import {
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ElementRef,
|
ElementRef,
|
||||||
Input
|
Input,
|
||||||
|
NgZone
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { NgForm } from '@angular/forms';
|
import { NgForm } from '@angular/forms';
|
||||||
import { FileUploadItem } from '@ucap-webmessenger/api';
|
import { FileUploadItem } from '@ucap-webmessenger/api';
|
||||||
import { FileUploadQueueComponent } from '@ucap-webmessenger/ui';
|
import {
|
||||||
|
FileUploadQueueComponent,
|
||||||
|
SnackBarService,
|
||||||
|
AlertSnackbarComponent,
|
||||||
|
AlertSnackbarData
|
||||||
|
} from '@ucap-webmessenger/ui';
|
||||||
|
import { CommonApiService } from '@ucap-webmessenger/api-common';
|
||||||
|
import { NGXLogger } from 'ngx-logger';
|
||||||
|
import { FileUtil } from '@ucap-webmessenger/core';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-chat-form',
|
selector: 'ucap-chat-form',
|
||||||
|
@ -43,7 +53,13 @@ export class FormComponent implements OnInit {
|
||||||
@ViewChild('fileInput', { static: false })
|
@ViewChild('fileInput', { static: false })
|
||||||
fileInput: ElementRef<HTMLInputElement>;
|
fileInput: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
constructor() {}
|
constructor(
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private commonApiService: CommonApiService,
|
||||||
|
private snackBarService: SnackBarService,
|
||||||
|
private readonly ngZone: NgZone,
|
||||||
|
private logger: NGXLogger
|
||||||
|
) {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
|
||||||
|
@ -64,17 +80,27 @@ export class FormComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeFileInput() {
|
onChangeFileInput() {
|
||||||
const fileUploadItems = FileUploadItem.fromFiles(
|
const self = this;
|
||||||
this.fileInput.nativeElement.files
|
const fileList = this.fileInput.nativeElement.files;
|
||||||
);
|
this.validUploadFile(fileList)
|
||||||
|
.then(async result => {
|
||||||
|
if (!result) {
|
||||||
|
self.fileInput.nativeElement.value = '';
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const fileUploadItems = FileUploadItem.fromFiles(fileList);
|
||||||
|
|
||||||
if (!!this.fileUploadQueue) {
|
if (!!self.fileUploadQueue) {
|
||||||
this.fileUploadQueue.onFileSelected(fileUploadItems);
|
self.fileUploadQueue.onFileSelected(fileUploadItems);
|
||||||
}
|
}
|
||||||
|
self.fileInput.nativeElement.value = '';
|
||||||
this.sendFiles.emit(fileUploadItems);
|
self.sendFiles.emit(fileUploadItems);
|
||||||
|
}
|
||||||
this.fileInput.nativeElement.value = '';
|
})
|
||||||
|
.catch(err => {
|
||||||
|
self.fileInput.nativeElement.value = '';
|
||||||
|
this.logger.error(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickStickerSelector() {
|
onClickStickerSelector() {
|
||||||
|
@ -90,4 +116,62 @@ export class FormComponent implements OnInit {
|
||||||
|
|
||||||
this.clipboardPaste.emit(event);
|
this.clipboardPaste.emit(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async validUploadFile(fileList: FileList): Promise<boolean> {
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
const checkExt = this.commonApiService.acceptableExtensionForFileTalk(
|
||||||
|
FileUtil.getExtensions(fileList)
|
||||||
|
);
|
||||||
|
if (!!checkExt) {
|
||||||
|
this.ngZone.run(() => {
|
||||||
|
this.snackBarService.openFromComponent<
|
||||||
|
AlertSnackbarComponent,
|
||||||
|
AlertSnackbarData
|
||||||
|
>(AlertSnackbarComponent, {
|
||||||
|
duration: 1000,
|
||||||
|
verticalPosition: 'bottom',
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
data: {
|
||||||
|
html: this.translateService.instant(
|
||||||
|
'common.file.errors.notSupporedType',
|
||||||
|
{
|
||||||
|
supporedType: checkExt.join(',')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
valid = false;
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeMedia = await this.commonApiService.checkInvalidMediaMimeForFileTalkForFileList(
|
||||||
|
fileList
|
||||||
|
);
|
||||||
|
if (!!fakeMedia) {
|
||||||
|
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.join(',')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
valid = false;
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,37 +173,41 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
const self = this;
|
const self = this;
|
||||||
this.fileInput.nativeElement.onchange = async () => {
|
this.fileInput.nativeElement.onchange = async () => {
|
||||||
const fileList: FileList = self.fileInput.nativeElement.files;
|
const fileList: FileList = self.fileInput.nativeElement.files;
|
||||||
|
this.validUploadFile(fileList)
|
||||||
|
.then(async result => {
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
|
const file = fileList.item(i);
|
||||||
|
|
||||||
if (!this.validUploadFile(fileList)) {
|
const dataUrl = await FileUtil.fromBlobToDataUrl(file);
|
||||||
return;
|
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);
|
||||||
|
|
||||||
this.validUploadFile(fileList).then(async result => {
|
const empty = document.createElement('div');
|
||||||
if (!result) {
|
empty.innerHTML = ' <br/>';
|
||||||
return;
|
self.insertNode(empty, true);
|
||||||
} else {
|
}
|
||||||
for (let i = 0; i < fileList.length; i++) {
|
|
||||||
const file = fileList.item(i);
|
|
||||||
|
|
||||||
const dataUrl = await FileUtil.fromBlobToDataUrl(file);
|
self.fileInput.nativeElement.value = '';
|
||||||
const img = document.createElement('img');
|
self.fileInput.nativeElement.onchange = undefined;
|
||||||
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');
|
self.checkContentLength();
|
||||||
empty.innerHTML = ' <br/>';
|
self.changeDetectorRef.detectChanges();
|
||||||
self.insertNode(empty, true);
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
self.fileInput.nativeElement.value = '';
|
self.fileInput.nativeElement.value = '';
|
||||||
self.fileInput.nativeElement.onchange = undefined;
|
self.fileInput.nativeElement.onchange = undefined;
|
||||||
|
|
||||||
self.checkContentLength();
|
self.checkContentLength();
|
||||||
self.changeDetectorRef.detectChanges();
|
self.changeDetectorRef.detectChanges();
|
||||||
}
|
this.logger.error(err);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,24 +217,28 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
const self = this;
|
const self = this;
|
||||||
this.fileInput.nativeElement.onchange = () => {
|
this.fileInput.nativeElement.onchange = () => {
|
||||||
const fileList: FileList = this.fileInput.nativeElement.files;
|
const fileList: FileList = this.fileInput.nativeElement.files;
|
||||||
|
this.validUploadFile(fileList)
|
||||||
if (!this.validUploadFile(fileList)) {
|
.then(async result => {
|
||||||
self.fileInput.nativeElement.value = '';
|
if (!result) {
|
||||||
return;
|
self.fileInput.nativeElement.value = '';
|
||||||
}
|
return;
|
||||||
|
} else {
|
||||||
if (!self.attachmentList) {
|
if (!self.attachmentList) {
|
||||||
self.attachmentList = [];
|
self.attachmentList = [];
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
for (let i = 0; i < fileList.length; i++) {
|
const file = fileList.item(i);
|
||||||
const file = fileList.item(i);
|
self.attachmentList.push(file);
|
||||||
self.attachmentList.push(file);
|
}
|
||||||
}
|
self.fileInput.nativeElement.value = '';
|
||||||
|
self.changeDetectorRef.detectChanges();
|
||||||
self.changeDetectorRef.detectChanges();
|
}
|
||||||
|
})
|
||||||
self.fileInput.nativeElement.value = '';
|
.catch(err => {
|
||||||
|
self.fileInput.nativeElement.value = '';
|
||||||
|
self.changeDetectorRef.detectChanges();
|
||||||
|
this.logger.error(err);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,12 +297,8 @@ export class WriteComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
const files: File[] = [];
|
const fakeMedia = await this.commonApiService.checkInvalidMediaMimeForFileTalkForFileList(
|
||||||
for (let i = 0; i < fileList.length; i++) {
|
fileList
|
||||||
files.push(fileList.item(i));
|
|
||||||
}
|
|
||||||
const fakeMedia = await this.commonApiService.checkInvalidMediaMimeForFileTalk(
|
|
||||||
files
|
|
||||||
);
|
);
|
||||||
if (!!fakeMedia) {
|
if (!!fakeMedia) {
|
||||||
this.ngZone.run(() => {
|
this.ngZone.run(() => {
|
||||||
|
|
|
@ -5,12 +5,21 @@ import {
|
||||||
HostListener,
|
HostListener,
|
||||||
Output,
|
Output,
|
||||||
Input,
|
Input,
|
||||||
AfterViewInit
|
AfterViewInit,
|
||||||
|
NgZone
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { NGXLogger } from 'ngx-logger';
|
import { NGXLogger } from 'ngx-logger';
|
||||||
import { FileUploadQueueComponent } from '../components/file-upload-queue.component';
|
import { FileUploadQueueComponent } from '../components/file-upload-queue.component';
|
||||||
import { FileUploadItem } from '@ucap-webmessenger/api';
|
import { FileUploadItem } from '@ucap-webmessenger/api';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CommonApiService } from '@ucap-webmessenger/api-common';
|
||||||
|
import { SnackBarService } from '../services/snack-bar.service';
|
||||||
|
import { FileUtil } from '@ucap-webmessenger/core';
|
||||||
|
import {
|
||||||
|
AlertSnackbarComponent,
|
||||||
|
AlertSnackbarData
|
||||||
|
} from '../snackbars/alert.snackbar.component';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'input[ucapFileUploadFor], div[ucapFileUploadFor]'
|
selector: 'input[ucapFileUploadFor], div[ucapFileUploadFor]'
|
||||||
|
@ -33,7 +42,14 @@ export class FileUploadForDirective implements AfterViewInit {
|
||||||
|
|
||||||
dragOver = false;
|
dragOver = false;
|
||||||
|
|
||||||
constructor(private elementRef: ElementRef, private logger: NGXLogger) {}
|
constructor(
|
||||||
|
private elementRef: ElementRef,
|
||||||
|
private logger: NGXLogger,
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private commonApiService: CommonApiService,
|
||||||
|
private snackBarService: SnackBarService,
|
||||||
|
private readonly ngZone: NgZone
|
||||||
|
) {}
|
||||||
|
|
||||||
ngAfterViewInit(): void {}
|
ngAfterViewInit(): void {}
|
||||||
|
|
||||||
|
@ -93,17 +109,37 @@ export class FileUploadForDirective implements AfterViewInit {
|
||||||
if (!this.isFileDrag(event.dataTransfer)) {
|
if (!this.isFileDrag(event.dataTransfer)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const self = this;
|
||||||
const files: FileList = event.dataTransfer.files;
|
const files: FileList = event.dataTransfer.files;
|
||||||
|
|
||||||
const fileUploadItems = FileUploadItem.fromFiles(files);
|
|
||||||
this.fileSelected.emit(fileUploadItems);
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.elementRef.nativeElement.value = '';
|
this.elementRef.nativeElement.value = '';
|
||||||
this.dragOver = false;
|
this.dragOver = false;
|
||||||
if (!!this.fileUploadQueue) {
|
|
||||||
this.fileUploadQueue.onDrop(fileUploadItems);
|
this.validUploadFile(files)
|
||||||
}
|
.then(async result => {
|
||||||
|
if (!result) {
|
||||||
|
if (!!this.fileUploadQueue) {
|
||||||
|
this.fileUploadQueue.onUploadComplete();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
const fileUploadItems = FileUploadItem.fromFiles(files);
|
||||||
|
self.fileSelected.emit(fileUploadItems);
|
||||||
|
|
||||||
|
if (!!self.fileUploadQueue) {
|
||||||
|
self.fileUploadQueue.onDrop(fileUploadItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
if (!!this.fileUploadQueue) {
|
||||||
|
this.fileUploadQueue.onUploadComplete();
|
||||||
|
}
|
||||||
|
this.logger.error(err);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private isFileDrag(dataTransfer: DataTransfer): boolean {
|
private isFileDrag(dataTransfer: DataTransfer): boolean {
|
||||||
|
@ -121,4 +157,62 @@ export class FileUploadForDirective implements AfterViewInit {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async validUploadFile(fileList: FileList): Promise<boolean> {
|
||||||
|
let valid = true;
|
||||||
|
|
||||||
|
const checkExt = this.commonApiService.acceptableExtensionForFileTalk(
|
||||||
|
FileUtil.getExtensions(fileList)
|
||||||
|
);
|
||||||
|
if (!!checkExt) {
|
||||||
|
this.ngZone.run(() => {
|
||||||
|
this.snackBarService.openFromComponent<
|
||||||
|
AlertSnackbarComponent,
|
||||||
|
AlertSnackbarData
|
||||||
|
>(AlertSnackbarComponent, {
|
||||||
|
duration: 1000,
|
||||||
|
verticalPosition: 'bottom',
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
data: {
|
||||||
|
html: this.translateService.instant(
|
||||||
|
'common.file.errors.notSupporedType',
|
||||||
|
{
|
||||||
|
supporedType: checkExt.join(',')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
valid = false;
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fakeMedia = await this.commonApiService.checkInvalidMediaMimeForFileTalkForFileList(
|
||||||
|
fileList
|
||||||
|
);
|
||||||
|
if (!!fakeMedia) {
|
||||||
|
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.join(',')
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
valid = false;
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user