This commit is contained in:
khk 2019-12-31 16:03:54 +09:00
commit e110849fbe
63 changed files with 741 additions and 94 deletions

@ -82,7 +82,7 @@ const mainConfig: webpack.Configuration = {
), ),
new CopyWebpackPlugin([ new CopyWebpackPlugin([
{ {
from: 'ucap-webmessenger-electron/resources/**/*', from: 'ucap-webmessenger-electron/assets/**/*',
to: path.resolve(__dirname, '..', 'dist'), to: path.resolve(__dirname, '..', 'dist'),
context: 'electron-projects' context: 'electron-projects'
} }

@ -23,9 +23,25 @@
"!**/.awcache", "!**/.awcache",
"!**/.vscode", "!**/.vscode",
"!config/", "!config/",
"!dist/",
"!docker/", "!docker/",
"!tsconfig.json", "!tsconfig.json",
"!tslint.json" "!tslint.json",
{
"filter": ["**/*"],
"from": "./dist/ucap-webmessenger-app/",
"to": "./renderer/"
},
{
"filter": ["**/*"],
"from": "./dist/ucap-webmessenger-electron/",
"to": "./electron/"
},
{
"filter": ["**/*"],
"from": "./config/build/${os}/icon/daesang/",
"to": "./assets/icon/"
}
], ],
"protocols": { "protocols": {
"name": "DS Talk", "name": "DS Talk",

@ -165,11 +165,7 @@ export class AppWindow {
} else { } else {
this.window.loadURL( this.window.loadURL(
url.format({ url.format({
pathname: path.join( pathname: path.join(__dirname, '..', 'renderer/index.html'),
__dirname,
'..',
'ucap-webmessenger-app/index.html'
),
protocol: 'file:', protocol: 'file:',
slashes: true slashes: true
}) })

@ -52,13 +52,18 @@ const appIconPath = __LINUX__
? path.join( ? path.join(
__dirname, __dirname,
'../../', '../../',
'config/build/linux/icon/woori/', 'config/build/linux/icon/daesang/',
'256x256.png' '256x256.png'
) )
: path.join(__dirname, 'resources/linuxicon', '256x256.png') : path.join(__dirname, '..', '/assets/icon/', '256x256.png')
: __DEV__ : __DEV__
? path.join(__dirname, 'resources/image', '64_64.png') ? path.join(
: path.join(__dirname, 'resources/image', '64_64.png'); __dirname,
'../../',
'config/build/win/icon/daesang/',
'16x16.ico'
)
: path.join(__dirname, '..', '/assets/icon/', '16x16.ico');
let appWindow: AppWindow | null = null; let appWindow: AppWindow | null = null;
let appTray: Tray | null = null; let appTray: Tray | null = null;
@ -266,12 +271,12 @@ app.on(ElectronAppChannel.Ready, () => {
notificationService.options.defaultWindow.webPreferences.preload = path.join( notificationService.options.defaultWindow.webPreferences.preload = path.join(
__dirname, __dirname,
'resources/notification/preload.js' 'assets/notification/preload.js'
); );
notificationService.templatePath = path.join( notificationService.templatePath = path.join(
__dirname, __dirname,
'resources/notification/template.html' 'assets/notification/template.html'
); );
updateWindowService = new ElectronUpdateWindowService({ updateWindowService = new ElectronUpdateWindowService({
@ -299,7 +304,7 @@ app.on(ElectronAppChannel.Ready, () => {
updateWindowService.templatePath = path.join( updateWindowService.templatePath = path.join(
__dirname, __dirname,
'resources/update-window/template.html' 'assets/update-window/template.html'
); );
// updateWindowService.show(); // updateWindowService.show();
@ -589,15 +594,12 @@ ipcMain.on(
text: noti.contents, text: noti.contents,
image: image:
noti.image || noti.image ||
path.join( path.join(__dirname, 'assets/notification/images/img_nophoto_50.png'),
__dirname,
'resources/notification/images/img_nophoto_50.png'
),
sound: noti.useSound sound: noti.useSound
? path.join( ? path.join(
'file://', 'file://',
__dirname, __dirname,
'resources/notification/sounds/messageAlarm.mp3' 'assets/notification/sounds/messageAlarm.mp3'
) )
: '', : '',
displayTime: !!noti.displayTime ? noti.displayTime : undefined, displayTime: !!noti.displayTime ? noti.displayTime : undefined,

@ -3,7 +3,7 @@ import * as path from 'path';
// tslint:disable-next-line: variable-name // tslint:disable-next-line: variable-name
const _root = __DEV__ const _root = __DEV__
? path.resolve(__dirname, '..', '..') ? path.resolve(__dirname, '..', '..')
: path.resolve(__dirname, '..', '..'); : path.resolve(__dirname, '..');
export function root(...paths: string[]) { export function root(...paths: string[]) {
const args = Array.prototype.slice.call(paths, 0); const args = Array.prototype.slice.call(paths, 0);

@ -145,5 +145,5 @@
"webpack-node-externals": "^1.7.2", "webpack-node-externals": "^1.7.2",
"zone.js": "~0.9.1" "zone.js": "~0.9.1"
}, },
"main": "./dist/ucap-webmessenger-electron/main.js" "main": "./electron/main.js"
} }

@ -4,7 +4,9 @@ import {
APIResponse, APIResponse,
APIEncoder, APIEncoder,
APIDecoder, APIDecoder,
ParameterUtil ParameterUtil,
StatusCode,
JsonAnalization
} from '@ucap-webmessenger/api'; } from '@ucap-webmessenger/api';
export interface TransMassTalkDownloadRequest extends APIRequest { export interface TransMassTalkDownloadRequest extends APIRequest {
@ -15,23 +17,41 @@ export interface TransMassTalkDownloadRequest extends APIRequest {
} }
export interface TransMassTalkDownloadResponse extends APIResponse { export interface TransMassTalkDownloadResponse extends APIResponse {
Original?: string; content?: string;
Translation?: string; userName?: string;
Locale?: string; regDate?: string;
UserName?: string; returnJson?: any;
RegDate?: string;
} }
const transMassTalkDownloadEncodeMap = {}; const transMassTalkDownloadEncodeMap = {
userSeq: 'p_user_seq',
deviceType: 'p_device_type',
token: 'p_token',
eventTransSeq: 'p_event_mass_seq'
};
export const encodeTransMassTalkDownload: APIEncoder< export const encodeTransMassTalkDownload: APIEncoder<TransMassTalkDownloadRequest> = (
TransMassTalkDownloadRequest req: TransMassTalkDownloadRequest
> = (req: TransMassTalkDownloadRequest) => { ) => {
return ParameterUtil.encode(transMassTalkDownloadEncodeMap, req); return ParameterUtil.encode(transMassTalkDownloadEncodeMap, req);
}; };
export const decodeTransMassTalkDownload: APIDecoder< export const decodeTransMassTalkDownload: APIDecoder<TransMassTalkDownloadResponse> = (
TransMassTalkDownloadResponse res: any
> = (res: any) => { ) => {
return {} as TransMassTalkDownloadResponse; try {
const json = JsonAnalization.receiveAnalization(res);
return {
statusCode: json.StatusCode,
content: json.Content,
userName: json.UserName,
regDate: json.RegDate,
returnJson: res
} as TransMassTalkDownloadResponse;
} catch (e) {
return {
statusCode: StatusCode.Fail,
errorMessage: e
} as TransMassTalkDownloadResponse;
}
}; };

@ -49,16 +49,15 @@ export const decodeTransMassTalkSave: APIDecoder<TransMassTalkSaveResponse> = (
res: any res: any
) => { ) => {
try { try {
const json = JsonAnalization.receiveAnalization(res);
return { return {
statusCode: json.StatusCode, statusCode: res.StatusCode,
translationSeq: json.EventTransSEQ, translationSeq: res.EventTransSEQ,
roomSeq: json.RoomID, roomSeq: res.RoomID,
registrationDate: json.RegDate, registrationDate: res.RegDate,
locale: json.Locale, locale: res.Locale,
original: json.Original, original: res.Original,
translation: json.Translation, translation: res.Translation,
returnJson: res returnJson: JSON.stringify(res)
} as TransMassTalkSaveResponse; } as TransMassTalkSaveResponse;
} catch (e) { } catch (e) {
return { return {

@ -4,7 +4,9 @@ import {
APIResponse, APIResponse,
APIEncoder, APIEncoder,
APIDecoder, APIDecoder,
ParameterUtil ParameterUtil,
StatusCode,
JsonAnalization
} from '@ucap-webmessenger/api'; } from '@ucap-webmessenger/api';
export interface TranslationReqRequest extends APIRequest { export interface TranslationReqRequest extends APIRequest {
@ -17,13 +19,21 @@ export interface TranslationReqRequest extends APIRequest {
} }
export interface TranslationReqResponse extends APIResponse { export interface TranslationReqResponse extends APIResponse {
SrcLocale?: string; srcLocale?: string;
DestLocale?: string; destLocale?: string;
Original?: string; original?: string;
Translation?: string; translation?: string;
returnJson?: any;
} }
const translationReqEncodeMap = {}; const translationReqEncodeMap = {
userSeq: 'p_user_seq',
deviceType: 'p_device_type',
token: 'p_token',
original: 'p_original',
srcLocale: 'p_src_locale',
destLocale: 'p_dest_locale'
};
export const encodeTranslationReq: APIEncoder<TranslationReqRequest> = ( export const encodeTranslationReq: APIEncoder<TranslationReqRequest> = (
req: TranslationReqRequest req: TranslationReqRequest
@ -34,5 +44,20 @@ export const encodeTranslationReq: APIEncoder<TranslationReqRequest> = (
export const decodeTranslationReq: APIDecoder<TranslationReqResponse> = ( export const decodeTranslationReq: APIDecoder<TranslationReqResponse> = (
res: any res: any
) => { ) => {
try {
return {
statusCode: res.StatusCode,
srcLocale: res.SrcLocale,
destLocale: res.DestLocale,
original: res.Original,
translation: res.Translation,
returnJson: JSON.stringify(res)
} as TranslationReqResponse;
} catch (e) {
return {
statusCode: StatusCode.Fail,
errorMessage: e
} as TranslationReqResponse;
}
return {} as TranslationReqResponse; return {} as TranslationReqResponse;
}; };

@ -20,7 +20,7 @@ export interface TranslationSaveRequest extends APIRequest {
} }
export interface TranslationSaveResponse extends APIResponse { export interface TranslationSaveResponse extends APIResponse {
translationSeq?: string; translationSeq?: number;
roomSeq?: string; roomSeq?: string;
registrationDate?: string; registrationDate?: string;
srcLocale?: string; srcLocale?: string;
@ -50,17 +50,16 @@ export const decodeTranslationSave: APIDecoder<TranslationSaveResponse> = (
res: any res: any
) => { ) => {
try { try {
const json = JsonAnalization.receiveAnalization(res);
return { return {
statusCode: json.StatusCode, statusCode: res.StatusCode,
translationSeq: json.EventTransSEQ, translationSeq: !!res.EventTransSeq ? Number(res.EventTransSeq) : 0,
roomSeq: json.RoomID, roomSeq: res.RoomID,
registrationDate: json.RegDate, registrationDate: res.RegDate,
srcLocale: json.SrcLocale, srcLocale: res.SrcLocale,
destLocale: json.DestLocale, destLocale: res.DestLocale,
original: json.Original, original: res.Original,
translation: json.Translation, translation: res.Translation,
returnJson: res returnJson: JSON.stringify(res)
} as TranslationSaveResponse; } as TranslationSaveResponse;
} catch (e) { } catch (e) {
return { return {

@ -19,7 +19,7 @@ export function createTranslateLoader(nativeService: NativeService) {
break; break;
case NativeType.Electron: case NativeType.Electron:
prefix = environment.production prefix = environment.production
? '/dist/ucap-webmessenger-app/assets/i18n/' ? '/renderer/assets/i18n/'
: '/projects/ucap-webmessenger-app/src/assets/i18n/'; : '/projects/ucap-webmessenger-app/src/assets/i18n/';
break; break;

@ -233,6 +233,7 @@
[minShowReadHere]=" [minShowReadHere]="
environment.productConfig.CommonSetting.readHereShowMinimumEventCount environment.productConfig.CommonSetting.readHereShowMinimumEventCount
" "
[translationSimpleview]="translationSimpleview"
(moreEvent)="onMoreEvent($event)" (moreEvent)="onMoreEvent($event)"
(massDetail)="onMassDetail($event)" (massDetail)="onMassDetail($event)"
(save)="onSave($event)" (save)="onSave($event)"
@ -266,6 +267,25 @@
<div></div> <div></div>
</div> </div>
<!-- / sticker-selector --> <!-- / sticker-selector -->
<!-- Translation -->
<div class="translation-container">
<ucap-translation-section
*ngIf="isShowTranslation"
[destLocale]="destLocale"
[simpleView]="translationSimpleview"
[preView]="translationPreview"
[translationPreviewInfo]="translationPreviewInfo"
(changeTranslationSimpleview)="onChangeTranslationSimpleView($event)"
(changeTranslationPreview)="onChangeTranslationPreView($event)"
(changeDestLocale)="onChangeDestLocale($event)"
(cancelTranslation)="onCancelTranslation($event)"
(sendTranslationMessage)="onSendTranslationMessage($event)"
>
</ucap-translation-section>
</div>
<!-- / Translation -->
<!-- CHAT FOOTER --> <!-- CHAT FOOTER -->
<div fxFlex="0 0 auto" fxLayout="column" *ngIf="getEnableSend()"> <div fxFlex="0 0 auto" fxLayout="column" *ngIf="getEnableSend()">
<!-- REPLY FORM --> <!-- REPLY FORM -->
@ -276,6 +296,7 @@
(sendFiles)="onFileSelected($event)" (sendFiles)="onFileSelected($event)"
(clearView)="clearView()" (clearView)="clearView()"
(toggleStickerSelector)="onShowToggleStickerSelector($event)" (toggleStickerSelector)="onShowToggleStickerSelector($event)"
(toggleTranslation)="onShowToggleTranslation($event)"
></ucap-chat-form> ></ucap-chat-form>
<!-- / REPLY FORM --> <!-- / REPLY FORM -->
</div> </div>

@ -127,6 +127,22 @@
width: 100%; width: 100%;
} }
} }
.translation-container {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
background-color: transparent;
.translation-zone {
position: absolute;
padding: 10px 10px 0 10px;
background-color: rgba(250, 255, 255, 0.8);
bottom: 0;
width: 100%;
}
}
} }
::ng-deep .chat-snackbar-class { ::ng-deep .chat-snackbar-class {

@ -23,7 +23,7 @@ import {
} from '@ucap-webmessenger/ui'; } from '@ucap-webmessenger/ui';
import { Store, select } from '@ngrx/store'; import { Store, select } from '@ngrx/store';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { Observable, Subscription, forkJoin, of, combineLatest } from 'rxjs'; import { Observable, Subscription, forkJoin, combineLatest, of } from 'rxjs';
import { import {
Info, Info,
EventType, EventType,
@ -35,7 +35,8 @@ import {
EventJson, EventJson,
FileEventJson, FileEventJson,
StickerEventJson, StickerEventJson,
MassTextEventJson MassTextEventJson,
TranslationEventJson
} from '@ucap-webmessenger/protocol-event'; } from '@ucap-webmessenger/protocol-event';
import * as AppStore from '@app/store'; import * as AppStore from '@app/store';
@ -72,7 +73,9 @@ import { FileUploadItem } from '@ucap-webmessenger/api';
import { import {
CommonApiService, CommonApiService,
FileTalkSaveRequest, FileTalkSaveRequest,
FileTalkSaveResponse FileTalkSaveResponse,
TranslationReqRequest,
TranslationSaveRequest
} from '@ucap-webmessenger/api-common'; } from '@ucap-webmessenger/api-common';
import { import {
CreateChatDialogComponent, CreateChatDialogComponent,
@ -166,6 +169,13 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
isShowStickerSelector = false; isShowStickerSelector = false;
selectedSticker: StickerFilesInfo; selectedSticker: StickerFilesInfo;
/** About Translation */
isShowTranslation = false;
translationSimpleview = false;
translationPreview = false;
destLocale = 'en'; // default English :: en
translationPreviewInfo: TranslationEventJson | null;
/** About ReadHere */ /** About ReadHere */
firstcheckReadHere = true; firstcheckReadHere = true;
clearReadHere = false; clearReadHere = false;
@ -360,8 +370,17 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
// Chat Search Clear.. // Chat Search Clear..
this.onCloseSearchArea(); this.onCloseSearchArea();
// Translate Clear..
this.isShowTranslation = false;
this.translationSimpleview = false;
this.translationPreview = false;
// this.destLocale = 'en'; // default English :: en
this.translationPreviewInfo = null;
// Read here Clear..
this.firstcheckReadHere = true; this.firstcheckReadHere = true;
// Chat Formfield Clear..
if (!!this.chatForm) { if (!!this.chatForm) {
this.chatForm.replyForm.reset(); this.chatForm.replyForm.reset();
} }
@ -578,6 +597,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
} }
} }
/** Send Event */
async onSendMessage(message: string) { async onSendMessage(message: string) {
this.setEventMoreInit(); this.setEventMoreInit();
@ -598,8 +618,77 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
} }
} }
if (!!this.selectedSticker) { if (!!this.isShowTranslation && this.destLocale.trim().length > 0) {
// Send Sticker /** CASE : Translation */
console.log('번역들어간다.');
const destLocale = this.destLocale;
const original = message;
const roomSeq = this.roomInfo.roomSeq;
this.commonApiService
.translationSave({
userSeq: this.loginRes.userSeq,
deviceType: this.environmentsInfo.deviceType,
token: this.loginRes.tokenString,
roomSeq,
original,
srcLocale: '',
destLocale
} as TranslationSaveRequest)
.pipe(
take(1),
map(res => {
console.log(res);
if (res.statusCode === StatusCode.Success) {
if (res.translationSeq > 0) {
// Mass Text Translation
} else {
// Normal Text Translation
const json: TranslationEventJson = {
locale: destLocale,
original,
translation: res.translation,
stickername: '',
stickerfile: !!this.selectedSticker
? this.selectedSticker.index
: ''
};
if (!!this.translationPreview) {
// preview
this.translationPreviewInfo = json;
} else {
// direct send
this.store.dispatch(
EventStore.send({
senderSeq: this.loginRes.userSeq,
req: {
roomSeq,
eventType: EventType.Translation,
sentMessage: JSON.stringify(json)
}
})
);
if (!!this.translationPreviewInfo) {
this.translationPreviewInfo = null;
}
}
if (!!this.selectedSticker) {
this.isShowStickerSelector = false;
this.setStickerHistory(this.selectedSticker);
}
}
} else {
this.logger.error(res);
}
}),
catchError(error => of(this.logger.error(error)))
)
.subscribe();
} else if (!!this.selectedSticker) {
/** CASE : Sticker */
if ( if (
!!message && !!message &&
message.trim().length > message.trim().length >
@ -640,7 +729,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
message.trim().length > message.trim().length >
environment.productConfig.CommonSetting.masstextLength environment.productConfig.CommonSetting.masstextLength
) { ) {
// MASS TEXT /** CASE : MASS TEXT */
this.store.dispatch( this.store.dispatch(
EventStore.sendMass({ EventStore.sendMass({
senderSeq: this.loginRes.userSeq, senderSeq: this.loginRes.userSeq,
@ -653,6 +742,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
}) })
); );
} else { } else {
/** CASE : Normal Text */
this.store.dispatch( this.store.dispatch(
EventStore.send({ EventStore.send({
senderSeq: this.loginRes.userSeq, senderSeq: this.loginRes.userSeq,
@ -1361,4 +1451,37 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
} }
} }
} }
/** About Translation */
onShowToggleTranslation() {
this.isShowTranslation = !this.isShowTranslation;
if (!this.isShowTranslation) {
}
}
onChangeTranslationSimpleView(value: boolean) {
this.translationSimpleview = value;
}
onChangeTranslationPreView(value: boolean) {
this.translationPreview = value;
}
onChangeDestLocale(destLocale: string) {
this.destLocale = destLocale;
}
onCancelTranslation() {
this.translationPreviewInfo = null;
}
onSendTranslationMessage(translationInfo: TranslationEventJson) {
this.store.dispatch(
EventStore.send({
senderSeq: this.loginRes.userSeq,
req: {
roomSeq: this.roomInfo.roomSeq,
eventType: EventType.Translation,
sentMessage: JSON.stringify(translationInfo)
}
})
);
this.translationPreviewInfo = null;
}
} }

@ -5,21 +5,21 @@ export interface TranslationEventJson {
locale?: string; locale?: string;
original?: string; original?: string;
translation?: string; translation?: string;
stickerName?: string; stickername?: string;
stickerFile?: string; stickerfile?: string;
} }
export const decodeTranslationEventJson: EventJsonDecoder< export const decodeTranslationEventJson: EventJsonDecoder<TranslationEventJson> = (
TranslationEventJson message: string
> = (message: string) => { ) => {
try { try {
const json = JsonAnalization.receiveAnalization(message); const json = JsonAnalization.receiveAnalization(message);
return { return {
locale: json.locale, locale: json.locale,
original: json.original, original: json.original,
translation: json.translation, translation: json.translation,
stickerName: json.stickername, stickername: json.stickername,
stickerFile: json.stickerfile stickerfile: json.stickerfile
} as TranslationEventJson; } as TranslationEventJson;
} catch (e) { } catch (e) {
return {} as TranslationEventJson; return {} as TranslationEventJson;

@ -24,9 +24,13 @@
<mat-icon>sentiment_satisfied_alt</mat-icon> <mat-icon>sentiment_satisfied_alt</mat-icon>
</button> </button>
<!-- <button mat-icon-button class="material-icons"> <button
mat-icon-button
class="material-icons"
(click)="onClickTranslation()"
>
<mat-icon>g_translate</mat-icon> <mat-icon>g_translate</mat-icon>
</button> --> </button>
</div> </div>
<form <form

@ -28,6 +28,8 @@ export class FormComponent implements OnInit {
@Output() @Output()
toggleStickerSelector = new EventEmitter<void>(); toggleStickerSelector = new EventEmitter<void>();
@Output()
toggleTranslation = new EventEmitter<void>();
@Output() @Output()
clearView = new EventEmitter(); clearView = new EventEmitter();
@ -78,4 +80,8 @@ export class FormComponent implements OnInit {
onClickStickerSelector() { onClickStickerSelector() {
this.toggleStickerSelector.emit(); this.toggleStickerSelector.emit();
} }
onClickTranslation() {
this.toggleTranslation.emit();
}
} }

@ -0,0 +1,24 @@
<div class="translation-main">
<div class="original">
{{ message.sentMessageJson.translation }}
</div>
<div class="translation">
<span class="language">Kor</span>
녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요
장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요
장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트
</div>
<div class="btn-box">
<ul>
<li>
<button mat-button>Save</button>
</li>
<li>
<button mat-button><span class="language">Kor</span>번역보기</button>
</li>
</ul>
</div>
</div>

@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { TranslationEventJson, Info } from '@ucap-webmessenger/protocol-event';
@Component({ @Component({
selector: 'ucap-chat-message-box-mass-translation', selector: 'ucap-chat-message-box-mass-translation',
@ -6,6 +7,9 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./mass-translation.component.scss'] styleUrls: ['./mass-translation.component.scss']
}) })
export class MassTranslationComponent implements OnInit { export class MassTranslationComponent implements OnInit {
@Input()
message: Info<TranslationEventJson>;
constructor() {} constructor() {}
ngOnInit() {} ngOnInit() {}

@ -1,17 +1,30 @@
<div class="translation-main"> <div class="translation-main">
<div class="original"> <div
{{ message.sentMessageJson.translation }} *ngIf="
!!this.message.sentMessageJson &&
!!this.message.sentMessageJson.stickerfile &&
this.message.sentMessageJson.stickerfile.trim().length > 0
"
class="sticker"
>
<img
[src]="stickerUrl"
onerror="this.src='assets/sticker/sticker_default.png'"
/>
</div> </div>
<div class="translation"> <div
<span class="language">Kor</span> *ngIf="!translationSimpleview || (!!translationSimpleview && !!isMe)"
녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 class="original"
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 [innerHTML]="message.sentMessageJson.original | linky"
장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 ></div>
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 <div
장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 *ngIf="!translationSimpleview || (!!translationSimpleview && !isMe)"
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트 class="translation"
>
<span class="language">{{ message.sentMessageJson.locale }}</span>
<span [innerHTML]="message.sentMessageJson.translation | linky"> </span>
</div> </div>
<div class="btn-box"> <!-- <div class="btn-box">
<ul> <ul>
<li> <li>
<button mat-button>Save</button> <button mat-button>Save</button>
@ -20,5 +33,5 @@
<button mat-button><span class="language">Kor</span>번역보기</button> <button mat-button><span class="language">Kor</span>번역보기</button>
</li> </li>
</ul> </ul>
</div> </div> -->
</div> </div>

@ -10,7 +10,23 @@ export class TranslationComponent implements OnInit {
@Input() @Input()
message: Info<TranslationEventJson>; message: Info<TranslationEventJson>;
@Input()
translationSimpleview: boolean;
@Input()
isMe: boolean;
stickerUrl?: string;
constructor() {} constructor() {}
ngOnInit() {} ngOnInit() {
if (
!!this.message.sentMessageJson &&
!!this.message.sentMessageJson.stickerfile &&
this.message.sentMessageJson.stickerfile.trim().length > 0
) {
this.stickerUrl = `assets/sticker/sticker_s_${this.message.sentMessageJson.stickerfile}.png`;
}
}
} }

@ -196,6 +196,13 @@
(contextmenu)="onContextMenuMessage($event, message)" (contextmenu)="onContextMenuMessage($event, message)"
> >
</ucap-chat-message-box-schedule> </ucap-chat-message-box-schedule>
<ucap-chat-message-box-translation
*ngSwitchCase="EventType.Translation"
[message]="message"
[translationSimpleview]="translationSimpleview"
[isMe]="message.senderSeq === loginRes.userSeq"
class="information-msg"
></ucap-chat-message-box-translation>
<ucap-chat-message-box-allim <ucap-chat-message-box-allim
*ngSwitchCase="EventType.AllimTms" *ngSwitchCase="EventType.AllimTms"
@ -209,13 +216,12 @@
class="information-msg" class="information-msg"
> >
</ucap-chat-message-box-allim> </ucap-chat-message-box-allim>
<div *ngSwitchDefault> <div *ngSwitchDefault>
<!-- mass-translation <!-- mass-translation
<ucap-chat-message-box-mass-translation></ucap-chat-message-box-mass-translation> <ucap-chat-message-box-mass-translation></ucap-chat-message-box-mass-translation>
notice notice
<ucap-chat-message-box-notice></ucap-chat-message-box-notice> <ucap-chat-message-box-notice></ucap-chat-message-box-notice>
translation
<ucap-chat-message-box-translation></ucap-chat-message-box-translation>
video-conference video-conference
<ucap-chat-message-box-video-conference></ucap-chat-message-box-video-conference> <ucap-chat-message-box-video-conference></ucap-chat-message-box-video-conference>

@ -50,6 +50,8 @@ export class MessagesComponent implements OnInit {
clearReadHere: boolean; clearReadHere: boolean;
@Input() @Input()
minShowReadHere = 10; minShowReadHere = 10;
@Input()
translationSimpleview = false;
@Output() @Output()
openProfile = new EventEmitter<UserInfo>(); openProfile = new EventEmitter<UserInfo>();

@ -0,0 +1,108 @@
<div
*ngIf="
isShowTranslationPreview &&
!!translationPreviewInfo &&
!!translationPreviewInfo.translation
"
class="translation-preview"
>
<span class="translation-section">
{{ translationPreviewInfo.translation }}
</span>
<button
mat-stroked-button
(click)="onClickSendTranslationMessage(translationPreviewInfo)"
>
Send
</button>
<span class="btn-close">
<button
mat-button
matSuffix
mat-icon-button
aria-label="Close"
(click)="onClickTranslationCancel()"
>
<mat-icon>close</mat-icon>
</button>
</span>
</div>
<div>
<form name="translationForm" [formGroup]="translationForm">
<mat-form-field>
<mat-label>Dest Type</mat-label>
<mat-select
formControlName="destType"
(selectionChange)="onChangeSelection($event)"
>
<mat-option value="">번역없음</mat-option>
<mat-option *ngFor="let dest of translationDestList" [value]="dest.key"
>{{ dest.text }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-slide-toggle
labelPosition="before"
[checked]="simpleView"
(change)="onChangToggleSimpleview($event)"
>Simple View</mat-slide-toggle
>
<mat-slide-toggle
labelPosition="before"
[checked]="preView"
(change)="onChangTogglePreview($event)"
>Preview</mat-slide-toggle
>
</form>
</div>
<!-- <div *ngIf="currentSticker" class="selected-sticker">
<img [src]="getStickerContentsImage(currentSticker)" />
<span class="btn-close">
<button
mat-button
matSuffix
mat-icon-button
aria-label="Close"
(click)="onClickClose()"
>
<mat-icon>close</mat-icon>
</button>
</span>
</div>
<div class="sticker-selector">
<mat-tab-group
mat-stretch-tabs
animationDuration="0ms"
(selectedIndexChange)="onSelectedIndexChange($event)"
>
<mat-tab
*ngFor="let stickerInfo of stickerInfoList; let idx = index"
[aria-label]="stickerInfo.title"
>
<ng-template mat-tab-label>
<img
#stickerTitle
[matTooltip]="stickerInfo.title"
matTooltipPosition="after"
[src]="getStickerTitleImage(stickerInfo, false, idx)"
(mouseover)="
stickerTitle.src = getStickerTitleImage(stickerInfo, true, idx)
"
(mouseout)="
stickerTitle.src = getStickerTitleImage(stickerInfo, false, idx)
"
/>
</ng-template>
<div fxFlex fxLayout="row" fxLayoutGap="20px" class="sticker-item-box">
<div
*ngFor="let sticker of getStickerInfos(stickerInfo)"
(click)="onClickSelectSticker(sticker)"
class="sticker-item"
>
<img [src]="getStickerContentsImage(sticker)" />
</div>
</div>
</mat-tab>
</mat-tab-group>
</div> -->

@ -0,0 +1,34 @@
.sticker-selector {
border-top: 1px solid #cccccc;
// .sticker-item-box {
// flex-wrap: wrap;
// padding: 20px 20px 0;
// overflow: auto;
// }
}
// .sticker-item {
// width: 60px;
// margin-bottom: 20px;
// display: inline-flex;
// cursor: pointer;
// }
.selected-sticker {
position: relative;
height: 100px;
padding: 20px;
text-align: right;
background-color: rgba(0, 0, 0, 0.4);
// img {
// margin-right: 40px;
// }
// .btn-close {
// position: absolute;
// top: 2px;
// right: 20px;
// width: 40px;
// height: 40px;
// color: #ffffff;
// }
}

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

@ -0,0 +1,181 @@
import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core';
import {
StickerInfo,
StickerFilesInfo,
StickerUtil,
StickerMap
} from '@ucap-webmessenger/core';
import { FormGroup, FormBuilder } from '@angular/forms';
import { MatSlideToggleChange, MatSelectChange } from '@angular/material';
import { TranslationEventJson } from '@ucap-webmessenger/protocol-event';
@Component({
selector: 'ucap-translation-section',
templateUrl: './translation-section.component.html',
styleUrls: ['./translation-section.component.scss']
})
export class TranslationSectionComponent implements OnInit {
@Input()
destLocale: string;
@Input()
simpleView: boolean;
@Input()
preView: boolean;
@Input()
translationPreviewInfo: TranslationEventJson | null;
@Output()
changeTranslationSimpleview = new EventEmitter<boolean>();
@Output()
changeTranslationPreview = new EventEmitter<boolean>();
@Output()
changeDestLocale = new EventEmitter<string>();
@Output()
cancelTranslation = new EventEmitter<void>();
@Output()
sendTranslationMessage = new EventEmitter<TranslationEventJson>();
isShowTranslationSimpleview = false;
isShowTranslationPreview = false;
translationDestList: {
key: string;
text: string;
}[] = [
{ key: 'af', text: 'Afrikaans' },
{ key: 'sq', text: 'Albanian' },
{ key: 'am', text: 'Amharic' },
{ key: 'ar', text: 'Arabic' },
{ key: 'hy', text: 'Armenian' },
{ key: 'az', text: 'Azeerbaijani' },
{ key: 'eu', text: 'Basque' },
{ key: 'be', text: 'Belarusian' },
{ key: 'bn', text: 'Bengali' },
{ key: 'bs', text: 'Bosnian' },
{ key: 'bg', text: 'Bulgarian' },
{ key: 'ca', text: 'Catalan' },
{ key: 'ceb', text: 'Cebuano' },
{ key: 'zh-CN', text: 'Chinese(Simplified)' },
{ key: 'zh-TW', text: 'Chinese(Traditional)' },
{ key: 'co', text: 'Corsican' },
{ key: 'hr', text: 'Croatian' },
{ key: 'cs', text: 'Czech' },
{ key: 'da', text: 'Danish' },
{ key: 'nl', text: 'Dutch' },
{ key: 'en', text: 'English' },
{ key: 'eo', text: 'Esperanto' },
{ key: 'fi', text: 'Finnish' },
{ key: 'fr', text: 'French' },
{ key: 'fy', text: 'Frisian' },
{ key: 'gl', text: 'Galician' },
{ key: 'ka', text: 'Georgian' },
{ key: 'de', text: 'German' },
{ key: 'el', text: 'Greek' },
{ key: 'gu', text: 'Gujarati' },
{ key: 'ht', text: 'Haitian Creole' },
{ key: 'ha', text: 'Hausa' },
{ key: 'haw', text: 'Hawaiian' },
{ key: 'iw', text: 'Hebrew' },
{ key: 'hi', text: 'Hindi' },
{ key: 'hmn', text: 'Hmong' },
{ key: 'hu', text: 'Hungarian' },
{ key: 'is', text: 'Icelandic' },
{ key: 'ig', text: 'Igbo' },
{ key: 'id', text: 'Indonesian' },
{ key: 'ga', text: 'Irish' },
{ key: 'it', text: 'Italian' },
{ key: 'ja', text: 'Japanese' },
{ key: 'jw', text: 'Javanese' },
{ key: 'kn', text: 'Kannada' },
{ key: 'kk', text: 'Kazakh' },
{ key: 'km', text: 'Khmer' },
{ key: 'ko', text: 'Korean' },
{ key: 'ku', text: 'Kurdish' },
{ key: 'ky', text: 'Kyrgyz' },
{ key: 'lo', text: 'Lao' },
{ key: 'la', text: 'Latin' },
{ key: 'lv', text: 'Latvian' },
{ key: 'lt', text: 'Lithuanian' },
{ key: 'lb', text: 'Luxembourgish' },
{ key: 'mk', text: 'Macedonian' },
{ key: 'mg', text: 'Malagasy' },
{ key: 'ms', text: 'Malay' },
{ key: 'ml', text: 'Malayalam' },
{ key: 'mi', text: 'Maori' },
{ key: 'mr', text: 'Marathi' },
{ key: 'mn', text: 'Mongolian' },
{ key: 'my', text: 'Myanmar(Burmese)' },
{ key: 'ne', text: 'Nepali' },
{ key: 'no', text: 'Norwegian' },
{ key: 'ny', text: 'Nyanja(Chichewa)' },
{ key: 'ps', text: 'Pashto' },
{ key: 'fa', text: 'Persian' },
{ key: 'pl', text: 'Polish' },
{ key: 'pt', text: 'Portuguese' },
{ key: 'pa', text: 'Punjabi' },
{ key: 'ro', text: 'Romanian' },
{ key: 'ru', text: 'Russian' },
{ key: 'sm', text: 'Samoan' },
{ key: 'gd', text: 'Scots Gaelic' },
{ key: 'sr', text: 'Serbian' },
{ key: 'st', text: 'Sesotho' },
{ key: 'sn', text: 'Shona' },
{ key: 'sd', text: 'Sindhi' },
{ key: 'si', text: 'Sinhala(Sinhalese)' },
{ key: 'sk', text: 'Slovak' },
{ key: 'sl', text: 'Slovenian' },
{ key: 'so', text: 'Somali' },
{ key: 'es', text: 'Spanish' },
{ key: 'su', text: 'Sundanese' },
{ key: 'sw', text: 'Swahili' },
{ key: 'sv', text: 'Swedish' },
{ key: 'tl', text: 'Tagalog(Filipino)' },
{ key: 'tg', text: 'Tajik' },
{ key: 'ta', text: 'Tamil' },
{ key: 'te', text: 'Telugu' },
{ key: 'th', text: 'Thai' },
{ key: 'tr', text: 'Turkish' },
{ key: 'uk', text: 'Ukrainian' },
{ key: 'ur', text: 'Urdu' },
{ key: 'uz', text: 'Uzbek' },
{ key: 'vi', text: 'Vietnamese' },
{ key: 'cy', text: 'Welsh' },
{ key: 'xh', text: 'Xhosa' },
{ key: 'yi', text: 'Yiddish' },
{ key: 'yo', text: 'Yoruba' },
{ key: 'zu', text: 'Zulu' }
];
translationForm: FormGroup;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.translationForm = this.formBuilder.group({
destType: [this.destLocale]
});
}
onChangeSelection(event: MatSelectChange) {
this.destLocale = event.value;
this.changeDestLocale.emit(this.destLocale);
}
onChangToggleSimpleview(event: MatSlideToggleChange) {
this.isShowTranslationSimpleview = event.checked;
this.changeTranslationSimpleview.emit(event.checked);
}
onChangTogglePreview(event: MatSlideToggleChange) {
this.isShowTranslationPreview = event.checked;
this.changeTranslationPreview.emit(event.checked);
}
onClickSendTranslationMessage(translationInfo: TranslationEventJson) {
this.sendTranslationMessage.emit(translationInfo);
}
onClickTranslationCancel() {
this.cancelTranslation.emit();
}
}

@ -19,6 +19,11 @@ import { MatFormFieldModule } from '@angular/material/form-field';
import { MatButtonToggleModule } from '@angular/material/button-toggle'; import { MatButtonToggleModule } from '@angular/material/button-toggle';
import { MatMenuModule } from '@angular/material/menu'; import { MatMenuModule } from '@angular/material/menu';
import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDatepickerModule } from '@angular/material/datepicker';
import {
MatTabsModule,
MatSelectModule,
MatSlideToggleModule,
} from '@angular/material';
import { DragDropModule } from '@angular/cdk/drag-drop'; import { DragDropModule } from '@angular/cdk/drag-drop';
@ -66,12 +71,12 @@ import { LinkyPipe } from './pipes/linky.pipe';
import { TranslatePipe } from './pipes/translate.pipe'; import { TranslatePipe } from './pipes/translate.pipe';
import { DatePipe } from './pipes/date.pipe'; import { DatePipe } from './pipes/date.pipe';
import { MatTabsModule } from '@angular/material';
import { import {
StringEmptyCheckPipe, StringEmptyCheckPipe,
StringFormatterPhonePipe StringFormatterPhonePipe
} from './pipes/string.pipe'; } from './pipes/string.pipe';
import { ClickDebounceDirective } from './directives/click-debounce.directive'; import { ClickDebounceDirective } from './directives/click-debounce.directive';
import { TranslationSectionComponent } from './components/translation-section.component';
const COMPONENTS = [ const COMPONENTS = [
FileUploadQueueComponent, FileUploadQueueComponent,
@ -83,6 +88,7 @@ const COMPONENTS = [
PickDateComponent, PickDateComponent,
PickTimeComponent, PickTimeComponent,
StepInputComponent, StepInputComponent,
TranslationSectionComponent,
BinaryViewerComponent, BinaryViewerComponent,
DocumentViewerComponent, DocumentViewerComponent,
@ -142,6 +148,8 @@ const SERVICES = [
MatButtonToggleModule, MatButtonToggleModule,
MatMenuModule, MatMenuModule,
MatDatepickerModule, MatDatepickerModule,
MatSelectModule,
MatSlideToggleModule,
DragDropModule DragDropModule
], ],
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES], exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],