Merge branch 'master' of https://git.loafle.net/ucap-web/next-ucap-messenger
This commit is contained in:
commit
4fc2f89ce5
|
@ -23,8 +23,8 @@ export class AppWindow {
|
|||
// tslint:disable-next-line: variable-name
|
||||
private _rendererReadyTime: number | null = null;
|
||||
|
||||
private minWidth = 700;
|
||||
private minHeight = 600;
|
||||
private minWidth = 1160;
|
||||
private minHeight = 800;
|
||||
|
||||
public constructor(private appIconPath: string) {
|
||||
const savedWindowState = windowStateKeeper({
|
||||
|
@ -165,7 +165,11 @@ export class AppWindow {
|
|||
} else {
|
||||
this.window.loadURL(
|
||||
url.format({
|
||||
pathname: path.join(__dirname, '..', 'ucap-webmessenger-app/index.html'),
|
||||
pathname: path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'ucap-webmessenger-app/index.html'
|
||||
),
|
||||
protocol: 'file:',
|
||||
slashes: true
|
||||
})
|
||||
|
|
|
@ -6,7 +6,8 @@ import {
|
|||
Menu,
|
||||
shell,
|
||||
dialog,
|
||||
BrowserWindow
|
||||
BrowserWindow,
|
||||
clipboard
|
||||
} from 'electron';
|
||||
import path from 'path';
|
||||
import fse from 'fs-extra';
|
||||
|
@ -29,7 +30,8 @@ import {
|
|||
ChatChannel,
|
||||
MessengerChannel,
|
||||
MessageChannel,
|
||||
AppChannel
|
||||
AppChannel,
|
||||
ClipboardChannel
|
||||
} from '@ucap-webmessenger/native-electron';
|
||||
import { ElectronNotificationService } from '@ucap-webmessenger/electron-notification';
|
||||
import { ElectronUpdateWindowService } from '@ucap-webmessenger/electron-update-window';
|
||||
|
@ -729,6 +731,25 @@ ipcMain.on(
|
|||
}
|
||||
);
|
||||
|
||||
ipcMain.on(ClipboardChannel.Read, (event: IpcMainEvent, ...args: any[]) => {
|
||||
try {
|
||||
const text = clipboard.readText('clipboard');
|
||||
const rtf = clipboard.readRTF('clipboard');
|
||||
const html = clipboard.readHTML('clipboard');
|
||||
const image = clipboard.readImage('clipboard');
|
||||
|
||||
event.returnValue = {
|
||||
text,
|
||||
rtf,
|
||||
html,
|
||||
image: !image.isEmpty() ? image.toBitmap() : undefined,
|
||||
imageDataUrl: !image.isEmpty() ? image.toDataURL() : undefined
|
||||
};
|
||||
} catch (error) {
|
||||
event.returnValue = {};
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.on(AppChannel.Exit, (event: IpcMainEvent, ...args: any[]) => {
|
||||
appExit();
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Observable, Subject } from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import { FileUtil } from '@ucap-webmessenger/core';
|
||||
|
||||
export class FileUploadItem {
|
||||
file: File;
|
||||
|
@ -24,6 +25,24 @@ export class FileUploadItem {
|
|||
return fileItems;
|
||||
}
|
||||
|
||||
static fromDataUrls(
|
||||
fileNameAppend: string,
|
||||
...dataUrls: string[]
|
||||
): FileUploadItem[] {
|
||||
const fileItems: FileUploadItem[] = [];
|
||||
|
||||
// tslint:disable-next-line: prefer-for-of
|
||||
for (let i = 0; i < dataUrls.length; i++) {
|
||||
fileItems.push(
|
||||
new FileUploadItem(
|
||||
FileUtil.fromDataUrlToFile(fileNameAppend, dataUrls[i])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return fileItems;
|
||||
}
|
||||
|
||||
static from(): FileUploadItem {
|
||||
return new FileUploadItem();
|
||||
}
|
||||
|
|
|
@ -327,6 +327,7 @@
|
|||
(clearView)="clearView()"
|
||||
(toggleStickerSelector)="onShowToggleStickerSelector($event)"
|
||||
(toggleTranslation)="onShowToggleTranslation($event)"
|
||||
(clipboardPaste)="onClipboardPaste()"
|
||||
></ucap-chat-form>
|
||||
<!-- / REPLY FORM -->
|
||||
</div>
|
||||
|
|
|
@ -8,7 +8,8 @@ import {
|
|||
EventEmitter,
|
||||
Inject,
|
||||
ChangeDetectorRef,
|
||||
ChangeDetectionStrategy
|
||||
ChangeDetectionStrategy,
|
||||
ElementRef
|
||||
} from '@angular/core';
|
||||
import {
|
||||
ucapAnimations,
|
||||
|
@ -130,6 +131,11 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
import { TranslatePipe } from 'projects/ucap-webmessenger-ui/src/lib/pipes/translate.pipe';
|
||||
import { TranslateService as UiTranslateService } from '@ucap-webmessenger/ui';
|
||||
import { FileProtocolService } from '@ucap-webmessenger/protocol-file';
|
||||
import {
|
||||
ClipboardDialogComponent,
|
||||
ClipboardDialogData,
|
||||
ClipboardDialogResult
|
||||
} from '../dialogs/chat/clipboard.dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout-messenger-messages',
|
||||
|
@ -1958,4 +1964,47 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.isTranslationProcess = false;
|
||||
this.translationPreviewInfo = null;
|
||||
}
|
||||
|
||||
onClipboardPaste() {
|
||||
this.nativeService.readFromClipboard().then(async data => {
|
||||
if (!!data.image && !!data.text) {
|
||||
const result = await this.dialogService.open<
|
||||
ClipboardDialogComponent,
|
||||
ClipboardDialogData,
|
||||
ClipboardDialogResult
|
||||
>(ClipboardDialogComponent, {
|
||||
width: '800px',
|
||||
maxWidth: '800px',
|
||||
height: '800px',
|
||||
minHeight: '800px',
|
||||
disableClose: false,
|
||||
data: {
|
||||
content: data
|
||||
}
|
||||
});
|
||||
|
||||
if (result.selected.text) {
|
||||
this.onSendMessage(data.text);
|
||||
}
|
||||
|
||||
if (result.selected.image) {
|
||||
const fileUploadItems = FileUploadItem.fromDataUrls(
|
||||
'clipboard',
|
||||
data.imageDataUrl
|
||||
);
|
||||
|
||||
this.onFileSelected(fileUploadItems);
|
||||
}
|
||||
} else if (!!data.image && !data.text) {
|
||||
const fileUploadItems = FileUploadItem.fromDataUrls(
|
||||
'clipboard',
|
||||
data.imageDataUrl
|
||||
);
|
||||
|
||||
this.onFileSelected(fileUploadItems);
|
||||
} else {
|
||||
this.chatForm.replyInput.nativeElement.value = `${this.chatForm.replyInput.nativeElement.value}${data.text}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<mat-card class="confirm-card mat-elevation-z setting-frame">
|
||||
<mat-card-header cdkDrag cdkDragRootElement=".cdk-overlay-pane" cdkDragHandle>
|
||||
<mat-card-title>{{ 'settings.label' | translate }}</mat-card-title>
|
||||
<button class="icon-button btn-dialog-close" (click)="onClickChoice(false)">
|
||||
<i class="mdi mdi-window-close"></i>
|
||||
</button>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<div fxFlex class="setting-tab">
|
||||
<mat-tab-group animationDuration="0ms">
|
||||
<mat-tab *ngIf="data.content.text">
|
||||
<ng-template mat-tab-label>
|
||||
<mat-checkbox #chkText> </mat-checkbox>
|
||||
<span class="title-text">{{
|
||||
'common.file.clipboardType.text' | translate
|
||||
}}</span>
|
||||
</ng-template>
|
||||
<perfect-scrollbar>
|
||||
<div>{{ data.content.text }}</div>
|
||||
</perfect-scrollbar>
|
||||
</mat-tab>
|
||||
<!-- <mat-tab *ngIf="data.content.rtf">
|
||||
<ng-template mat-tab-label>
|
||||
<mat-checkbox #chkRtf="matCheckbox"> </mat-checkbox>
|
||||
<span class="title-text">{{
|
||||
'common.file.clipboardType.rtf' | translate
|
||||
}}</span>
|
||||
</ng-template>
|
||||
<perfect-scrollbar>
|
||||
<div>{{ data.content.rtf }}</div>
|
||||
</perfect-scrollbar>
|
||||
</mat-tab> -->
|
||||
<!-- <mat-tab *ngIf="data.content.html">
|
||||
<ng-template mat-tab-label>
|
||||
<mat-checkbox #chkHtml="matCheckbox"> </mat-checkbox>
|
||||
<span class="title-text">{{
|
||||
'common.file.clipboardType.html' | translate
|
||||
}}</span>
|
||||
</ng-template>
|
||||
|
||||
<perfect-scrollbar>
|
||||
<div fxFlexFill>
|
||||
<table
|
||||
fxFlexFill
|
||||
[innerHTML]="data.content.html | ucapSafeHtml"
|
||||
></table>
|
||||
</div>
|
||||
</perfect-scrollbar>
|
||||
</mat-tab> -->
|
||||
<mat-tab *ngIf="data.content.image && data.content.imageDataUrl">
|
||||
<ng-template mat-tab-label>
|
||||
<mat-checkbox #chkImage> </mat-checkbox>
|
||||
<span class="title-text">{{
|
||||
'common.file.clipboardType.image' | translate
|
||||
}}</span>
|
||||
</ng-template>
|
||||
|
||||
<perfect-scrollbar>
|
||||
<div>
|
||||
<img [src]="data.content.imageDataUrl" />
|
||||
</div>
|
||||
</perfect-scrollbar>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="button-farm flex-row">
|
||||
<button
|
||||
mat-stroked-button
|
||||
(click)="onClickChoice(false)"
|
||||
class="mat-primary"
|
||||
>
|
||||
{{ 'common.messages.no' | translate }}
|
||||
</button>
|
||||
<button mat-flat-button (click)="onClickChoice(true)" class="mat-primary">
|
||||
{{ 'common.messages.yes' | translate }}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
|
@ -0,0 +1,127 @@
|
|||
::ng-deep .setting-frame {
|
||||
padding: 16px;
|
||||
height: 100%;
|
||||
min-width: 500px;
|
||||
position: relative;
|
||||
|
||||
.mat-dialog-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mat-card-header {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
border-bottom: 1px solid #dddddd;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.btn-dialog-close {
|
||||
font-size: 20px;
|
||||
display: flex;
|
||||
margin-left: auto;
|
||||
align-self: flex-start;
|
||||
color: #444444;
|
||||
}
|
||||
}
|
||||
.mat-card-content {
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
height: calc(100% - 100px);
|
||||
border-bottom: 1px solid #dddddd;
|
||||
.setting-tab {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.title-text {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.mat-tab-group {
|
||||
flex-direction: row;
|
||||
.mat-tab-header {
|
||||
width: 160px;
|
||||
.mat-tab-labels {
|
||||
flex-direction: column;
|
||||
.mat-tab-label {
|
||||
padding: 0 10px;
|
||||
align-content: flex-start;
|
||||
text-align: left;
|
||||
align-items: self-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
.mat-ink-bar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-farm {
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 10px;
|
||||
.mat-primary {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .setting-tab {
|
||||
.mat-tab-group {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.mat-tab-header {
|
||||
width: 160px;
|
||||
flex-flow: column;
|
||||
border-right: 1px solid #dddddd;
|
||||
.mat-tab-label-container {
|
||||
.mat-tab-list {
|
||||
.mat-tab-labels {
|
||||
border-bottom: 0;
|
||||
padding-right: 10px;
|
||||
.mat-tab-label {
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mat-tab-body-wrapper {
|
||||
.mat-tab-body {
|
||||
.mat-tab-body-conten {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.mat-list-base {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .setting-category {
|
||||
.mat-list-base {
|
||||
position: relative;
|
||||
.mat-list-item {
|
||||
font-size: 15px;
|
||||
.mat-tab-header {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
.mat-divider {
|
||||
//margin-top: 10px;
|
||||
}
|
||||
.mat-subheader {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ClipboardDialogComponent } from './clipboard.dialog.component';
|
||||
|
||||
describe('ClipboardDialogComponent', () => {
|
||||
let component: ClipboardDialogComponent;
|
||||
let fixture: ComponentFixture<ClipboardDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ClipboardDialogComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ClipboardDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
Inject,
|
||||
AfterViewInit,
|
||||
ViewChild
|
||||
} from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA, MatCheckbox } from '@angular/material';
|
||||
|
||||
export interface ClipboardDialogData {
|
||||
content: {
|
||||
text?: string;
|
||||
rtf?: string;
|
||||
html?: string;
|
||||
image?: Buffer;
|
||||
imageDataUrl?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ClipboardDialogResult {
|
||||
selected?: {
|
||||
text?: boolean;
|
||||
rtf?: boolean;
|
||||
html?: boolean;
|
||||
image?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout-messenger-clipboard',
|
||||
templateUrl: './clipboard.dialog.component.html',
|
||||
styleUrls: ['./clipboard.dialog.component.scss']
|
||||
})
|
||||
export class ClipboardDialogComponent implements OnInit, AfterViewInit {
|
||||
@ViewChild('chkText', { static: false })
|
||||
chkText: MatCheckbox;
|
||||
|
||||
@ViewChild('chkImage', { static: false })
|
||||
chkImage: MatCheckbox;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<ClipboardDialogData, ClipboardDialogResult>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: ClipboardDialogData
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngAfterViewInit(): void {}
|
||||
|
||||
onClickChoice(choice: boolean): void {
|
||||
let selected: {
|
||||
text?: boolean;
|
||||
rtf?: boolean;
|
||||
html?: boolean;
|
||||
image?: boolean;
|
||||
};
|
||||
|
||||
if (choice) {
|
||||
selected = {
|
||||
text: !!this.chkText && this.chkText.checked,
|
||||
image: !!this.chkImage && this.chkImage.checked
|
||||
};
|
||||
} else {
|
||||
}
|
||||
|
||||
this.dialogRef.close({ selected });
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { CreateChatDialogComponent } from './create-chat.dialog.component';
|
||||
import { EditChatRoomDialogComponent } from './edit-chat-room.dialog.component';
|
||||
import { MassDetailComponent } from './mass-detail.component';
|
||||
import { ClipboardDialogComponent } from './clipboard.dialog.component';
|
||||
|
||||
export const DIALOGS = [
|
||||
CreateChatDialogComponent,
|
||||
EditChatRoomDialogComponent,
|
||||
MassDetailComponent
|
||||
MassDetailComponent,
|
||||
ClipboardDialogComponent
|
||||
];
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
padding: 0 10px;
|
||||
align-content: flex-start;
|
||||
text-align: left;
|
||||
align-items: self-start;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -402,6 +402,13 @@
|
|||
"openDownloadFolder": "Open download folder",
|
||||
"selectFiles": "Select files",
|
||||
"dropZoneForUpload": "Drop files here to upload.",
|
||||
"clipboard": "Clipboard",
|
||||
"clipboardType": {
|
||||
"text": "Text",
|
||||
"rtf": "Rich Text Format",
|
||||
"html": "HTML",
|
||||
"image": "Image"
|
||||
},
|
||||
"type": {
|
||||
"label": "Type of file",
|
||||
"images": "Images",
|
||||
|
|
|
@ -402,6 +402,13 @@
|
|||
"openDownloadFolder": "다운로드 폴더 열기",
|
||||
"selectFiles": "파일을 선택하세요",
|
||||
"dropZoneForUpload": "여기에 파일을 Drop하시면 업로드 됩니다.",
|
||||
"clipboard": "클립보드",
|
||||
"clipboardType": {
|
||||
"text": "텍스트",
|
||||
"rtf": "리치 텍스트 포맷",
|
||||
"html": "HTML",
|
||||
"image": "이미지"
|
||||
},
|
||||
"type": {
|
||||
"label": "파일 종류",
|
||||
"images": "이미지",
|
||||
|
|
|
@ -42,6 +42,28 @@ export class FileUtil {
|
|||
});
|
||||
}
|
||||
|
||||
static fromDataUrlToFile(fileNameAppender: string, dataUrl: string): File {
|
||||
const BASE64_MARKER = ';base64,';
|
||||
// tslint:disable-next-line: variable-name
|
||||
const isDataURI = (_url: string) => _url.split(BASE64_MARKER).length === 2;
|
||||
|
||||
if (!isDataURI(dataUrl)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const mime = dataUrl.split(BASE64_MARKER)[0].split(':')[1];
|
||||
const filename =
|
||||
fileNameAppender + new Date().getTime() + '.' + mime.split('/')[1];
|
||||
const bytes = atob(dataUrl.split(BASE64_MARKER)[1]);
|
||||
const writer = new Uint8Array(new ArrayBuffer(bytes.length));
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
writer[i] = bytes.charCodeAt(i);
|
||||
}
|
||||
|
||||
return new File([writer.buffer], filename, { type: mime });
|
||||
}
|
||||
|
||||
static fromBlobToString(blob: Blob): Promise<string> {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const fileReader = new FileReader();
|
||||
|
|
|
@ -281,6 +281,24 @@ export class BrowserNativeService extends NativeService {
|
|||
open(url);
|
||||
}
|
||||
|
||||
readFromClipboard(): Promise<{
|
||||
text?: string;
|
||||
rtf?: string;
|
||||
html?: string;
|
||||
image?: Buffer;
|
||||
imageDataUrl?: string;
|
||||
}> {
|
||||
return new Promise<{
|
||||
text?: string;
|
||||
rtf?: string;
|
||||
html?: string;
|
||||
image?: Buffer;
|
||||
imageDataUrl?: string;
|
||||
}>((resolve, reject) => {
|
||||
resolve({});
|
||||
});
|
||||
}
|
||||
|
||||
constructor(private httpClient: HttpClient) {
|
||||
super();
|
||||
this.notificationService = new NotificationService();
|
||||
|
|
|
@ -23,7 +23,8 @@ import {
|
|||
MessengerChannel,
|
||||
MessageChannel,
|
||||
ProcessChannel,
|
||||
AppChannel
|
||||
AppChannel,
|
||||
ClipboardChannel
|
||||
} from '../types/channel.type';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateLoaderService } from '../translate/electron-loader';
|
||||
|
@ -470,6 +471,28 @@ export class ElectronNativeService implements NativeService {
|
|||
this.shell.openExternal(url);
|
||||
}
|
||||
|
||||
readFromClipboard(): Promise<{
|
||||
text?: string;
|
||||
rtf?: string;
|
||||
html?: string;
|
||||
image?: Buffer;
|
||||
imageDataUrl?: string;
|
||||
}> {
|
||||
return new Promise<{
|
||||
text?: string;
|
||||
rtf?: string;
|
||||
html?: string;
|
||||
image?: Buffer;
|
||||
imageDataUrl?: string;
|
||||
}>((resolve, reject) => {
|
||||
try {
|
||||
resolve(this.ipcRenderer.sendSync(ClipboardChannel.Read));
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get isElectron() {
|
||||
return window && (window as any).process && (window as any).process.type;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,10 @@ export enum IdleStateChannel {
|
|||
ChangeLimitTime = 'UCAP::idleState::changeLimitTime'
|
||||
}
|
||||
|
||||
export enum ClipboardChannel {
|
||||
Read = 'UCAP::clipboard::read'
|
||||
}
|
||||
|
||||
export enum AppChannel {
|
||||
Exit = 'UCAP::app::exit'
|
||||
}
|
||||
|
|
|
@ -88,4 +88,12 @@ export abstract class NativeService {
|
|||
): TranslateLoader;
|
||||
|
||||
abstract openDefaultBrowser(url: string): void;
|
||||
|
||||
abstract readFromClipboard(): Promise<{
|
||||
text?: string;
|
||||
rtf?: string;
|
||||
html?: string;
|
||||
image?: Buffer;
|
||||
imageDataUrl?: string;
|
||||
}>;
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@
|
|||
name="message"
|
||||
[matTextareaAutosize]="true"
|
||||
[matAutosizeMaxRows]="20"
|
||||
(paste)="onPasteReply($event)"
|
||||
></textarea>
|
||||
</mat-form-field>
|
||||
|
||||
|
|
|
@ -34,6 +34,9 @@ export class FormComponent implements OnInit {
|
|||
@Output()
|
||||
clearView = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
clipboardPaste = new EventEmitter();
|
||||
|
||||
@ViewChild('replyForm', { static: false })
|
||||
replyForm: NgForm;
|
||||
|
||||
|
@ -84,4 +87,10 @@ export class FormComponent implements OnInit {
|
|||
onClickTranslation() {
|
||||
this.toggleTranslation.emit();
|
||||
}
|
||||
|
||||
onPasteReply(event: ClipboardEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
this.clipboardPaste.emit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { DomSanitizer } from '@angular/platform-browser';
|
||||
|
||||
@Pipe({ name: 'ucapSafeHtml' })
|
||||
export class SafeHtmlPipe implements PipeTransform {
|
||||
constructor(private domSanitizer: DomSanitizer) {}
|
||||
|
||||
public transform(value: string) {
|
||||
return this.domSanitizer.bypassSecurityTrustHtml(value);
|
||||
}
|
||||
}
|
|
@ -71,6 +71,7 @@ import { SecondsToMinutesPipe } from './pipes/seconds-to-minutes.pipe';
|
|||
import { LinkyPipe } from './pipes/linky.pipe';
|
||||
import { TranslatePipe } from './pipes/translate.pipe';
|
||||
import { DatePipe } from './pipes/date.pipe';
|
||||
import { SafeHtmlPipe } from './pipes/safe-html.pipe';
|
||||
|
||||
import {
|
||||
StringEmptyCheckPipe,
|
||||
|
@ -125,7 +126,8 @@ const PIPES = [
|
|||
TranslatePipe,
|
||||
DatePipe,
|
||||
StringEmptyCheckPipe,
|
||||
StringFormatterPhonePipe
|
||||
StringFormatterPhonePipe,
|
||||
SafeHtmlPipe
|
||||
];
|
||||
const SERVICES = [
|
||||
BottomSheetService,
|
||||
|
|
Loading…
Reference in New Issue
Block a user