This commit is contained in:
leejh 2019-11-07 16:56:35 +09:00
commit b6aad4ddc9
34 changed files with 690 additions and 285 deletions

View File

@ -22,7 +22,18 @@ const fileTalkDownloadEncodeMap = {
attachmentsSeq: 'p_att_seq'
};
export const encodeFileTalkDownload: APIFormDataEncoder<
export const encodeFileTalkDownload: APIEncoder<FileTalkDownloadRequest> = (
req: FileTalkDownloadRequest
) => {
const extraParams: any = {};
extraParams.userSeq = String(req.userSeq);
extraParams.attachmentsSeq = String(req.attachmentsSeq);
return ParameterUtil.encode(fileTalkDownloadEncodeMap, req, extraParams);
};
export const encodeFormDataFileTalkDownload: APIFormDataEncoder<
FileTalkDownloadRequest
> = (req: FileTalkDownloadRequest) => {
const extraParams: any = {};

View File

@ -19,7 +19,8 @@ import {
} from '../apis/file-profile-save';
import {
FileTalkDownloadRequest,
encodeFileTalkDownload
encodeFileTalkDownload,
encodeFormDataFileTalkDownload
} from '../apis/file-talk-download';
import {
FileTalkSaveRequest,
@ -96,6 +97,24 @@ export class CommonApiService {
.pipe(map(res => decodeFileProfileSave(res)));
}
public urlForFileTalkDownload(
req: FileTalkDownloadRequest,
fileTalkDownloadUrl?: string
): string {
const httpReq = new HttpRequest(
'GET',
!!fileTalkDownloadUrl
? fileTalkDownloadUrl
: this.moduleConfig.urls.fileTalkDownload,
{},
{
params: encodeFileTalkDownload(req)
}
);
return httpReq.urlWithParams;
}
public fileTalkDownload(
req: FileTalkDownloadRequest,
fileTalkDownloadUrl?: string
@ -105,7 +124,7 @@ export class CommonApiService {
!!fileTalkDownloadUrl
? fileTalkDownloadUrl
: this.moduleConfig.urls.fileTalkDownload,
encodeFileTalkDownload(req),
encodeFormDataFileTalkDownload(req),
{ reportProgress: true, responseType: 'blob' }
);

View File

@ -2,7 +2,8 @@ import {
APIRequest,
APIResponse,
APIEncoder,
APIDecoder
APIDecoder,
ParameterUtil
} from '@ucap-webmessenger/api';
export interface CheckUserInfoExRequest extends APIRequest {
@ -15,14 +16,16 @@ export interface CheckUserInfoExResponse extends APIResponse {
userId?: string;
}
const checkUserInfoExEncodeMap = {
userId: 'p_user_id',
companyCode: 'p_comp_code',
userSession: 'p_user_session'
};
export const encodeCheckUserInfoEx: APIEncoder<CheckUserInfoExRequest> = (
req: CheckUserInfoExRequest
) => {
return {
p_user_id: req.userId,
p_comp_code: req.companyCode,
p_user_session: req.userSession
};
return ParameterUtil.encode(checkUserInfoExEncodeMap, req);
};
export const decodeCheckUserInfoEx: APIDecoder<CheckUserInfoExResponse> = (

View File

@ -3,7 +3,8 @@ import {
APIRequest,
APIResponse,
APIEncoder,
APIDecoder
APIDecoder,
ParameterUtil
} from '@ucap-webmessenger/api';
import { Company } from '../models/company';
@ -19,16 +20,22 @@ export interface CompanyListResponse extends APIResponse {
companyList?: Company[];
}
const companyListEncodeMap = {
userSeq: 'p_user_seq',
appType: 'p_app_type',
deviceType: 'p_device_type',
token: 'p_token',
companyGroupCode: 'p_comp_group_code'
};
export const encodeCompanyList: APIEncoder<CompanyListRequest> = (
req: CompanyListRequest
) => {
return {
p_user_seq: String(req.userSeq),
p_app_type: req.appType,
p_device_type: req.deviceType,
p_token: req.token,
p_comp_group_code: req.companyGroupCode
};
const extraParams: any = {};
extraParams.userSeq = String(req.userSeq);
return ParameterUtil.encode(companyListEncodeMap, req, extraParams);
};
export const decodeCompanyList: APIDecoder<CompanyListResponse> = (

View File

@ -3,7 +3,8 @@ import {
APIRequest,
APIResponse,
APIEncoder,
APIDecoder
APIDecoder,
ParameterUtil
} from '@ucap-webmessenger/api';
export interface TokenUpdateRequest extends APIRequest {
@ -17,16 +18,22 @@ export interface TokenUpdateRequest extends APIRequest {
// tslint:disable-next-line: no-empty-interface
export interface TokenUpdateResponse extends APIResponse {}
const tokenUpdateEncodeMap = {
userSeq: 'p_user_seq',
deviceType: 'p_device_type',
token: 'p_token',
mobilePid: 'p_mobile_pid',
pushType: 'p_push_type'
};
export const encodeTokenUpdate: APIEncoder<TokenUpdateRequest> = (
req: TokenUpdateRequest
) => {
return {
p_user_seq: String(req.userSeq),
p_device_type: req.deviceType,
p_token: req.token,
p_mobile_pid: req.mobilePid,
p_push_type: req.pushType
};
const extraParams: any = {};
extraParams.userSeq = String(req.userSeq);
return ParameterUtil.encode(tokenUpdateEncodeMap, req, extraParams);
};
export const decodeTokenUpdate: APIDecoder<TokenUpdateResponse> = (

View File

@ -3,7 +3,8 @@ import {
APIRequest,
APIResponse,
APIEncoder,
APIDecoder
APIDecoder,
ParameterUtil
} from '@ucap-webmessenger/api';
export interface UrlInfoRequest extends APIRequest {
@ -25,12 +26,14 @@ export interface UrlInfoResponse extends APIResponse {
synapViewUrl?: string;
}
const urlInfoEncodeMap = {
deviceType: 'p_device_type'
};
export const encodeUrlInfo: APIEncoder<UrlInfoRequest> = (
req: UrlInfoRequest
) => {
return {
p_device_type: req.deviceType
};
return ParameterUtil.encode(urlInfoEncodeMap, req);
};
export const decodeUrlInfo: APIDecoder<UrlInfoResponse> = (res: any) => {

View File

@ -12,13 +12,7 @@ export interface APIResponse {
errorMessage: string;
}
export type APIEncoder<REQ> = (
req: REQ
) =>
| HttpParams
| {
[param: string]: string | string[];
};
export type APIEncoder<REQ> = (req: REQ) => HttpParams;
export type APIFormDataEncoder<REQ> = (req: REQ) => FormData;

View File

@ -1,6 +1,6 @@
export type Parameter = {
[param: string]: string | string[];
} | null;
import { HttpParams } from '@angular/common/http';
export type Parameter = HttpParams | null;
export class ParameterUtil {
public static encode(
@ -8,7 +8,7 @@ export class ParameterUtil {
param: {},
extraParam?: {}
): Parameter {
const parameter: Parameter = {};
let parameter: HttpParams = new HttpParams();
Object.keys(parameterMap).map(key => {
if (!param.hasOwnProperty(key)) {
@ -23,11 +23,15 @@ export class ParameterUtil {
}
if (!!v) {
parameter[parameterMap[key]] = v;
if (parameter.has(parameterMap[key])) {
parameter = parameter.append(parameterMap[key], v);
} else {
parameter = parameter.set(parameterMap[key], v);
}
}
});
return 0 === Object.keys(parameter).length ? null : parameter;
return parameter;
}
public static encodeFormData(

View File

@ -3,7 +3,7 @@ import { HttpParams } from '@angular/common/http';
export class UrlUtil {
public static format(href: string, param: Parameter): string {
const params = new HttpParams({ fromObject: param }).toString();
const params = param.toString();
if (params.length === 0) {
return href;

View File

@ -4,6 +4,7 @@ import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
import { UCapUiModule } from '@ucap-webmessenger/ui';
@ -16,6 +17,7 @@ import { DIALOGS } from './dialogs';
CommonModule,
FlexLayoutModule,
MatIconModule,
MatProgressBarModule,
MatToolbarModule,
UCapUiModule
],

View File

@ -1,9 +1,11 @@
<mat-progress-bar
*ngIf="fileDownloadItem && fileDownloadItem.downloadingProgress$"
mode="determinate"
[value]="fileDownloadItem.downloadingProgress$ | async"
></mat-progress-bar>
<ucap-file-viewer
[fileInfo]="fileInfo"
[downloadUrl]="downloadUrl"
[userSeq]="userSeq"
[deviceType]="deviceType"
[token]="token"
(download)="onDownload($event)"
[fileDownloadUrl]="fileDownloadUrl"
(download)="onDownload()"
(closed)="onClosedViewer()"
></ucap-file-viewer>

View File

@ -9,10 +9,14 @@ import {
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
import { NGXLogger } from 'ngx-logger';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { DeviceType, FileUtil } from '@ucap-webmessenger/core';
import { DeviceType, FileUtil, MimeUtil } from '@ucap-webmessenger/core';
import { NativeService, UCAP_NATIVE_SERVICE } from '@ucap-webmessenger/native';
import { take } from 'rxjs/operators';
import { take, map, finalize, tap } from 'rxjs/operators';
import { SnackBarService } from '@ucap-webmessenger/ui';
import {
FileDownloadItem,
CommonApiService
} from '@ucap-webmessenger/api-common';
export interface FileViewerDialogData {
fileInfo: FileEventJson;
@ -36,6 +40,10 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
deviceType: DeviceType;
token: string;
fileDownloadItem: FileDownloadItem;
fileDownloadUrl: string;
constructor(
public dialogRef: MatDialogRef<
FileViewerDialogData,
@ -44,6 +52,7 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
@Inject(MAT_DIALOG_DATA) public data: FileViewerDialogData,
@Inject(UCAP_NATIVE_SERVICE) private nativeService: NativeService,
private snackBarService: SnackBarService,
private commonApiService: CommonApiService,
private logger: NGXLogger
) {
this.fileInfo = data.fileInfo;
@ -51,36 +60,73 @@ export class FileViewerDialogComponent implements OnInit, OnDestroy {
this.userSeq = data.userSeq;
this.deviceType = data.deviceType;
this.token = data.token;
this.fileDownloadUrl = this.commonApiService.urlForFileTalkDownload(
{
userSeq: this.userSeq,
deviceType: this.deviceType,
token: this.token,
attachmentsSeq: this.fileInfo.attachmentSeq
},
this.downloadUrl
);
}
ngOnInit() {}
ngOnDestroy(): void {}
onDownload(blob: Blob): void {
FileUtil.fromBlobToBuffer(blob)
.then(buffer => {
this.nativeService
.saveFile(buffer, this.fileInfo.fileName)
.pipe(take(1))
.subscribe(result => {
if (!!result) {
this.snackBarService.open(
`파일이 경로[${result}]에 저장되었습니다.`,
'',
{
duration: 3000,
verticalPosition: 'bottom'
}
);
} else {
this.snackBarService.open('파일 저장에 실패하였습니다.');
}
});
})
.catch(reason => {
this.logger.error('download', reason);
});
onDownload(): void {
this.fileDownloadItem = new FileDownloadItem();
this.commonApiService
.fileTalkDownload(
{
userSeq: this.userSeq,
deviceType: this.deviceType,
token: this.token,
attachmentsSeq: this.fileInfo.attachmentSeq,
fileDownloadItem: this.fileDownloadItem
},
this.downloadUrl
)
.pipe(
take(1),
map(async rawBlob => {
const blob = rawBlob.slice(
0,
rawBlob.size,
MimeUtil.getMimeFromExtension(this.fileInfo.fileExt)
);
FileUtil.fromBlobToBuffer(blob)
.then(buffer => {
this.nativeService
.saveFile(buffer, this.fileInfo.fileName)
.pipe(take(1))
.subscribe(result => {
if (!!result) {
this.snackBarService.open(
`파일이 경로[${result}]에 저장되었습니다.`,
'',
{
duration: 3000,
verticalPosition: 'bottom'
}
);
} else {
this.snackBarService.open('파일 저장에 실패하였습니다.');
}
});
})
.catch(reason => {
this.logger.error('download', reason);
});
}),
finalize(() => {
this.fileDownloadItem = undefined;
})
)
.subscribe();
}
onClosedViewer(): void {

View File

@ -109,7 +109,7 @@
(moreEvent)="onMoreEvent($event)"
(massDetail)="onMassDetail($event)"
(save)="onSave($event)"
(imageViewer)="onImageViewer($event)"
(fileViewer)="onFileViewer($event)"
(contextMenu)="onContextMenuMessage($event)"
>
</ucap-chat-messages>

View File

@ -470,8 +470,8 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
);
}
async onImageViewer(fileInfo: FileEventJson) {
this.logger.debug('imageViewer', fileInfo);
async onFileViewer(fileInfo: FileEventJson) {
this.logger.debug('onFileViewer', fileInfo);
const result = await this.dialogService.open<
FileViewerDialogComponent,
FileViewerDialogData,

View File

@ -6,6 +6,7 @@
*ngSwitchCase="FileType.File"
[fileInfo]="fileInfo"
[expired]="getExpiredFile()"
(click)="onClickFileViewer(fileInfo)"
(save)="onSave($event)"
>
</ucap-chat-message-box-attach-file>
@ -13,6 +14,7 @@
*ngSwitchCase="FileType.Sound"
[fileInfo]="fileInfo"
[expired]="getExpiredFile()"
(click)="onClickFileViewer(fileInfo)"
(save)="onSave($event)"
>
</ucap-chat-message-box-attach-file>
@ -20,13 +22,13 @@
*ngSwitchCase="FileType.Image"
[fileInfo]="fileInfo"
[expired]="getExpiredFile()"
(click)="onClickImageViewer(fileInfo)"
(click)="onClickFileViewer(fileInfo)"
></ucap-chat-message-box-image>
<ucap-chat-message-box-video
*ngSwitchCase="FileType.Video"
[fileInfo]="fileInfo"
[expired]="getExpiredFile()"
(click)="onClickImageViewer(fileInfo)"
(click)="onClickFileViewer(fileInfo)"
></ucap-chat-message-box-video>
<ucap-chat-message-box-text
*ngSwitchDefault

View File

@ -22,7 +22,7 @@ export class FileComponent implements OnInit {
@Output()
save = new EventEmitter<{ fileInfo: FileEventJson; type: string }>();
@Output()
imageViewer = new EventEmitter<FileEventJson>();
fileViewer = new EventEmitter<FileEventJson>();
fileInfo?: FileEventJson;
errorMessage?: string;
@ -50,8 +50,8 @@ export class FileComponent implements OnInit {
}
}
onClickImageViewer(fileInfo: FileEventJson) {
this.imageViewer.emit(fileInfo);
onClickFileViewer(fileInfo: FileEventJson) {
this.fileViewer.emit(fileInfo);
}
onSave(value: string) {

View File

@ -111,7 +111,7 @@
[eventInfoStatus]="eventInfoStatus"
[message]="message"
(save)="onSave($event)"
(imageViewer)="onImageViewer($event)"
(fileViewer)="onFileViewer($event)"
(contextmenu)="onContextMenuMessage($event, message)"
>
</ucap-chat-message-box-file>

View File

@ -46,7 +46,7 @@ export class MessagesComponent implements OnInit {
@Output()
massDetail = new EventEmitter<number>();
@Output()
imageViewer = new EventEmitter<FileEventJson>();
fileViewer = new EventEmitter<FileEventJson>();
@Output()
save = new EventEmitter<{ fileInfo: FileInfo; type: string }>();
@Output()
@ -158,8 +158,8 @@ export class MessagesComponent implements OnInit {
}
/** [Event] Image Viewer */
onImageViewer(fileInfo: FileEventJson) {
this.imageViewer.emit(fileInfo);
onFileViewer(fileInfo: FileEventJson) {
this.fileViewer.emit(fileInfo);
}
/** [Event] Attach File Save & Save As */

View File

@ -5,51 +5,36 @@
<ucap-document-viewer
*ngSwitchCase="FileViewerType.Document"
[fileInfo]="fileInfo"
[downloadUrl]="downloadUrl"
[userSeq]="userSeq"
[deviceType]="deviceType"
[token]="token"
(download)="onDownload($event)"
[fileDownloadUrl]="fileDownloadUrl"
(download)="onDownload()"
(closed)="onClosedViewer()"
></ucap-document-viewer>
<ucap-image-viewer
*ngSwitchCase="FileViewerType.Image"
[fileInfo]="fileInfo"
[downloadUrl]="downloadUrl"
[userSeq]="userSeq"
[deviceType]="deviceType"
[token]="token"
(download)="onDownload($event)"
[fileDownloadUrl]="fileDownloadUrl"
(download)="onDownload()"
(closed)="onClosedViewer()"
></ucap-image-viewer>
<ucap-sound-viewer
*ngSwitchCase="FileViewerType.Sound"
[fileInfo]="fileInfo"
[downloadUrl]="downloadUrl"
[userSeq]="userSeq"
[deviceType]="deviceType"
[token]="token"
(download)="onDownload($event)"
[fileDownloadUrl]="fileDownloadUrl"
(download)="onDownload()"
(closed)="onClosedViewer()"
></ucap-sound-viewer>
<ucap-video-viewer
*ngSwitchCase="FileViewerType.Video"
[fileInfo]="fileInfo"
[downloadUrl]="downloadUrl"
[userSeq]="userSeq"
[deviceType]="deviceType"
[token]="token"
(download)="onDownload($event)"
[fileDownloadUrl]="fileDownloadUrl"
(download)="onDownload()"
(closed)="onClosedViewer()"
></ucap-video-viewer>
<ucap-binary-viewer
*ngSwitchDefault
[fileInfo]="fileInfo"
[downloadUrl]="downloadUrl"
[userSeq]="userSeq"
[deviceType]="deviceType"
[token]="token"
(download)="onDownload($event)"
[fileDownloadUrl]="fileDownloadUrl"
(download)="onDownload()"
(closed)="onClosedViewer()"
></ucap-binary-viewer>
</div>

View File

@ -1,9 +1,8 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ucapAnimations } from '../animations';
import { Info, FileEventJson } from '@ucap-webmessenger/protocol-event';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { FileViewerType } from '../types/file-viewer.type';
import { FileType } from '@ucap-webmessenger/protocol-file';
import { DeviceType } from '@ucap-webmessenger/core';
@Component({
selector: 'ucap-file-viewer',
@ -16,19 +15,10 @@ export class FileViewerComponent implements OnInit {
fileInfo: FileEventJson;
@Input()
downloadUrl: string;
@Input()
userSeq: number;
@Input()
deviceType: DeviceType;
@Input()
token: string;
fileDownloadUrl: string;
@Output()
download = new EventEmitter<Blob>();
download = new EventEmitter<void>();
@Output()
closed = new EventEmitter<void>();
@ -48,14 +38,21 @@ export class FileViewerComponent implements OnInit {
case FileType.Video:
return FileViewerType.Video;
default:
if (this.isSoundFile(fileInfo.fileExt)) {
return FileViewerType.Sound;
}
return FileViewerType.Binary;
}
}
onDownload(blob: Blob): void {
this.download.emit(blob);
onDownload(): void {
this.download.emit();
}
onClosedViewer(): void {
this.closed.emit();
}
private isSoundFile(fileExt: string): boolean {
return -1 !== ['mp3'].indexOf(fileExt);
}
}

View File

@ -1,18 +1,53 @@
<div class="ucap-image-viewer-container">
<mat-toolbar color="primary">
<span>Third Line</span>
<span class="ucap-image-viewer-spacer"></span>
<mat-icon
class="example-icon"
aria-hidden="false"
aria-label="Example heart icon"
>favorite</mat-icon
<div class="ucap-binary-viewer-container">
<mat-toolbar color="accent" class="ucap-binary-viewer-header">
<mat-icon class="ucap-binary-viewer-icon">attachment</mat-icon>
<span class="ucap-binary-viewer-title">{{ fileInfo.fileName }}</span>
<span class="ucap-binary-viewer-spacer"></span>
<button
mat-icon-button
class="ucap-binary-viewer-action"
matTooltip="다운로드"
matTooltipPosition="below"
aria-label=""
(click)="onClickDownload()"
>
<mat-icon
class="example-icon"
aria-hidden="false"
aria-label="Example delete icon"
>delete</mat-icon
<mat-icon>get_app</mat-icon>
</button>
<button
mat-raised-button
color="primary"
class="ucap-binary-viewer-action"
(click)="onClickClose()"
>
Close
</button>
</mat-toolbar>
<div class="ucap-binary-viewer-body">
<div
class="ucap-binary-viewer-content-wrapper"
fxLayout="column"
fxLayout.xs="row"
fxFlexFill
fxLayoutAlign="center center"
>
<div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.fileExt]">
<div class="ico"></div>
</div>
<div>
미리보기를 지원하지 않는 파일입니다.
</div>
<div>
<button
color="warn"
mat-raised-button
aria-label=""
(click)="onClickDownload()"
>
Download
</button>
</div>
</div>
</div>
</div>

View File

@ -6,8 +6,27 @@
width: 100%;
height: 50px;
.ucap-binary-viewer-icon {
}
.ucap-binary-viewer-title {
}
.ucap-binary-viewer-spacer {
flex: 1 1 auto;
}
.ucap-binary-viewer-action {
}
}
.ucap-binary-viewer-body {
position: relative;
width: 100%;
height: 100%;
background-color: white;
.ucap-binary-viewer-content-wrapper {
}
}
}

View File

@ -14,19 +14,10 @@ export class BinaryViewerComponent implements OnInit {
fileInfo: FileEventJson;
@Input()
downloadUrl: string;
@Input()
userSeq: number;
@Input()
deviceType: DeviceType;
@Input()
token: string;
fileDownloadUrl: string;
@Output()
download = new EventEmitter<Blob>();
download = new EventEmitter<void>();
@Output()
closed = new EventEmitter<void>();
@ -35,7 +26,9 @@ export class BinaryViewerComponent implements OnInit {
ngOnInit() {}
onClickDownload(): void {}
onClickDownload(): void {
this.download.emit();
}
onClickClose(): void {
this.closed.emit();

View File

@ -14,19 +14,10 @@ export class DocumentViewerComponent implements OnInit {
fileInfo: FileEventJson;
@Input()
downloadUrl: string;
@Input()
userSeq: number;
@Input()
deviceType: DeviceType;
@Input()
token: string;
fileDownloadUrl: string;
@Output()
download = new EventEmitter<Blob>();
download = new EventEmitter<void>();
@Output()
closed = new EventEmitter<void>();

View File

@ -51,11 +51,6 @@
Close
</button>
</mat-toolbar>
<mat-progress-bar
*ngIf="fileDownloadItem.downloadingProgress$ && !imageSrc"
mode="determinate"
[value]="fileDownloadItem.downloadingProgress$ | async"
></mat-progress-bar>
<div class="ucap-image-viewer-body">
<div
class="ucap-image-viewer-image-wrapper"
@ -64,7 +59,7 @@
fxFlexFill
fxLayoutAlign="center center"
>
<img *ngIf="imageSrc" [src]="imageSrc" />
<img *ngIf="fileDownloadUrl" [src]="fileDownloadUrl" />
</div>
</div>
</div>

View File

@ -1,13 +1,8 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { DeviceType, FileUtil, MimeUtil } from '@ucap-webmessenger/core';
import {
CommonApiService,
FileDownloadItem
} from '@ucap-webmessenger/api-common';
import { take, map } from 'rxjs/operators';
import { DomSanitizer } from '@angular/platform-browser';
import { DeviceType } from '@ucap-webmessenger/core';
import { CommonApiService } from '@ucap-webmessenger/api-common';
@Component({
selector: 'ucap-image-viewer',
@ -20,63 +15,20 @@ export class ImageViewerComponent implements OnInit {
fileInfo: FileEventJson;
@Input()
downloadUrl: string;
@Input()
userSeq: number;
@Input()
deviceType: DeviceType;
@Input()
token: string;
fileDownloadUrl: string;
@Output()
closed = new EventEmitter<void>();
@Output()
download = new EventEmitter<Blob>();
download = new EventEmitter<void>();
blob: Blob;
imageSrc: string | ArrayBuffer;
constructor() {}
fileDownloadItem: FileDownloadItem;
constructor(
private domSanitizer: DomSanitizer,
private commonApiService: CommonApiService
) {
this.fileDownloadItem = new FileDownloadItem();
}
ngOnInit() {
this.commonApiService
.fileTalkDownload(
{
userSeq: this.userSeq,
deviceType: this.deviceType,
token: this.token,
attachmentsSeq: this.fileInfo.attachmentSeq,
fileDownloadItem: this.fileDownloadItem
},
this.downloadUrl
)
.pipe(
take(1),
map(async blob => {
this.blob = blob.slice(
0,
blob.size,
MimeUtil.getMimeFromExtension(this.fileInfo.fileExt)
);
this.imageSrc = await FileUtil.fromBlobToDataUrl(this.blob);
})
)
.subscribe();
}
ngOnInit() {}
onClickDownload(): void {
this.download.emit(this.blob);
this.download.emit();
}
onClickClose(): void {

View File

@ -1,18 +1,84 @@
<div class="ucap-image-viewer-container">
<mat-toolbar color="primary">
<span>Third Line</span>
<span class="ucap-image-viewer-spacer"></span>
<mat-icon
class="example-icon"
aria-hidden="false"
aria-label="Example heart icon"
>favorite</mat-icon
<div class="ucap-sound-viewer-container">
<mat-toolbar color="accent" class="ucap-sound-viewer-header">
<mat-icon class="ucap-sound-viewer-icon">music_note</mat-icon>
<span class="ucap-sound-viewer-title">{{ fileInfo.fileName }}</span>
<span class="ucap-sound-viewer-spacer"></span>
<button
mat-icon-button
class="ucap-sound-viewer-action"
matTooltip="다운로드"
matTooltipPosition="below"
aria-label=""
(click)="onClickDownload()"
>
<mat-icon
class="example-icon"
aria-hidden="false"
aria-label="Example delete icon"
>delete</mat-icon
<mat-icon>get_app</mat-icon>
</button>
<button
mat-raised-button
color="primary"
class="ucap-sound-viewer-action"
(click)="onClickClose()"
>
Close
</button>
</mat-toolbar>
<div class="ucap-sound-viewer-body">
<div
class="ucap-sound-viewer-sound-icon"
fxLayout="row"
fxLayout.xs="column"
fxLayoutAlign="center center"
>
<audio
[src]="fileDownloadUrl"
#audioPlayer
(playing)="onPlayingAudio()"
(pause)="onPauseAudio()"
(timeupdate)="onTimeUpdateAudio()"
(volumechange)="onVolumeChangeAudio()"
(loadstart)="onLoadStartAudio()"
(loadeddata)="onLoadedDataAudio()"
></audio>
<mat-icon class="ucap-sound-viewer-icon">music_note</mat-icon>
</div>
<div
class="ucap-sound-viewer-sound-time"
fxLayout="row"
fxLayout.xs="column"
fxLayoutAlign="center center"
>
<mat-slider
#timeSlider
min="0"
[max]="duration"
[value]="currentTime"
(change)="onChangeTimeSlider($event)"
>
</mat-slider>
</div>
<div
class="ucap-sound-viewer-sound-controls"
fxLayout="row"
fxLayout.xs="column"
fxLayoutAlign="center center"
>
<div class="ucap-sound-viewer-sound-time-current">
{{ currentTime | ucapSecondsToMinutes }}
</div>
<span class="ucap-sound-viewer-spacer"></span>
<button
mat-icon-button
class="ucap-sound-viewer-action"
[matTooltip]="playing ? '멈춤' : '재생'"
aria-label=""
(click)="onClickPlayOrPause()"
>
<mat-icon>{{ playing ? 'pause' : 'play_arrow' }}</mat-icon>
</button>
<span class="ucap-sound-viewer-spacer"></span>
<div class="ucap-sound-viewer-sound-time-total">
{{ duration | ucapSecondsToMinutes }}
</div>
</div>
</div>
</div>

View File

@ -6,8 +6,48 @@
width: 100%;
height: 50px;
.ucap-sound-viewer-spacer {
flex: 1 1 auto;
.ucap-sound-viewer-icon {
}
.ucap-sound-viewer-title {
}
}
.ucap-sound-viewer-body {
position: relative;
background-color: white;
width: 100%;
height: 100%;
padding-bottom: 70px;
.ucap-sound-viewer-sound-icon {
width: 100%;
height: calc(100% - 60px);
}
.ucap-sound-viewer-sound-time {
width: 100%;
height: 30px;
}
.ucap-sound-viewer-sound-controls {
width: 100%;
height: 30px;
.ucap-sound-viewer-sound-time-current {
padding-left: 30px;
}
.ucap-sound-viewer-sound-time-total {
padding-right: 30px;
}
}
}
.ucap-sound-viewer-spacer {
flex: 1 1 auto;
}
.ucap-sound-viewer-action {
}
}
mat-slider {
width: 95%;
}

View File

@ -1,7 +1,15 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ViewChild,
ElementRef
} from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { DeviceType } from '@ucap-webmessenger/core';
import { MatSlider, MatSliderChange } from '@angular/material';
@Component({
selector: 'ucap-sound-viewer',
@ -14,28 +22,76 @@ export class SoundViewerComponent implements OnInit {
fileInfo: FileEventJson;
@Input()
downloadUrl: string;
@Input()
userSeq: number;
@Input()
deviceType: DeviceType;
@Input()
token: string;
fileDownloadUrl: string;
@Output()
download = new EventEmitter<Blob>();
download = new EventEmitter<void>();
@Output()
closed = new EventEmitter<void>();
@ViewChild('audioPlayer', { static: true })
audioPlayer: ElementRef<HTMLAudioElement>;
@ViewChild('timeSlider', { static: true })
timeSlider: MatSlider;
playing = false;
duration = 0.01;
currentTime = 0;
volume = 0.1;
loading = false;
constructor() {}
ngOnInit() {}
onClickDownload(): void {}
onClickPlayOrPause(): void {
if (this.loading) {
return;
}
if (this.audioPlayer.nativeElement.paused) {
this.audioPlayer.nativeElement.play();
} else {
this.currentTime = this.audioPlayer.nativeElement.currentTime;
this.audioPlayer.nativeElement.pause();
}
}
onChangeTimeSlider(e: MatSliderChange): void {
console.log('onChangeTimeSlider', e.value);
this.audioPlayer.nativeElement.currentTime = e.value;
}
onPlayingAudio(): void {
this.playing = true;
// this.duration = Math.floor(this.audioPlayer.nativeElement.duration);
}
onPauseAudio(): void {
this.playing = false;
}
onTimeUpdateAudio(): void {
this.currentTime = Math.floor(this.audioPlayer.nativeElement.currentTime);
}
onVolumeChangeAudio(): void {
this.volume = Math.floor(this.audioPlayer.nativeElement.volume);
}
onLoadStartAudio(): void {
this.loading = true;
}
onLoadedDataAudio(): void {
this.loading = false;
this.duration = Math.floor(this.audioPlayer.nativeElement.duration);
}
onClickDownload(): void {
this.download.emit();
}
onClickClose(): void {
this.closed.emit();

View File

@ -1,18 +1,84 @@
<div class="ucap-image-viewer-container">
<mat-toolbar color="primary">
<span>Third Line</span>
<span class="ucap-image-viewer-spacer"></span>
<mat-icon
class="example-icon"
aria-hidden="false"
aria-label="Example heart icon"
>favorite</mat-icon
<div class="ucap-video-viewer-container">
<mat-toolbar color="accent" class="ucap-video-viewer-header">
<mat-icon class="ucap-video-viewer-icon">video_label</mat-icon>
<span class="ucap-video-viewer-title">{{ fileInfo.fileName }}</span>
<span class="ucap-video-viewer-spacer"></span>
<button
mat-icon-button
class="ucap-video-viewer-action"
matTooltip="다운로드"
matTooltipPosition="below"
aria-label=""
(click)="onClickDownload()"
>
<mat-icon
class="example-icon"
aria-hidden="false"
aria-label="Example delete icon"
>delete</mat-icon
<mat-icon>get_app</mat-icon>
</button>
<button
mat-raised-button
color="primary"
class="ucap-video-viewer-action"
(click)="onClickClose()"
>
Close
</button>
</mat-toolbar>
<div class="ucap-video-viewer-body">
<div
class="ucap-video-viewer-video-icon"
fxLayout="row"
fxLayout.xs="column"
fxLayoutAlign="center center"
>
<video
[src]="fileDownloadUrl"
#audioPlayer
(playing)="onPlayingVideo()"
(pause)="onPauseVideo()"
(timeupdate)="onTimeUpdateVideo()"
(volumechange)="onVolumeChangeVideo()"
(loadstart)="onLoadStartVideo()"
(loadeddata)="onLoadedDataVideo()"
></video>
</div>
<div
class="ucap-video-viewer-video-time"
fxLayout="row"
fxLayout.xs="column"
fxLayoutAlign="center center"
>
<mat-slider
#timeSlider
min="0"
[max]="duration"
[value]="currentTime"
(change)="onChangeTimeSlider($event)"
>
</mat-slider>
</div>
<div
class="ucap-video-viewer-video-controls"
fxLayout="row"
fxLayout.xs="column"
fxLayoutAlign="center center"
>
<div class="ucap-video-viewer-video-time-current">
{{ currentTime | ucapSecondsToMinutes }}
</div>
<span class="ucap-video-viewer-spacer"></span>
<button
mat-icon-button
class="ucap-video-viewer-action"
[matTooltip]="playing ? '멈춤' : '재생'"
aria-label=""
(click)="onClickPlayOrPause()"
>
<mat-icon>{{ playing ? 'pause' : 'play_arrow' }}</mat-icon>
</button>
<span class="ucap-video-viewer-spacer"></span>
<div class="ucap-video-viewer-video-time-total">
{{ duration | ucapSecondsToMinutes }}
</div>
</div>
</div>
</div>

View File

@ -6,8 +6,48 @@
width: 100%;
height: 50px;
.ucap-video-viewer-spacer {
flex: 1 1 auto;
.ucap-video-viewer-icon {
}
.ucap-video-viewer-title {
}
}
.ucap-video-viewer-body {
position: relative;
background-color: white;
width: 100%;
height: 100%;
padding-bottom: 70px;
.ucap-video-viewer-video-icon {
width: 100%;
height: calc(100% - 60px);
}
.ucap-video-viewer-video-time {
width: 100%;
height: 30px;
}
.ucap-video-viewer-video-controls {
width: 100%;
height: 30px;
.ucap-video-viewer-video-time-current {
padding-left: 30px;
}
.ucap-video-viewer-video-time-total {
padding-right: 30px;
}
}
}
.ucap-video-viewer-spacer {
flex: 1 1 auto;
}
.ucap-video-viewer-action {
}
}
mat-slider {
width: 95%;
}

View File

@ -1,7 +1,15 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ViewChild,
ElementRef
} from '@angular/core';
import { ucapAnimations } from '../../animations';
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
import { DeviceType } from '@ucap-webmessenger/core';
import { MatSlider, MatSliderChange } from '@angular/material';
@Component({
selector: 'ucap-video-viewer',
@ -14,28 +22,76 @@ export class VideoViewerComponent implements OnInit {
fileInfo: FileEventJson;
@Input()
downloadUrl: string;
@Input()
userSeq: number;
@Input()
deviceType: DeviceType;
@Input()
token: string;
fileDownloadUrl: string;
@Output()
download = new EventEmitter<Blob>();
download = new EventEmitter<void>();
@Output()
closed = new EventEmitter<void>();
@ViewChild('audioPlayer', { static: true })
audioPlayer: ElementRef<HTMLVideoElement>;
@ViewChild('timeSlider', { static: true })
timeSlider: MatSlider;
playing = false;
duration = 0.01;
currentTime = 0;
volume = 0.1;
loading = false;
constructor() {}
ngOnInit() {}
onClickDownload(): void {}
onClickPlayOrPause(): void {
if (this.loading) {
return;
}
if (this.audioPlayer.nativeElement.paused) {
this.audioPlayer.nativeElement.play();
} else {
this.currentTime = this.audioPlayer.nativeElement.currentTime;
this.audioPlayer.nativeElement.pause();
}
}
onChangeTimeSlider(e: MatSliderChange): void {
console.log('onChangeTimeSlider', e.value);
this.audioPlayer.nativeElement.currentTime = e.value;
}
onPlayingVideo(): void {
this.playing = true;
// this.duration = Math.floor(this.audioPlayer.nativeElement.duration);
}
onPauseVideo(): void {
this.playing = false;
}
onTimeUpdateVideo(): void {
this.currentTime = Math.floor(this.audioPlayer.nativeElement.currentTime);
}
onVolumeChangeVideo(): void {
this.volume = Math.floor(this.audioPlayer.nativeElement.volume);
}
onLoadStartVideo(): void {
this.loading = true;
}
onLoadedDataVideo(): void {
this.loading = false;
this.duration = Math.floor(this.audioPlayer.nativeElement.duration);
}
onClickDownload(): void {
this.download.emit();
}
onClickClose(): void {
this.closed.emit();

View File

@ -0,0 +1,10 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: 'ucapSecondsToMinutes' })
export class SecondsToMinutesPipe implements PipeTransform {
public transform(time: number): string {
const minutes = ('0' + Math.floor(time / 60)).slice(-2);
const seconds = ('0' + (time % 60)).slice(-2);
return `${minutes}:${seconds}`;
}
}

View File

@ -9,6 +9,7 @@ import { MatCardModule } from '@angular/material/card';
import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatSliderModule } from '@angular/material/slider';
import { MatSnackBarModule } from '@angular/material/snack-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
@ -39,6 +40,7 @@ import { ConfirmDialogComponent } from './dialogs/confirm.dialog.component';
import { BytesPipe } from './pipes/bytes.pipe';
import { LinefeedToHtmlPipe, HtmlToLinefeedPipe } from './pipes/linefeed.pipe';
import { DateToStringForChatRoomListPipe } from './pipes/dates.pipe';
import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe';
const COMPONENTS = [
FileUploadQueueComponent,
@ -61,7 +63,8 @@ const PIPES = [
BytesPipe,
LinefeedToHtmlPipe,
HtmlToLinefeedPipe,
DateToStringForChatRoomListPipe
DateToStringForChatRoomListPipe,
SecondsToMinutesPipe
];
const SERVICES = [
BottomSheetService,
@ -79,6 +82,7 @@ const SERVICES = [
MatDialogModule,
MatIconModule,
MatProgressBarModule,
MatSliderModule,
MatSnackBarModule,
MatToolbarModule,
MatTooltipModule,