Merge branch 'master' of http://10.81.13.221:6990/Web/next-ucap-messenger
This commit is contained in:
commit
bca6a54a24
|
@ -9,7 +9,6 @@ import {
|
|||
JsonAnalization,
|
||||
APIFormDataEncoder
|
||||
} from '@ucap-webmessenger/api';
|
||||
import { JsonObject } from 'type-fest';
|
||||
import { FileUploadItem } from '../models/file-upload-item';
|
||||
|
||||
export interface FileTalkSaveRequest extends APIRequest {
|
||||
|
@ -73,7 +72,7 @@ export const decodeFileTalkSave: APIDecoder<FileTalkSaveResponse> = (
|
|||
res: any
|
||||
) => {
|
||||
try {
|
||||
const json: JsonObject | Error = JsonAnalization.receiveAnalization(res);
|
||||
const json = JsonAnalization.receiveAnalization(res);
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
roomSeq: json.RoomID,
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
JsonAnalization,
|
||||
StatusCode
|
||||
} from '@ucap-webmessenger/api';
|
||||
import { JsonObject } from 'type-fest';
|
||||
|
||||
export interface FileTalkShareRequest extends APIRequest {
|
||||
userSeq: string;
|
||||
|
@ -52,7 +51,7 @@ export const decodeFileTalkShare: APIDecoder<FileTalkShareResponse> = (
|
|||
res: any
|
||||
) => {
|
||||
try {
|
||||
const json: JsonObject | Error = JsonAnalization.receiveAnalization(res);
|
||||
const json = JsonAnalization.receiveAnalization(res);
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
roomSeq: json.RoomID,
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
JsonAnalization,
|
||||
StatusCode
|
||||
} from '@ucap-webmessenger/api';
|
||||
import { JsonObject } from 'type-fest';
|
||||
|
||||
export interface MassTalkDownloadRequest extends APIRequest {
|
||||
userSeq: number;
|
||||
|
@ -40,7 +39,7 @@ export const decodeMassTalkDownload: APIDecoder<MassTalkDownloadResponse> = (
|
|||
res: any
|
||||
) => {
|
||||
try {
|
||||
const json: JsonObject | Error = JsonAnalization.receiveAnalization(res);
|
||||
const json = JsonAnalization.receiveAnalization(res);
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
errorMessage: json.ErrorMessage,
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
StatusCode,
|
||||
JsonAnalization
|
||||
} from '@ucap-webmessenger/api';
|
||||
import { JsonObject } from 'type-fest';
|
||||
|
||||
export interface MassTalkSaveRequest extends APIRequest {
|
||||
userSeq: number;
|
||||
|
@ -44,7 +43,7 @@ export const decodeMassTalkSave: APIDecoder<MassTalkSaveResponse> = (
|
|||
res: any
|
||||
) => {
|
||||
try {
|
||||
const json: JsonObject | Error = JsonAnalization.receiveAnalization(res);
|
||||
const json = JsonAnalization.receiveAnalization(res);
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
errorMessage: json.ErrorMessage,
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
JsonAnalization,
|
||||
StatusCode
|
||||
} from '@ucap-webmessenger/api';
|
||||
import { JsonObject } from 'type-fest';
|
||||
|
||||
export interface TransMassTalkSaveRequest extends APIRequest {
|
||||
userSeq: string;
|
||||
|
@ -50,7 +49,7 @@ export const decodeTransMassTalkSave: APIDecoder<TransMassTalkSaveResponse> = (
|
|||
res: any
|
||||
) => {
|
||||
try {
|
||||
const json: JsonObject | Error = JsonAnalization.receiveAnalization(res);
|
||||
const json = JsonAnalization.receiveAnalization(res);
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
translationSeq: json.EventTransSEQ,
|
||||
|
|
|
@ -8,7 +8,6 @@ import {
|
|||
JsonAnalization,
|
||||
StatusCode
|
||||
} from '@ucap-webmessenger/api';
|
||||
import { JsonObject } from 'type-fest';
|
||||
|
||||
export interface TranslationSaveRequest extends APIRequest {
|
||||
userSeq: string;
|
||||
|
@ -51,7 +50,7 @@ export const decodeTranslationSave: APIDecoder<TranslationSaveResponse> = (
|
|||
res: any
|
||||
) => {
|
||||
try {
|
||||
const json: JsonObject | Error = JsonAnalization.receiveAnalization(res);
|
||||
const json = JsonAnalization.receiveAnalization(res);
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
translationSeq: json.EventTransSEQ,
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { JsonObject } from 'type-fest';
|
||||
|
||||
export class JsonAnalization {
|
||||
/**
|
||||
* Raw string Analization for JSON string.
|
||||
* @description Editing with string.util.ts
|
||||
*/
|
||||
public static receiveAnalization(jsonStr: string): JsonObject {
|
||||
public static receiveAnalization(jsonStr: string): any {
|
||||
const startJson = jsonStr.indexOf('{');
|
||||
const endJson = jsonStr.lastIndexOf('}');
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<ucap-file-viewer></ucap-file-viewer>
|
|
@ -3,20 +3,20 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
|||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { ImageViewerDialogComponent } from './image-viewer.dialog.component';
|
||||
import { FileViewerDialogComponent } from './file-viewer.dialog.component';
|
||||
|
||||
describe('ImageViewerDialogComponent', () => {
|
||||
let component: ImageViewerDialogComponent;
|
||||
let fixture: ComponentFixture<ImageViewerDialogComponent>;
|
||||
describe('FileViewerDialogComponent', () => {
|
||||
let component: FileViewerDialogComponent;
|
||||
let fixture: ComponentFixture<FileViewerDialogComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ImageViewerDialogComponent]
|
||||
declarations: [FileViewerDialogComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ImageViewerDialogComponent);
|
||||
fixture = TestBed.createComponent(FileViewerDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Inject,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
export interface FileViewerDialogData {}
|
||||
|
||||
export interface FileViewerDialogResult {}
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout-common-file-viewer',
|
||||
templateUrl: './file-viewer.dialog.component.html',
|
||||
styleUrls: ['./file-viewer.dialog.component.scss']
|
||||
})
|
||||
export class FileViewerDialogComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<
|
||||
FileViewerDialogData,
|
||||
FileViewerDialogResult
|
||||
>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: FileViewerDialogData,
|
||||
private logger: NGXLogger
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
Inject,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
export interface ImageViewerDialogData {}
|
||||
|
||||
export interface ImageViewerDialogResult {}
|
||||
|
||||
@Component({
|
||||
selector: 'app-layout-common-image-viewer',
|
||||
templateUrl: './image-viewer.dialog.component.html',
|
||||
styleUrls: ['./image-viewer.dialog.component.scss']
|
||||
})
|
||||
export class ImageViewerDialogComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<
|
||||
ImageViewerDialogData,
|
||||
ImageViewerDialogResult
|
||||
>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: ImageViewerDialogData,
|
||||
private logger: NGXLogger
|
||||
) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
ngOnDestroy(): void {}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
import { ImageViewerDialogComponent } from './image-viewer.dialog.component';
|
||||
import { FileViewerDialogComponent } from './file-viewer.dialog.component';
|
||||
|
||||
export const DIALOGS = [ImageViewerDialogComponent];
|
||||
export const DIALOGS = [FileViewerDialogComponent];
|
||||
|
|
|
@ -28,7 +28,8 @@ import {
|
|||
isRecalled,
|
||||
isCopyable,
|
||||
isRecallable,
|
||||
InfoResponse
|
||||
InfoResponse,
|
||||
EventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
import * as AppStore from '@app/store';
|
||||
|
@ -63,10 +64,10 @@ import {
|
|||
CreateChatDialogResult
|
||||
} from '../dialogs/chat/create-chat.dialog.component';
|
||||
import {
|
||||
ImageViewerDialogComponent,
|
||||
ImageViewerDialogData,
|
||||
ImageViewerDialogResult
|
||||
} from '@app/layouts/common/dialogs/image-viewer.dialog.component';
|
||||
FileViewerDialogComponent,
|
||||
FileViewerDialogData,
|
||||
FileViewerDialogResult
|
||||
} from '@app/layouts/common/dialogs/file-viewer.dialog.component';
|
||||
import { CONST } from '@ucap-webmessenger/core';
|
||||
import { PerfectScrollbarComponent } from 'ngx-perfect-scrollbar';
|
||||
import { StatusCode } from '@ucap-webmessenger/api';
|
||||
|
@ -103,7 +104,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
|
||||
loginRes: LoginResponse;
|
||||
loginResSubscription: Subscription;
|
||||
eventList$: Observable<Info[]>;
|
||||
eventList$: Observable<Info<EventJson>[]>;
|
||||
baseEventSeq = 0;
|
||||
roomInfo: RoomInfo;
|
||||
roomInfoSubscription: Subscription;
|
||||
|
@ -355,10 +356,10 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
async onImageViewer(value: FileInfo) {
|
||||
this.logger.debug('imageViewer', value);
|
||||
const result = await this.dialogService.open<
|
||||
ImageViewerDialogComponent,
|
||||
ImageViewerDialogData,
|
||||
ImageViewerDialogResult
|
||||
>(ImageViewerDialogComponent, {
|
||||
FileViewerDialogComponent,
|
||||
FileViewerDialogData,
|
||||
FileViewerDialogResult
|
||||
>(FileViewerDialogComponent, {
|
||||
position: {
|
||||
top: '30px'
|
||||
},
|
||||
|
@ -367,6 +368,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
height: 'calc(100% - 30px)',
|
||||
width: '100%',
|
||||
hasBackdrop: false,
|
||||
panelClass: 'app-dialog-full',
|
||||
data: {}
|
||||
});
|
||||
}
|
||||
|
@ -453,7 +455,10 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
);
|
||||
}
|
||||
|
||||
onContextMenuMessage(params: { event: MouseEvent; message: Info }) {
|
||||
onContextMenuMessage(params: {
|
||||
event: MouseEvent;
|
||||
message: Info<EventJson>;
|
||||
}) {
|
||||
params.event.preventDefault();
|
||||
params.event.stopPropagation();
|
||||
|
||||
|
@ -467,7 +472,7 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
this.messageContextMenuTrigger.openMenu();
|
||||
}
|
||||
|
||||
async onClickMessageContextMenu(menuType: string, message: Info) {
|
||||
async onClickMessageContextMenu(menuType: string, message: Info<EventJson>) {
|
||||
switch (menuType) {
|
||||
case 'COPY':
|
||||
{
|
||||
|
@ -475,7 +480,9 @@ export class MessagesComponent implements OnInit, OnDestroy, AfterViewInit {
|
|||
case EventType.Character:
|
||||
{
|
||||
if (
|
||||
this.clipboardService.copyFromContent(message.sentMessage)
|
||||
this.clipboardService.copyFromContent(
|
||||
(message as Info<string>).sentMessage
|
||||
)
|
||||
) {
|
||||
this.snackBarService.open('클립보드에 복사되었습니다.', '', {
|
||||
duration: 3000,
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
} from '@ucap-webmessenger/protocol-status';
|
||||
import { Router } from '@angular/router';
|
||||
import { Company } from '@ucap-webmessenger/api-external';
|
||||
import { EventType, Info } from '@ucap-webmessenger/protocol-event';
|
||||
import { EventType, Info, EventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import { VersionInfo2Response } from '@ucap-webmessenger/api-public';
|
||||
import { StatusCode as ApiStatusCode } from '@ucap-webmessenger/api';
|
||||
import { StatusCode } from '@ucap-webmessenger/core';
|
||||
|
@ -302,7 +302,7 @@ const companyList: Company[] = [
|
|||
}
|
||||
];
|
||||
|
||||
const eventInfo: Info[] = [
|
||||
const eventInfo: Info<EventJson>[] = [
|
||||
{
|
||||
seq: 6,
|
||||
type: EventType.Character,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createAction, props } from '@ngrx/store';
|
||||
import { Info } from '@ucap-webmessenger/protocol-event';
|
||||
import { Info, EventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import {
|
||||
MassTalkDownloadRequest,
|
||||
MassTalkDownloadResponse
|
||||
|
@ -18,7 +18,7 @@ export const newEventMessage = createAction(
|
|||
'[Messenger::Chat] newEventMessage',
|
||||
props<{
|
||||
roomSeq: string;
|
||||
info: Info;
|
||||
info: Info<EventJson>;
|
||||
}>()
|
||||
);
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ import {
|
|||
DelRequest,
|
||||
DelResponse,
|
||||
CancelRequest,
|
||||
CancelResponse
|
||||
CancelResponse,
|
||||
EventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
export const info = createAction(
|
||||
|
@ -24,7 +25,7 @@ export const info = createAction(
|
|||
export const infoSuccess = createAction(
|
||||
'[Messenger::Event] Info Success',
|
||||
props<{
|
||||
infoList: Info[];
|
||||
infoList: Info<EventJson>[];
|
||||
res: InfoResponse;
|
||||
}>()
|
||||
);
|
||||
|
@ -32,7 +33,7 @@ export const infoSuccess = createAction(
|
|||
export const infoMoreSuccess = createAction(
|
||||
'[Messenger::Event] Info More Success',
|
||||
props<{
|
||||
infoList: Info[];
|
||||
infoList: Info<EventJson>[];
|
||||
res: InfoResponse;
|
||||
}>()
|
||||
);
|
||||
|
@ -51,7 +52,7 @@ export const newInfo = createAction(
|
|||
'[Messenger::Event] New Info',
|
||||
props<{
|
||||
roomSeq: string;
|
||||
info: Info;
|
||||
info: Info<EventJson>;
|
||||
SVC_TYPE?: number;
|
||||
SSVC_TYPE?: number;
|
||||
}>()
|
||||
|
@ -60,7 +61,7 @@ export const newInfo = createAction(
|
|||
export const appendInfoList = createAction(
|
||||
'[Messenger::Event] Append InfoList',
|
||||
props<{
|
||||
info: Info;
|
||||
info: Info<EventJson>;
|
||||
}>()
|
||||
);
|
||||
|
||||
|
@ -134,7 +135,7 @@ export const read = createAction(
|
|||
export const readSuccess = createAction(
|
||||
'[Messenger::Event] read Success',
|
||||
props<{
|
||||
infoList: Info[];
|
||||
infoList: Info<EventJson>[];
|
||||
res: InfoResponse;
|
||||
}>()
|
||||
);
|
||||
|
|
|
@ -36,7 +36,8 @@ import {
|
|||
EventType,
|
||||
ReadNotification,
|
||||
SSVC_TYPE_EVENT_SEND_RES,
|
||||
SSVC_TYPE_EVENT_SEND_NOTI
|
||||
SSVC_TYPE_EVENT_SEND_NOTI,
|
||||
EventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
import * as ChatStore from '@app/store/messenger/chat';
|
||||
|
@ -102,7 +103,7 @@ export class Effects {
|
|||
|
||||
info$ = createEffect(
|
||||
() => {
|
||||
let infoList: Info[];
|
||||
let infoList: Info<EventJson>[];
|
||||
|
||||
return this.actions$.pipe(
|
||||
ofType(info),
|
||||
|
@ -170,7 +171,9 @@ export class Effects {
|
|||
this.store.pipe(
|
||||
select(
|
||||
(state: any) =>
|
||||
state.messenger.event.infoList.entities as Dictionary<Info>
|
||||
state.messenger.event.infoList.entities as Dictionary<
|
||||
Info<EventJson>
|
||||
>
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -179,7 +182,7 @@ export class Effects {
|
|||
const delEventSeq: number[] = [];
|
||||
// tslint:disable-next-line: forin
|
||||
for (const key in eventList) {
|
||||
const event: Info = eventList[key];
|
||||
const event: Info<EventJson> = eventList[key];
|
||||
if (
|
||||
new Date().getTime() - new Date(event.sendDate).getTime() >=
|
||||
roomInfo.timeRoomInterval * 1000
|
||||
|
@ -276,19 +279,11 @@ export class Effects {
|
|||
ofType(sendSuccess),
|
||||
tap(action => {
|
||||
const res = action.res;
|
||||
const appendInfo: Info = {
|
||||
seq: res.seq,
|
||||
type: res.eventType,
|
||||
senderSeq: action.senderSeq,
|
||||
sendDate: res.sendDate,
|
||||
sentMessage: res.message,
|
||||
receiverCount: res.receiverCount
|
||||
};
|
||||
|
||||
this.store.dispatch(
|
||||
newInfo({
|
||||
roomSeq: res.roomSeq,
|
||||
info: appendInfo,
|
||||
info: res.info,
|
||||
SVC_TYPE: res.SVC_TYPE,
|
||||
SSVC_TYPE: res.SSVC_TYPE
|
||||
})
|
||||
|
@ -305,19 +300,10 @@ export class Effects {
|
|||
ofType(sendNotification),
|
||||
map(action => action.noti),
|
||||
tap(noti => {
|
||||
const appendInfo: Info = {
|
||||
seq: noti.seq,
|
||||
type: noti.eventType,
|
||||
senderSeq: noti.SENDER_SEQ,
|
||||
sendDate: noti.sendDate,
|
||||
sentMessage: noti.message,
|
||||
receiverCount: noti.receiverCount
|
||||
};
|
||||
|
||||
this.store.dispatch(
|
||||
newInfo({
|
||||
roomSeq: noti.roomSeq,
|
||||
info: appendInfo,
|
||||
info: noti.info,
|
||||
SVC_TYPE: noti.SVC_TYPE,
|
||||
SSVC_TYPE: noti.SSVC_TYPE
|
||||
})
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
infoMoreSuccess
|
||||
} from './actions';
|
||||
import * as AuthenticationStore from '@app/store/account/authentication';
|
||||
import { Info, EventType } from '@ucap-webmessenger/protocol-event';
|
||||
import { Info, EventType, EventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import { CONST } from '@ucap-webmessenger/core';
|
||||
|
||||
export const reducer = createReducer(
|
||||
|
@ -63,7 +63,7 @@ export const reducer = createReducer(
|
|||
on(appendInfoList, (state, action) => {
|
||||
const eventinfo = action.info;
|
||||
|
||||
const statusEventInfo: Info = {
|
||||
const statusEventInfo: Info<EventJson> = {
|
||||
...state.infoList.entities[eventinfo.seq],
|
||||
type: eventinfo.type,
|
||||
senderSeq: eventinfo.senderSeq,
|
||||
|
@ -81,7 +81,7 @@ export const reducer = createReducer(
|
|||
on(recallInfoList, (state, action) => {
|
||||
const eventSeq = action.eventSeq;
|
||||
|
||||
const statusEventInfo: Info = {
|
||||
const statusEventInfo: Info<EventJson> = {
|
||||
...state.infoList.entities[eventSeq],
|
||||
type: EventType.RecalledMessage,
|
||||
sentMessage: '회수된 메시지'
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { Selector, createSelector } from '@ngrx/store';
|
||||
import { InfoResponse, Info } from '@ucap-webmessenger/protocol-event';
|
||||
import {
|
||||
InfoResponse,
|
||||
Info,
|
||||
EventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
import { EntityState, createEntityAdapter } from '@ngrx/entity';
|
||||
|
||||
export interface InfoListState extends EntityState<Info> {}
|
||||
export interface InfoListState extends EntityState<Info<EventJson>> {}
|
||||
|
||||
export interface State {
|
||||
infoListProcessing: boolean;
|
||||
|
@ -11,7 +15,7 @@ export interface State {
|
|||
remainInfo: boolean;
|
||||
}
|
||||
|
||||
export const adapterInfoList = createEntityAdapter<Info>({
|
||||
export const adapterInfoList = createEntityAdapter<Info<EventJson>>({
|
||||
selectId: info => info.seq,
|
||||
sortComparer: (a, b) => {
|
||||
return a.seq - b.seq;
|
||||
|
|
|
@ -13,7 +13,7 @@ import {
|
|||
UserInfo as RoomUserInfo,
|
||||
InfoRequest
|
||||
} from '@ucap-webmessenger/protocol-room';
|
||||
import { Info } from '@ucap-webmessenger/protocol-event';
|
||||
import { Info, EventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import {
|
||||
AddResponse as GroupAddResponse,
|
||||
UpdateRequest as GroupUpdateRequest,
|
||||
|
@ -88,7 +88,7 @@ export const updateRoomForNewEventMessage = createAction(
|
|||
'[Messenger::Sync] updateRoomForNewEventMessage',
|
||||
props<{
|
||||
roomSeq: string;
|
||||
info: Info;
|
||||
info: Info<EventJson>;
|
||||
}>()
|
||||
);
|
||||
|
||||
|
|
|
@ -111,8 +111,8 @@ $lg-red: (
|
|||
.text-accent-color {
|
||||
color: mat-color($accent);
|
||||
}
|
||||
.text-warn-color{
|
||||
color:mat-color($warn);
|
||||
.text-warn-color {
|
||||
color: mat-color($warn);
|
||||
}
|
||||
.border-primary-color {
|
||||
border: 1px solid mat-color($primary);
|
||||
|
@ -144,4 +144,12 @@ $lg-red: (
|
|||
background-color: mat-color($primary);
|
||||
}
|
||||
}
|
||||
|
||||
.app-dialog-full .mat-dialog-container {
|
||||
overflow: hidden;
|
||||
padding: 0px;
|
||||
background-color: rgba($color: #000000, $alpha: 0.3);
|
||||
box-shadow: none;
|
||||
border-radius: 0px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
// Media step breakpoint mixin based on Angular Material lib
|
||||
$breakpoints: (
|
||||
xs: 'screen and (max-width: 599px)',
|
||||
sm: 'screen and (min-width: 600px) and (max-width: 959px)',
|
||||
md: 'screen and (min-width: 960px) and (max-width: 1279px)',
|
||||
lg: 'screen and (min-width: 1280px) and (max-width: 1919px)',
|
||||
xl: 'screen and (min-width: 1920px) and (max-width: 5000px)',
|
||||
lt-sm: 'screen and (max-width: 599px)',
|
||||
lt-md: 'screen and (max-width: 959px)',
|
||||
lt-lg: 'screen and (max-width: 1279px)',
|
||||
lt-xl: 'screen and (max-width: 1919px)',
|
||||
gt-xs: 'screen and (min-width: 600px)',
|
||||
gt-sm: 'screen and (min-width: 960px)',
|
||||
gt-md: 'screen and (min-width: 1280px)',
|
||||
gt-lg: 'screen and (min-width: 1920px)'
|
||||
) !default;
|
||||
|
||||
// Re-map the breakpoints for the helper classes
|
||||
$helper-breakpoints: (
|
||||
xs: null,
|
||||
sm: 'gt-xs',
|
||||
md: 'gt-sm',
|
||||
lg: 'gt-md',
|
||||
xl: 'gt-lg'
|
||||
);
|
||||
|
||||
@mixin media-breakpoint($breakpointName) {
|
||||
$mediaQuery: map-get($breakpoints, $breakpointName);
|
||||
|
||||
@if ($mediaQuery == null) {
|
||||
@content;
|
||||
} @else {
|
||||
@media #{$mediaQuery} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
button,
|
||||
input[type='email'],
|
||||
input[type='tel'],
|
||||
input[type='text'],
|
||||
input[type='password'],
|
||||
input[type='image'],
|
||||
input[type='submit'],
|
||||
input[type='button'],
|
||||
input[type='search'],
|
||||
textarea {
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
outline: none;
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Body scroll lock
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
html,
|
||||
body {
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Boxed body
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
body {
|
||||
// Boxed
|
||||
&.boxed {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
|
||||
@include mat-elevation(8);
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------*/
|
||||
/* @ Text rendering & box sizing
|
||||
/*----------------------------------------------------------------*/
|
||||
* {
|
||||
text-rendering: optimizeLegibility;
|
||||
-o-text-rendering: optimizeLegibility;
|
||||
-ms-text-rendering: optimizeLegibility;
|
||||
-moz-text-rendering: optimizeLegibility;
|
||||
-webkit-text-rendering: optimizeLegibility;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// Remove focus outline
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Responsive images
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
vertical-align: top;
|
||||
border: none;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Input
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
input {
|
||||
border: none;
|
||||
padding: 0 16px;
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Position helpers
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
@each $breakpoint, $materialBreakpoint in $helper-breakpoints {
|
||||
@include media-breakpoint($materialBreakpoint) {
|
||||
$infix: if($materialBreakpoint == null, '', '-#{$breakpoint}');
|
||||
|
||||
.position#{$infix}-relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.position#{$infix}-absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.position#{$infix}-static {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Absolute position alignment helpers
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
@each $breakpoint, $materialBreakpoint in $helper-breakpoints {
|
||||
@include media-breakpoint($materialBreakpoint) {
|
||||
$infix: if($materialBreakpoint == null, '', '-#{$breakpoint}');
|
||||
|
||||
.align#{$infix}-top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.align#{$infix}-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.align#{$infix}-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.align#{$infix}-left {
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Size helpers
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
@each $prop, $abbrev in (height: h, width: w) {
|
||||
@for $index from 0 through 180 {
|
||||
$size: $index * 4;
|
||||
$length: #{$size}px;
|
||||
|
||||
.#{$abbrev}-#{$size} {
|
||||
#{$prop}: $length !important;
|
||||
min-#{$prop}: $length !important;
|
||||
max-#{$prop}: $length !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Percentage
|
||||
@for $i from 0 through 20 {
|
||||
$i-p: 5 * $i;
|
||||
$size-p: 5% * $i;
|
||||
|
||||
.#{$abbrev}-#{$i-p}-p {
|
||||
#{$prop}: $size-p !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Spacing helpers
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
@each $breakpoint, $materialBreakpoint in $helper-breakpoints {
|
||||
@include media-breakpoint($materialBreakpoint) {
|
||||
$infix: if($materialBreakpoint == null, '', '-#{$breakpoint}');
|
||||
|
||||
@each $prop, $abbrev in (margin: m, padding: p) {
|
||||
@for $index from 0 through 64 {
|
||||
$size: $index * 4;
|
||||
$length: #{$size}px;
|
||||
|
||||
.#{$abbrev}#{$infix}-#{$size} {
|
||||
#{$prop}: $length !important;
|
||||
}
|
||||
}
|
||||
|
||||
@for $index from 0 through 64 {
|
||||
$size: $index * 4;
|
||||
$length: #{$size}px;
|
||||
|
||||
.#{$abbrev}x#{$infix}-#{$size} {
|
||||
#{$prop}-right: $length !important;
|
||||
#{$prop}-left: $length !important;
|
||||
}
|
||||
|
||||
.#{$abbrev}y#{$infix}-#{$size} {
|
||||
#{$prop}-top: $length !important;
|
||||
#{$prop}-bottom: $length !important;
|
||||
}
|
||||
}
|
||||
|
||||
@for $index from 0 through 64 {
|
||||
$size: $index * 4;
|
||||
$length: #{$size}px;
|
||||
|
||||
.#{$abbrev}t#{$infix}-#{$size} {
|
||||
#{$prop}-top: $length !important;
|
||||
}
|
||||
|
||||
.#{$abbrev}r#{$infix}-#{$size} {
|
||||
#{$prop}-right: $length !important;
|
||||
}
|
||||
|
||||
.#{$abbrev}b#{$infix}-#{$size} {
|
||||
#{$prop}-bottom: $length !important;
|
||||
}
|
||||
|
||||
.#{$abbrev}l#{$infix}-#{$size} {
|
||||
#{$prop}-left: $length !important;
|
||||
}
|
||||
}
|
||||
|
||||
@if ($abbrev == m) {
|
||||
// Some special margin utils for flex alignments
|
||||
.m#{$infix}-auto {
|
||||
margin: auto !important;
|
||||
}
|
||||
|
||||
.mt#{$infix}-auto {
|
||||
margin-top: auto !important;
|
||||
}
|
||||
|
||||
.mr#{$infix}-auto {
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
.mb#{$infix}-auto {
|
||||
margin-bottom: auto !important;
|
||||
}
|
||||
|
||||
.ml#{$infix}-auto {
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
.mx#{$infix}-auto {
|
||||
margin-right: auto !important;
|
||||
margin-left: auto !important;
|
||||
}
|
||||
|
||||
.my#{$infix}-auto {
|
||||
margin-top: auto !important;
|
||||
margin-bottom: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Border helpers
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
$border-style: 1px solid rgba(0, 0, 0, 0.12);
|
||||
|
||||
.border,
|
||||
.b {
|
||||
border: $border-style;
|
||||
}
|
||||
|
||||
.border-top,
|
||||
.bt {
|
||||
border-top: $border-style;
|
||||
}
|
||||
|
||||
.border-right,
|
||||
.br {
|
||||
border-right: $border-style;
|
||||
}
|
||||
|
||||
.border-bottom,
|
||||
.bb {
|
||||
border-bottom: $border-style;
|
||||
}
|
||||
|
||||
.border-left,
|
||||
.bl {
|
||||
border-left: $border-style;
|
||||
}
|
||||
|
||||
.border-horizontal,
|
||||
.bx {
|
||||
border-left: $border-style;
|
||||
border-right: $border-style;
|
||||
}
|
||||
|
||||
.border-vertical,
|
||||
.by {
|
||||
border-top: $border-style;
|
||||
border-bottom: $border-style;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Border radius helpers
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
.border-radius-100 {
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.border-radius-2 {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.border-radius-4 {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.border-radius-8 {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.border-radius-16 {
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
// @ Cursor helpers
|
||||
// -----------------------------------------------------------------------------------------------------
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cursor-default {
|
||||
cursor: default;
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
i,
|
||||
mat-icon {
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
line-height: 24px;
|
||||
|
||||
@each $breakpoint, $materialBreakpoint in $helper-breakpoints {
|
||||
@include media-breakpoint($materialBreakpoint) {
|
||||
$infix: if($materialBreakpoint == null, '', '-#{$breakpoint}');
|
||||
|
||||
@for $size from 2 through 128 {
|
||||
&.s#{$infix}-#{$size * 2} {
|
||||
font-size: #{($size * 2) + 'px'} !important;
|
||||
width: #{($size * 2) + 'px'} !important;
|
||||
height: #{($size * 2) + 'px'} !important;
|
||||
min-width: #{($size * 2) + 'px'} !important;
|
||||
min-height: #{($size * 2) + 'px'} !important;
|
||||
line-height: #{($size * 2) + 'px'} !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,450 +0,0 @@
|
|||
/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in
|
||||
* IE on Windows Phone and in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-ms-text-size-adjust: 100%; /* 2 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers (opinionated).
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
article,
|
||||
aside,
|
||||
footer,
|
||||
header,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in IE.
|
||||
*/
|
||||
|
||||
figcaption,
|
||||
figure,
|
||||
main {
|
||||
/* 1 */
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct margin in IE 8.
|
||||
*/
|
||||
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Remove the gray background on active links in IE 10.
|
||||
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent; /* 1 */
|
||||
-webkit-text-decoration-skip: objects; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57- and Firefox 39-.
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font style in Android 4.3-.
|
||||
*/
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct background and color in IE 9-.
|
||||
*/
|
||||
|
||||
mark {
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
audio,
|
||||
video {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in iOS 4-7.
|
||||
*/
|
||||
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10-.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the overflow in IE.
|
||||
*/
|
||||
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers (opinionated).
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: sans-serif; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input {
|
||||
/* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
/* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||
* controls in Android 4.
|
||||
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
html [type="button"], /* 1 */
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type='button']::-moz-focus-inner,
|
||||
[type='reset']::-moz-focus-inner,
|
||||
[type='submit']::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type='button']:-moz-focusring,
|
||||
[type='reset']:-moz-focusring,
|
||||
[type='submit']:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct display in IE 9-.
|
||||
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
display: inline-block; /* 1 */
|
||||
vertical-align: baseline; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10-.
|
||||
* 2. Remove the padding in IE 10-.
|
||||
*/
|
||||
|
||||
[type='checkbox'],
|
||||
[type='radio'] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type='number']::-webkit-inner-spin-button,
|
||||
[type='number']::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type='search']::-webkit-search-cancel-button,
|
||||
[type='search']::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in IE 9-.
|
||||
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||
*/
|
||||
|
||||
details, /* 1 */
|
||||
menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Scripting
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 9-.
|
||||
*/
|
||||
|
||||
canvas {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Hidden
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10-.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
body:not(.is-mobile) {
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar:hover {
|
||||
background-color: rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border: 2px solid transparent;
|
||||
box-shadow: inset 0 0 0 12px rgba(0, 0, 0, 0.37);
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
box-shadow: inset 0 0 0 12px rgba(0, 0, 0, 0.54);
|
||||
border-radius: 12px;
|
||||
}
|
||||
}
|
|
@ -5,13 +5,7 @@
|
|||
@include mat-core();
|
||||
|
||||
// Partials
|
||||
// @import 'partials/breakpoints';
|
||||
// @import 'partials/forms';
|
||||
// @import 'partials/general';
|
||||
// @import 'partials/helpers';
|
||||
// @import 'partials/icons';
|
||||
// @import 'partials/normalize';
|
||||
// @import 'partials/scrollbars';
|
||||
@import 'partials/material-ui';
|
||||
|
||||
//creative
|
||||
@import 'global/default';
|
||||
|
|
|
@ -1,56 +1,63 @@
|
|||
import { EventType } from '@ucap-webmessenger/protocol-event';
|
||||
import {
|
||||
EventType,
|
||||
EventJson,
|
||||
FileEventJson,
|
||||
MassTextEventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
import { FileType } from '@ucap-webmessenger/protocol-file';
|
||||
import { JsonObject } from 'type-fest';
|
||||
import { JsonAnalization } from '@ucap-webmessenger/api';
|
||||
|
||||
export class StringUtil {
|
||||
public static convertFinalEventMessage(
|
||||
eventType: EventType,
|
||||
finalEventMessage: string
|
||||
finalEventMessage: EventJson
|
||||
): string | null {
|
||||
let eventMessage: string = null;
|
||||
|
||||
switch (eventType) {
|
||||
case EventType.Join:
|
||||
case EventType.Exit:
|
||||
case EventType.RenameRoom:
|
||||
case EventType.NotificationForTimerRoom:
|
||||
case EventType.GuideForRoomTimerChanged: {
|
||||
/**
|
||||
* 해당 타입은 메시지를 갱신하지 않는다.
|
||||
* @description Edit with ui-chat > messages.component.ts
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
case EventType.GuideForRoomTimerChanged:
|
||||
{
|
||||
/**
|
||||
* 해당 타입은 메시지를 갱신하지 않는다.
|
||||
* @description Edit with ui-chat > messages.component.ts
|
||||
*/
|
||||
}
|
||||
break;
|
||||
case EventType.Sticker:
|
||||
finalEventMessage = '스티커';
|
||||
eventMessage = '스티커';
|
||||
break;
|
||||
case EventType.File:
|
||||
{
|
||||
const contentJson = JSON.parse(finalEventMessage);
|
||||
if (contentJson.FileType === FileType.Image) {
|
||||
finalEventMessage = '이미지';
|
||||
const m = finalEventMessage as FileEventJson;
|
||||
|
||||
if (FileType.Image === m.fileType) {
|
||||
eventMessage = '이미지';
|
||||
} else {
|
||||
finalEventMessage = '첨부파일';
|
||||
eventMessage = '첨부파일';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.VideoConference:
|
||||
finalEventMessage = '화상회의';
|
||||
eventMessage = '화상회의';
|
||||
break;
|
||||
case EventType.MassText:
|
||||
{
|
||||
try {
|
||||
const json: JsonObject | Error = JsonAnalization.receiveAnalization(
|
||||
finalEventMessage
|
||||
);
|
||||
finalEventMessage = json.Content.toString();
|
||||
} catch (e) {
|
||||
finalEventMessage = '대용량 텍스트';
|
||||
}
|
||||
const m = finalEventMessage as MassTextEventJson;
|
||||
eventMessage = m.content;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return finalEventMessage;
|
||||
{
|
||||
const m = finalEventMessage as string;
|
||||
eventMessage = m;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return eventMessage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { EventType } from '../types/event.type';
|
||||
|
||||
export interface Info {
|
||||
export interface Info<T = {}> {
|
||||
// 이벤트SEQ
|
||||
seq: number;
|
||||
// 이벤트타입
|
||||
|
@ -11,6 +11,8 @@ export interface Info {
|
|||
sendDate: string;
|
||||
// 발신내용
|
||||
sentMessage: string;
|
||||
// // 발신내용
|
||||
sentMessageJson?: T;
|
||||
// 수신자수
|
||||
receiverCount: number;
|
||||
}
|
||||
|
@ -25,7 +27,7 @@ export function isRecalled(eventType: EventType): boolean {
|
|||
return EventType.RecalledMessage === eventType;
|
||||
}
|
||||
|
||||
export function isRecallable(event: Info, userSeq: number): boolean {
|
||||
export function isRecallable(event: Info<any>, userSeq: number): boolean {
|
||||
return (
|
||||
event.senderSeq === userSeq && event.type !== EventType.RecalledMessage
|
||||
);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export type CharacterEventJson = string;
|
||||
|
||||
export const decodeCharacterEventJson: EventJsonDecoder<CharacterEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
return message as CharacterEventJson;
|
||||
};
|
|
@ -0,0 +1,124 @@
|
|||
import { EventType } from '../../types/event.type';
|
||||
|
||||
import { FileEventJson, decodeFileEventJson } from './file.event-json';
|
||||
import {
|
||||
MassTextEventJson,
|
||||
decodeMassTextEventJson
|
||||
} from './mass-text.event-json';
|
||||
import { StickerEventJson, decodeStickerEventJson } from './sticker.event-json';
|
||||
import { PlanEventJson, decodePlanEventJson } from './plan.event-json';
|
||||
import {
|
||||
VideoConferenceEventJson,
|
||||
decodeVideoConferenceEventJson
|
||||
} from './video-conference.event-json';
|
||||
import {
|
||||
RenameRoomEventJson,
|
||||
decodeRenameRoomEventJson
|
||||
} from './rename-room.event-json';
|
||||
import {
|
||||
TranslationEventJson,
|
||||
decodeTranslationEventJson
|
||||
} from './translation.event-json';
|
||||
import {
|
||||
MassTranslationEventJson,
|
||||
decodeMassTranslationEventJson
|
||||
} from './mass-translation.event-json';
|
||||
import {
|
||||
GuideForRoomTimerChangedEventJson,
|
||||
decodeGuideForRoomTimerChangedEventJson
|
||||
} from './guide-for-room-timer-changed.event-json';
|
||||
import { JoinEventJson, decodeJoinEventJson } from './join.event-json';
|
||||
import {
|
||||
CharacterEventJson,
|
||||
decodeCharacterEventJson
|
||||
} from './character.event-json';
|
||||
import { ExitEventJson, decodeExitEventJson } from './exit.event-json';
|
||||
import { LinkEventJson, decodeLinkEventJson } from './link.event-json';
|
||||
import {
|
||||
RecalledMessageEventJson,
|
||||
decodeRecalledMessageEventJson
|
||||
} from './recalled-message.event-json';
|
||||
import {
|
||||
VideoStreammingEventJson,
|
||||
decodeVideoStreammingEventJson
|
||||
} from './video-streamming.event-json';
|
||||
import {
|
||||
NotificationForTimerRoomEventJson,
|
||||
decodeNotificationForTimerRoomEventJson
|
||||
} from './notification-for-timer-room.event-json';
|
||||
|
||||
export type EventJson =
|
||||
| string
|
||||
| string[]
|
||||
| JoinEventJson
|
||||
| CharacterEventJson
|
||||
| ExitEventJson
|
||||
| FileEventJson
|
||||
| LinkEventJson
|
||||
| MassTextEventJson
|
||||
| RecalledMessageEventJson
|
||||
| StickerEventJson
|
||||
| PlanEventJson
|
||||
| VideoConferenceEventJson
|
||||
| RenameRoomEventJson
|
||||
| NotificationForTimerRoomEventJson
|
||||
| TranslationEventJson
|
||||
| MassTranslationEventJson
|
||||
| VideoStreammingEventJson
|
||||
| GuideForRoomTimerChangedEventJson;
|
||||
|
||||
export const decodeEventJson = (
|
||||
eventType: EventType,
|
||||
message: string
|
||||
): EventJson => {
|
||||
switch (eventType) {
|
||||
case EventType.Join:
|
||||
return decodeJoinEventJson(message);
|
||||
case EventType.Character:
|
||||
return decodeCharacterEventJson(message);
|
||||
case EventType.File:
|
||||
return decodeFileEventJson(message);
|
||||
case EventType.Sticker:
|
||||
return decodeStickerEventJson(message);
|
||||
case EventType.MassText:
|
||||
return decodeMassTextEventJson(message);
|
||||
case EventType.Exit:
|
||||
return decodeExitEventJson(message);
|
||||
case EventType.Plan:
|
||||
return decodePlanEventJson(message);
|
||||
case EventType.VideoConference:
|
||||
return decodeVideoConferenceEventJson(message);
|
||||
case EventType.Link:
|
||||
return decodeLinkEventJson(message);
|
||||
case EventType.RenameRoom:
|
||||
return decodeRenameRoomEventJson(message);
|
||||
case EventType.Translation:
|
||||
return decodeTranslationEventJson(message);
|
||||
case EventType.MassTranslation:
|
||||
return decodeMassTranslationEventJson(message);
|
||||
case EventType.RecalledMessage:
|
||||
return decodeRecalledMessageEventJson(message);
|
||||
case EventType.GuideForRoomTimerChanged:
|
||||
return decodeGuideForRoomTimerChangedEventJson(message);
|
||||
case EventType.NotificationForiOSCapture:
|
||||
return message;
|
||||
case EventType.NotificationForTimerRoom:
|
||||
return decodeNotificationForTimerRoomEventJson(message);
|
||||
case EventType.Before2MonthsAgo:
|
||||
return message;
|
||||
case EventType.ForcedExit:
|
||||
return message;
|
||||
case EventType.ChatbotStart:
|
||||
return message;
|
||||
case EventType.ChatbotEnd:
|
||||
return message;
|
||||
case EventType.ChatbotSend:
|
||||
return message;
|
||||
case EventType.ChatbotSendMass:
|
||||
return message;
|
||||
case EventType.VideoStreamming:
|
||||
return decodeVideoStreammingEventJson(message);
|
||||
default:
|
||||
return message;
|
||||
}
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export type EventJsonDecoder<T> = (message: string) => T;
|
|
@ -0,0 +1,9 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export type ExitEventJson = string;
|
||||
|
||||
export const decodeExitEventJson: EventJsonDecoder<ExitEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
return message;
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api';
|
||||
import { FileType } from '@ucap-webmessenger/protocol-file';
|
||||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export interface FileEventJson {
|
||||
statusCode?: StatusCode;
|
||||
errorMessage?: string;
|
||||
roomSeq?: number;
|
||||
fileName?: string;
|
||||
fileExt?: string;
|
||||
fileType?: FileType;
|
||||
thumbUrl?: string;
|
||||
attachmentSeq?: number;
|
||||
attachmentSize?: number;
|
||||
attachmentRegDate?: string;
|
||||
imageWidth?: number;
|
||||
imageHeight?: number;
|
||||
companyCode?: string;
|
||||
voiceTime?: string;
|
||||
synappKey?: string;
|
||||
}
|
||||
|
||||
export const decodeFileEventJson: EventJsonDecoder<FileEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
try {
|
||||
const json = JsonAnalization.receiveAnalization(message);
|
||||
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
errorMessage: json.ErrorMessage,
|
||||
roomSeq: json.RoomID,
|
||||
fileName: json.FileName,
|
||||
fileExt: json.FileExt,
|
||||
fileType: json.FileType,
|
||||
thumbUrl: json.ThumbURL,
|
||||
attachmentSeq: json.AttSEQ,
|
||||
attachmentSize: json.AttSize,
|
||||
attachmentRegDate: json.AttRegDate,
|
||||
imageWidth: json.ImageWidth,
|
||||
imageHeight: json.ImageHeight,
|
||||
companyCode: json.CompanyCode,
|
||||
voiceTime: json.VoiceTime,
|
||||
synappKey: json.SynappKey
|
||||
} as FileEventJson;
|
||||
} catch (e) {
|
||||
return {
|
||||
statusCode: StatusCode.Fail,
|
||||
errorMessage: e.toString()
|
||||
} as FileEventJson;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export interface GuideForRoomTimerChangedEventJson {
|
||||
senderSeq?: number;
|
||||
time?: number;
|
||||
}
|
||||
|
||||
export const decodeGuideForRoomTimerChangedEventJson: EventJsonDecoder<
|
||||
GuideForRoomTimerChangedEventJson
|
||||
> = (message: string) => {
|
||||
const v = message.split(',');
|
||||
|
||||
return {
|
||||
senderSeq: Number(v[0]),
|
||||
time: Number(v[1])
|
||||
} as GuideForRoomTimerChangedEventJson;
|
||||
};
|
|
@ -0,0 +1,17 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export interface JoinEventJson {
|
||||
owner?: string;
|
||||
inviter?: string[];
|
||||
}
|
||||
|
||||
export const decodeJoinEventJson: EventJsonDecoder<JoinEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
const v = message.split(',');
|
||||
|
||||
return {
|
||||
owner: v[0],
|
||||
inviter: v.slice(1)
|
||||
} as JoinEventJson;
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export type LinkEventJson = string;
|
||||
|
||||
export const decodeLinkEventJson: EventJsonDecoder<LinkEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
return message as LinkEventJson;
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api';
|
||||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export interface MassTextEventJson {
|
||||
statusCode?: StatusCode;
|
||||
errorMessage?: string;
|
||||
roomSeq?: number;
|
||||
massSeq?: number;
|
||||
regDate?: string;
|
||||
content?: string;
|
||||
}
|
||||
|
||||
export const decodeMassTextEventJson: EventJsonDecoder<MassTextEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
try {
|
||||
const json = JsonAnalization.receiveAnalization(message);
|
||||
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
errorMessage: json.ErrorMessage,
|
||||
roomSeq: json.RoomID,
|
||||
massSeq: json.EventMassSeq,
|
||||
regDate: json.RegDate,
|
||||
content: json.Content
|
||||
} as MassTextEventJson;
|
||||
} catch (e) {
|
||||
return {
|
||||
statusCode: StatusCode.Fail,
|
||||
errorMessage: e.toString()
|
||||
} as MassTextEventJson;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,37 @@
|
|||
import { StatusCode, JsonAnalization } from '@ucap-webmessenger/api';
|
||||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export interface MassTranslationEventJson {
|
||||
statusCode?: StatusCode;
|
||||
errorMessage?: string;
|
||||
translationSeq?: number;
|
||||
destLocale?: string;
|
||||
roomSeq?: number;
|
||||
regDate?: string;
|
||||
original?: string;
|
||||
translation?: string;
|
||||
}
|
||||
|
||||
export const decodeMassTranslationEventJson: EventJsonDecoder<
|
||||
MassTranslationEventJson
|
||||
> = (message: string) => {
|
||||
try {
|
||||
const json = JsonAnalization.receiveAnalization(message);
|
||||
|
||||
return {
|
||||
statusCode: json.StatusCode,
|
||||
errorMessage: json.ErrorMessage,
|
||||
translationSeq: json.EventTransSeq,
|
||||
destLocale: json.DestLocale,
|
||||
roomSeq: json.RoomID,
|
||||
regDate: json.RegDate,
|
||||
original: json.Original,
|
||||
translation: json.Translation
|
||||
} as MassTranslationEventJson;
|
||||
} catch (e) {
|
||||
return {
|
||||
statusCode: StatusCode.Fail,
|
||||
errorMessage: e.toString()
|
||||
} as MassTranslationEventJson;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export type NotificationForTimerRoomEventJson = string;
|
||||
|
||||
export const decodeNotificationForTimerRoomEventJson: EventJsonDecoder<
|
||||
NotificationForTimerRoomEventJson
|
||||
> = (message: string) => {
|
||||
return message as NotificationForTimerRoomEventJson;
|
||||
};
|
|
@ -0,0 +1,30 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
import { JsonAnalization } from '@ucap-webmessenger/api';
|
||||
|
||||
export interface PlanEventJson {
|
||||
planSeq?: number;
|
||||
title?: string;
|
||||
contents?: string;
|
||||
date?: string;
|
||||
endDate?: string;
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
export const decodePlanEventJson: EventJsonDecoder<PlanEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
try {
|
||||
const json = JsonAnalization.receiveAnalization(message);
|
||||
return {
|
||||
planSeq: json.planSeq,
|
||||
title: json.title,
|
||||
contents: json.contents,
|
||||
date: json.date,
|
||||
endDate: json.endDate,
|
||||
chat: json.chat,
|
||||
active: !!json.activeYn && 'Y' === json.activeYn ? true : false
|
||||
} as PlanEventJson;
|
||||
} catch (e) {
|
||||
return {} as PlanEventJson;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export type RecalledMessageEventJson = string;
|
||||
|
||||
export const decodeRecalledMessageEventJson: EventJsonDecoder<
|
||||
RecalledMessageEventJson
|
||||
> = (message: string) => {
|
||||
return message as RecalledMessageEventJson;
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export interface RenameRoomEventJson {
|
||||
requester?: string;
|
||||
roomName?: string;
|
||||
}
|
||||
|
||||
export const decodeRenameRoomEventJson: EventJsonDecoder<
|
||||
RenameRoomEventJson
|
||||
> = (message: string) => {
|
||||
return {
|
||||
requester: message.substring(0, message.indexOf(',')),
|
||||
roomName: message.substring(message.indexOf(',') + 1)
|
||||
} as RenameRoomEventJson;
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export interface StickerEventJson {
|
||||
name?: string;
|
||||
file?: string;
|
||||
chat?: string;
|
||||
}
|
||||
|
||||
export const decodeStickerEventJson: EventJsonDecoder<StickerEventJson> = (
|
||||
message: string
|
||||
) => {
|
||||
const json = JSON.parse(message);
|
||||
|
||||
return {
|
||||
name: json.name,
|
||||
file: json.file,
|
||||
chat: json.chat
|
||||
} as StickerEventJson;
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
import { JsonAnalization } from '@ucap-webmessenger/api';
|
||||
|
||||
export interface TranslationEventJson {
|
||||
locale?: string;
|
||||
original?: string;
|
||||
translation?: string;
|
||||
stickerName?: string;
|
||||
stickerFile?: string;
|
||||
}
|
||||
|
||||
export const decodeTranslationEventJson: EventJsonDecoder<
|
||||
TranslationEventJson
|
||||
> = (message: string) => {
|
||||
try {
|
||||
const json = JsonAnalization.receiveAnalization(message);
|
||||
return {
|
||||
locale: json.locale,
|
||||
original: json.original,
|
||||
translation: json.translation,
|
||||
stickerName: json.stickername,
|
||||
stickerFile: json.stickerfile
|
||||
} as TranslationEventJson;
|
||||
} catch (e) {
|
||||
return {} as TranslationEventJson;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,32 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
import { JsonAnalization } from '@ucap-webmessenger/api';
|
||||
|
||||
export interface VideoConferenceEventJson {
|
||||
conferenceSeq?: number;
|
||||
title?: string;
|
||||
contents?: string;
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
register?: string;
|
||||
attendee?: string;
|
||||
}
|
||||
|
||||
export const decodeVideoConferenceEventJson: EventJsonDecoder<
|
||||
VideoConferenceEventJson
|
||||
> = (message: string) => {
|
||||
try {
|
||||
const json = JsonAnalization.receiveAnalization(message);
|
||||
|
||||
return {
|
||||
conferenceSeq: json.confSeq,
|
||||
title: json.title,
|
||||
contents: json.contents,
|
||||
startDate: json.startDate,
|
||||
endDate: json.endDate,
|
||||
register: json.register,
|
||||
attendee: json.attendee
|
||||
} as VideoConferenceEventJson;
|
||||
} catch (e) {
|
||||
return {} as VideoConferenceEventJson;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import { EventJsonDecoder } from './event-json';
|
||||
|
||||
export type VideoStreammingEventJson = string;
|
||||
|
||||
export const decodeVideoStreammingEventJson: EventJsonDecoder<
|
||||
VideoStreammingEventJson
|
||||
> = (message: string) => {
|
||||
return message as VideoStreammingEventJson;
|
||||
};
|
|
@ -12,6 +12,7 @@ import {
|
|||
} from '@ucap-webmessenger/protocol';
|
||||
import { EventType } from '../types/event.type';
|
||||
import { Info } from '../models/info';
|
||||
import { decodeEventJson, EventJson } from './event-json/codec';
|
||||
|
||||
export interface InfoRequest extends ProtocolRequest {
|
||||
// 대화방SEQ(s)
|
||||
|
@ -25,7 +26,7 @@ export interface InfoRequest extends ProtocolRequest {
|
|||
export interface InfoData extends ProtocolStream {
|
||||
// 대화방SEQ(s)
|
||||
roomSeq: string;
|
||||
infoList: Info[];
|
||||
infoList: Info<EventJson>[];
|
||||
}
|
||||
|
||||
export interface InfoResponse extends ProtocolResponse {
|
||||
|
@ -52,17 +53,20 @@ export const encodeInfo: ProtocolEncoder<InfoRequest> = (req: InfoRequest) => {
|
|||
export const decodeInfoData: ProtocolDecoder<InfoData> = (
|
||||
message: ProtocolMessage
|
||||
) => {
|
||||
const infoList: Info[] = [];
|
||||
const infoList: Info<EventJson>[] = [];
|
||||
|
||||
for (const body of message.bodyList) {
|
||||
const info = body.split(BodyStringDivider);
|
||||
if (info.length > 5) {
|
||||
const eventType = info[1] as EventType;
|
||||
|
||||
infoList.push({
|
||||
seq: Number(info[0]),
|
||||
type: info[1] as EventType,
|
||||
type: eventType,
|
||||
senderSeq: Number(info[2]),
|
||||
sendDate: info[3],
|
||||
sentMessage: info[4],
|
||||
sentMessageJson: decodeEventJson(eventType, info[4]),
|
||||
receiverCount: Number(info[5])
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import {
|
|||
} from '@ucap-webmessenger/protocol';
|
||||
import { EventType } from '../types/event.type';
|
||||
import { PushStatus } from '../types/push-status.type';
|
||||
import { EventJson, decodeEventJson } from './event-json/codec';
|
||||
import { Info } from '../models/info';
|
||||
|
||||
export interface SendRequest extends ProtocolRequest {
|
||||
// 0. 대화방SEQ(s)
|
||||
|
@ -41,6 +43,8 @@ export interface SendResponse extends ProtocolResponse {
|
|||
ForcedExitType: string;
|
||||
// 요청자 이름(s)
|
||||
senderName: string;
|
||||
/** Decoded Info */
|
||||
info?: Info<EventJson>;
|
||||
}
|
||||
|
||||
export interface SendNotification extends ProtocolNotification {
|
||||
|
@ -66,6 +70,8 @@ export interface SendNotification extends ProtocolNotification {
|
|||
id?: string;
|
||||
/** 회사코드(s) */
|
||||
companyCode?: string;
|
||||
/** Decoded Info */
|
||||
info?: Info<EventJson>;
|
||||
}
|
||||
|
||||
export const encodeSend: ProtocolEncoder<SendRequest> = (req: SendRequest) => {
|
||||
|
@ -83,33 +89,63 @@ export const encodeSend: ProtocolEncoder<SendRequest> = (req: SendRequest) => {
|
|||
export const decodeSend: ProtocolDecoder<SendResponse> = (
|
||||
message: ProtocolMessage
|
||||
) => {
|
||||
const seq = message.bodyList[1];
|
||||
const eventType = message.bodyList[2] as EventType;
|
||||
const sendDate = message.bodyList[3];
|
||||
const sentMessage = message.bodyList[4];
|
||||
const receiverCount = message.bodyList[5] || 0;
|
||||
|
||||
return decodeProtocolMessage(message, {
|
||||
roomSeq: message.bodyList[0],
|
||||
seq: message.bodyList[1],
|
||||
eventType: message.bodyList[2] as EventType,
|
||||
sendDate: message.bodyList[3],
|
||||
message: message.bodyList[4],
|
||||
receiverCount: message.bodyList[5] || 0,
|
||||
eventType,
|
||||
sendDate,
|
||||
message: sentMessage,
|
||||
receiverCount,
|
||||
pushStatus: message.bodyList[6] as PushStatus,
|
||||
ForcedExitType: message.bodyList[7],
|
||||
senderName: message.bodyList[8]
|
||||
senderName: message.bodyList[8],
|
||||
info: {
|
||||
seq,
|
||||
type: eventType,
|
||||
senderSeq: message.senderSeq,
|
||||
sendDate,
|
||||
sentMessage,
|
||||
sentMessageJson: decodeEventJson(eventType, sentMessage),
|
||||
receiverCount
|
||||
}
|
||||
} as SendResponse);
|
||||
};
|
||||
|
||||
export const decodeSendNotification: ProtocolDecoder<SendNotification> = (
|
||||
message: ProtocolMessage
|
||||
) => {
|
||||
const seq = message.bodyList[1];
|
||||
const eventType = message.bodyList[2] as EventType;
|
||||
const sendDate = message.bodyList[3];
|
||||
const sentMessage = message.bodyList[4];
|
||||
const receiverCount = message.bodyList[5] || 0;
|
||||
|
||||
return decodeProtocolMessage(message, {
|
||||
roomSeq: message.bodyList[0],
|
||||
seq: message.bodyList[1],
|
||||
eventType: message.bodyList[2] as EventType,
|
||||
sendDate: message.bodyList[3],
|
||||
message: message.bodyList[4],
|
||||
receiverCount: message.bodyList[5] || 0,
|
||||
seq,
|
||||
eventType,
|
||||
sendDate,
|
||||
message: sentMessage,
|
||||
receiverCount,
|
||||
pushStatus: message.bodyList[6] as PushStatus,
|
||||
ForcedExitType: message.bodyList[7],
|
||||
senderName: message.bodyList[8],
|
||||
id: message.bodyList[9],
|
||||
companyCode: message.bodyList[10]
|
||||
companyCode: message.bodyList[10],
|
||||
info: {
|
||||
seq,
|
||||
type: eventType,
|
||||
senderSeq: message.senderSeq,
|
||||
sendDate,
|
||||
sentMessage,
|
||||
sentMessageJson: decodeEventJson(eventType, sentMessage),
|
||||
receiverCount
|
||||
}
|
||||
} as SendNotification);
|
||||
};
|
||||
|
|
|
@ -11,6 +11,27 @@ export * from './lib/protocols/push';
|
|||
export * from './lib/protocols/read';
|
||||
export * from './lib/protocols/send';
|
||||
|
||||
export * from './lib/protocols/event-json/event-json';
|
||||
|
||||
export * from './lib/protocols/event-json/character.event-json';
|
||||
export * from './lib/protocols/event-json/exit.event-json';
|
||||
export * from './lib/protocols/event-json/file.event-json';
|
||||
export * from './lib/protocols/event-json/guide-for-room-timer-changed.event-json';
|
||||
export * from './lib/protocols/event-json/join.event-json';
|
||||
export * from './lib/protocols/event-json/link.event-json';
|
||||
export * from './lib/protocols/event-json/mass-text.event-json';
|
||||
export * from './lib/protocols/event-json/mass-translation.event-json';
|
||||
export * from './lib/protocols/event-json/notification-for-timer-room.event-json';
|
||||
export * from './lib/protocols/event-json/plan.event-json';
|
||||
export * from './lib/protocols/event-json/recalled-message.event-json';
|
||||
export * from './lib/protocols/event-json/rename-room.event-json';
|
||||
export * from './lib/protocols/event-json/sticker.event-json';
|
||||
export * from './lib/protocols/event-json/translation.event-json';
|
||||
export * from './lib/protocols/event-json/video-conference.event-json';
|
||||
export * from './lib/protocols/event-json/video-streamming.event-json';
|
||||
|
||||
export * from './lib/protocols/event-json/codec';
|
||||
|
||||
export * from './lib/services/event-protocol.service';
|
||||
|
||||
export * from './lib/types/event.type';
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
<div class="bubble-main">
|
||||
<!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가-->
|
||||
<!-- <div class="file-img" [ngClass]="fileInfo.FileExt"></div> -->
|
||||
<div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.FileExt]">
|
||||
<div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.fileExt]">
|
||||
<div class="ico"></div>
|
||||
</div>
|
||||
<ul class="file-info">
|
||||
<li class="file-name">
|
||||
{{ fileInfo.FileName }}
|
||||
{{ fileInfo.fileName }}
|
||||
</li>
|
||||
<li class="file-size">
|
||||
{{ fileInfo.AttSize | ucapBytes }}
|
||||
{{ fileInfo.attachmentSize | ucapBytes }}
|
||||
</li>
|
||||
<li class="file-ext">
|
||||
{{ fileInfo.FileExt }}
|
||||
{{ fileInfo.fileExt }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<ul *ngIf="expired" class="expired">
|
||||
<li>기간이 만료된 파일입니다.</li>
|
||||
</ul>
|
||||
<ul *ngIf="!expired && fileInfo && fileInfo.AttSEQ">
|
||||
<ul *ngIf="!expired && fileInfo && fileInfo.attachmentSeq">
|
||||
<li>
|
||||
<button mat-button (click)="onClickSave()">Save</button>
|
||||
</li>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { FileInfo } from '../../models/file-info.json';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-attach-file',
|
||||
|
@ -9,7 +9,7 @@ import { NGXLogger } from 'ngx-logger';
|
|||
})
|
||||
export class AttachFileComponent implements OnInit {
|
||||
@Input()
|
||||
fileInfo: FileInfo;
|
||||
fileInfo: FileEventJson;
|
||||
@Input()
|
||||
expired = false;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Info } from '@ucap-webmessenger/protocol-event';
|
||||
import { Info, EventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import { DatePipe } from '@angular/common';
|
||||
|
||||
@Component({
|
||||
|
@ -9,7 +9,7 @@ import { DatePipe } from '@angular/common';
|
|||
})
|
||||
export class DateSplitterComponent implements OnInit {
|
||||
@Input()
|
||||
message: Info;
|
||||
message: Info<EventJson>;
|
||||
|
||||
dateInfo: string;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ng-container
|
||||
*ngIf="fileInfo && fileInfo.FileType"
|
||||
[ngSwitch]="fileInfo.FileType"
|
||||
*ngIf="fileInfo && fileInfo.fileType"
|
||||
[ngSwitch]="fileInfo.fileType"
|
||||
>
|
||||
<ucap-chat-message-box-attach-file
|
||||
*ngSwitchCase="FileType.File"
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
|
||||
import { Info, InfoResponse } from '@ucap-webmessenger/protocol-event';
|
||||
import {
|
||||
Info,
|
||||
InfoResponse,
|
||||
FileEventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
import { StatusCode } from '@ucap-webmessenger/api';
|
||||
import { FileType } from '@ucap-webmessenger/protocol-file';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { FileInfo } from '../../models/file-info.json';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-file',
|
||||
|
@ -12,27 +15,27 @@ import { FileInfo } from '../../models/file-info.json';
|
|||
})
|
||||
export class FileComponent implements OnInit {
|
||||
@Input()
|
||||
message: Info;
|
||||
message: Info<FileEventJson>;
|
||||
@Input()
|
||||
eventInfoStatus: InfoResponse;
|
||||
|
||||
@Output()
|
||||
save = new EventEmitter<{ fileInfo: FileInfo; type: string }>();
|
||||
save = new EventEmitter<{ fileInfo: FileEventJson; type: string }>();
|
||||
@Output()
|
||||
imageViewer = new EventEmitter<FileInfo>();
|
||||
imageViewer = new EventEmitter<FileEventJson>();
|
||||
|
||||
fileInfo?: FileInfo;
|
||||
fileInfo?: FileEventJson;
|
||||
errorMessage?: string;
|
||||
FileType = FileType;
|
||||
|
||||
constructor(private logger: NGXLogger) {}
|
||||
|
||||
ngOnInit() {
|
||||
const contentJson: FileInfo = JSON.parse(this.message.sentMessage);
|
||||
if (contentJson.StatusCode === StatusCode.Success) {
|
||||
this.fileInfo = contentJson;
|
||||
if (StatusCode.Success === this.message.sentMessageJson.statusCode) {
|
||||
this.fileInfo = this.message.sentMessageJson;
|
||||
} else {
|
||||
this.errorMessage = contentJson.ErrorMessage || '[Error] System Error!!';
|
||||
this.errorMessage =
|
||||
this.message.sentMessageJson.errorMessage || '[Error] System Error!!';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +50,7 @@ export class FileComponent implements OnInit {
|
|||
}
|
||||
}
|
||||
|
||||
onClickImageViewer(fileInfo: FileInfo) {
|
||||
onClickImageViewer(fileInfo: FileEventJson) {
|
||||
this.imageViewer.emit(this.fileInfo);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<div class="bubble-main">
|
||||
<img [src]="fileInfo.ThumbURL">
|
||||
<div class="bubble-main">
|
||||
<img [src]="fileInfo.thumbUrl" />
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { FileInfo } from '../../models/file-info.json';
|
||||
import { FileEventJson } from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-image',
|
||||
|
@ -9,7 +9,7 @@ import { FileInfo } from '../../models/file-info.json';
|
|||
})
|
||||
export class ImageComponent implements OnInit {
|
||||
@Input()
|
||||
fileInfo: FileInfo;
|
||||
fileInfo: FileEventJson;
|
||||
@Input()
|
||||
expired = false;
|
||||
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { Info, EventType } from '@ucap-webmessenger/protocol-event';
|
||||
import {
|
||||
Info,
|
||||
EventType,
|
||||
EventJson,
|
||||
JoinEventJson,
|
||||
RenameRoomEventJson,
|
||||
NotificationForTimerRoomEventJson,
|
||||
GuideForRoomTimerChangedEventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-information',
|
||||
|
@ -9,7 +17,7 @@ import { Info, EventType } from '@ucap-webmessenger/protocol-event';
|
|||
})
|
||||
export class InformationComponent implements OnInit {
|
||||
@Input()
|
||||
message: Info;
|
||||
message: Info<EventJson>;
|
||||
@Input()
|
||||
senderName?: string;
|
||||
|
||||
|
@ -20,41 +28,47 @@ export class InformationComponent implements OnInit {
|
|||
ngOnInit() {
|
||||
switch (this.message.type) {
|
||||
case EventType.Join:
|
||||
let owner: string;
|
||||
const inviter: string[] = [];
|
||||
this.message.sentMessage.split(',').forEach((userName, idx) => {
|
||||
if (idx === 0) {
|
||||
owner = userName + '님';
|
||||
} else {
|
||||
{
|
||||
const m = this.message as Info<JoinEventJson>;
|
||||
|
||||
const owner = m.sentMessageJson.owner + '님';
|
||||
const inviter: string[] = [];
|
||||
|
||||
m.sentMessageJson.inviter.forEach((userName, idx) => {
|
||||
inviter.push(userName + '님');
|
||||
}
|
||||
});
|
||||
this.contents = `${owner}이 ${inviter.join(',')}을 초대했습니다.`;
|
||||
});
|
||||
|
||||
this.contents = `${owner}이 ${inviter.join(',')}을 초대했습니다.`;
|
||||
}
|
||||
break;
|
||||
case EventType.Exit:
|
||||
this.contents = `${this.message.sentMessage}님이 퇴장하셨습니다.`;
|
||||
{
|
||||
const m = this.message as Info<JoinEventJson>;
|
||||
this.contents = `${m.sentMessage}님이 퇴장하셨습니다.`;
|
||||
}
|
||||
break;
|
||||
case EventType.RenameRoom:
|
||||
this.contents = `${this.message.sentMessage.substring(
|
||||
0,
|
||||
this.message.sentMessage.indexOf(',')
|
||||
)}님이 대화방명을 '${this.message.sentMessage.substring(
|
||||
this.message.sentMessage.indexOf(',') + 1
|
||||
)}'으로 변경하셨습니다.`;
|
||||
{
|
||||
const m = this.message as Info<RenameRoomEventJson>;
|
||||
this.contents = `${m.sentMessageJson.requester}님이 대화방명을 '${m.sentMessageJson.roomName}'으로 변경하셨습니다.`;
|
||||
}
|
||||
break;
|
||||
case EventType.NotificationForTimerRoom:
|
||||
/**
|
||||
* 타이머대화방 알림으로서 채팅 이벤트에서 처리하지 않고, 대화방 글로벌에서 처리.
|
||||
*/
|
||||
this.contents = this.message.sentMessage;
|
||||
{
|
||||
const m = this.message as Info<NotificationForTimerRoomEventJson>;
|
||||
/**
|
||||
* 타이머대화방 알림으로서 채팅 이벤트에서 처리하지 않고, 대화방 글로벌에서 처리.
|
||||
*/
|
||||
this.contents = m.sentMessage;
|
||||
}
|
||||
break;
|
||||
case EventType.GuideForRoomTimerChanged:
|
||||
const values = this.message.sentMessage.split(',');
|
||||
if (values && values.length === 2) {
|
||||
{
|
||||
const m = this.message as Info<GuideForRoomTimerChangedEventJson>;
|
||||
this.contents = `${
|
||||
this.senderName
|
||||
}님이 타이머를 설정하였습니다.(${this.getCalcTimer(
|
||||
Number(values[1]) * 1000
|
||||
m.sentMessageJson.time * 1000
|
||||
)})`;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { Info } from '@ucap-webmessenger/protocol-event';
|
||||
import { Info, MassTextEventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { StatusCode } from '@ucap-webmessenger/api';
|
||||
import { MassTextInfo } from '../../models/mass-talk-info.json';
|
||||
import { StringUtil } from '@ucap-webmessenger/ui';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-mass',
|
||||
|
@ -12,7 +10,7 @@ import { StringUtil } from '@ucap-webmessenger/ui';
|
|||
})
|
||||
export class MassComponent implements OnInit {
|
||||
@Input()
|
||||
message: Info;
|
||||
message: Info<MassTextEventJson>;
|
||||
|
||||
@Output()
|
||||
massDetail = new EventEmitter<number>();
|
||||
|
@ -25,18 +23,16 @@ export class MassComponent implements OnInit {
|
|||
|
||||
ngOnInit() {
|
||||
try {
|
||||
const contentJson: MassTextInfo = StringUtil.receiveAnalization(
|
||||
this.message.sentMessage
|
||||
);
|
||||
if (contentJson.StatusCode === StatusCode.Success) {
|
||||
this.content = contentJson.Content;
|
||||
if (StatusCode.Success === this.message.sentMessageJson.statusCode) {
|
||||
this.content = this.message.sentMessageJson.content;
|
||||
} else {
|
||||
this.content = contentJson.ErrorMessage || '[Error] System Error!!';
|
||||
this.content =
|
||||
this.message.sentMessageJson.errorMessage || '[Error] System Error!!';
|
||||
this.detailButteonShow = false;
|
||||
}
|
||||
|
||||
if (!!contentJson.EventMassSeq) {
|
||||
this.eventMassSeq = contentJson.EventMassSeq;
|
||||
if (!!this.message.sentMessageJson.massSeq) {
|
||||
this.eventMassSeq = this.message.sentMessageJson.massSeq;
|
||||
} else {
|
||||
this.detailButteonShow = false;
|
||||
}
|
||||
|
|
|
@ -2,17 +2,15 @@
|
|||
<div class="event-header">이벤트제목</div>
|
||||
<ul class="event-info">
|
||||
<li class="event-title">
|
||||
이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀
|
||||
</li>
|
||||
<li class="event-date">
|
||||
<span>날짜</span> 2019.09.30
|
||||
</li>
|
||||
<li class="event-time">
|
||||
<span>시간</span>오후 10시
|
||||
이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트
|
||||
타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트 타일틀이벤트
|
||||
타일틀이벤트 타일틀
|
||||
</li>
|
||||
<li class="event-date"><span>날짜</span> 2019.09.30</li>
|
||||
<li class="event-time"><span>시간</span>오후 10시</li>
|
||||
</ul>
|
||||
|
||||
<div class="btn-box">
|
||||
<button mat-button (click)="onClickSave()">상세보기</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,4 +9,6 @@ export class ScheduleComponent implements OnInit {
|
|||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
onClickSave(): void {}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Info } from '@ucap-webmessenger/protocol-event';
|
||||
import { Info, StickerEventJson } from '@ucap-webmessenger/protocol-event';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { StickerInfo } from '../../models/sticker-info.json';
|
||||
|
||||
|
@ -10,7 +10,7 @@ import { StickerInfo } from '../../models/sticker-info.json';
|
|||
})
|
||||
export class StickerComponent implements OnInit {
|
||||
@Input()
|
||||
message: Info;
|
||||
message: Info<StickerEventJson>;
|
||||
|
||||
contentJson?: StickerInfo;
|
||||
stickerUrl?: string;
|
||||
|
@ -18,9 +18,8 @@ export class StickerComponent implements OnInit {
|
|||
|
||||
ngOnInit() {
|
||||
try {
|
||||
this.contentJson = JSON.parse(this.message.sentMessage);
|
||||
if (!!this.contentJson.file) {
|
||||
this.stickerUrl = `assets/sticker/sticker_s_${this.contentJson.file}.png`;
|
||||
if (!!this.message.sentMessageJson.file) {
|
||||
this.stickerUrl = `assets/sticker/sticker_s_${this.message.sentMessageJson.file}.png`;
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Info } from '@ucap-webmessenger/protocol-event';
|
||||
import { StringUtil } from '@ucap-webmessenger/ui';
|
||||
import { Info, EventJson } from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-text',
|
||||
|
@ -9,7 +8,7 @@ import { StringUtil } from '@ucap-webmessenger/ui';
|
|||
})
|
||||
export class TextComponent implements OnInit {
|
||||
@Input()
|
||||
message: Info;
|
||||
message: Info<EventJson>;
|
||||
|
||||
test = `가<br>나<br >다<br/>라<br />마<br class=""/>바사`;
|
||||
constructor() {}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
<div class="translation-main">
|
||||
<div class="original">
|
||||
{{ message.sentMessage }}
|
||||
{{ message.sentMessageJson.translation }}
|
||||
</div>
|
||||
<div class="translation">
|
||||
<span class="language">Kor</span>
|
||||
녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문
|
||||
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트
|
||||
녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문
|
||||
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요
|
||||
장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문
|
||||
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요
|
||||
장문장문 롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문
|
||||
롱텍스트안녕하세요 장문장문 롱텍스트안녕하세요 장문장문 롱텍스트
|
||||
</div>
|
||||
<div class="btn-box">
|
||||
<ul>
|
||||
|
@ -17,4 +21,4 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { Info, TranslationEventJson } from '@ucap-webmessenger/protocol-event';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-translation',
|
||||
|
@ -6,6 +7,9 @@ import { Component, OnInit } from '@angular/core';
|
|||
styleUrls: ['./translation.component.scss']
|
||||
})
|
||||
export class TranslationComponent implements OnInit {
|
||||
@Input()
|
||||
message: Info<TranslationEventJson>;
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
|
|
@ -11,7 +11,8 @@ import {
|
|||
import {
|
||||
Info,
|
||||
EventType,
|
||||
InfoResponse
|
||||
InfoResponse,
|
||||
EventJson
|
||||
} from '@ucap-webmessenger/protocol-event';
|
||||
import { LoginResponse } from '@ucap-webmessenger/protocol-authentication';
|
||||
import { UserInfo } from '@ucap-webmessenger/protocol-room';
|
||||
|
@ -29,7 +30,7 @@ export class MessagesComponent implements OnInit {
|
|||
@Input()
|
||||
loginRes: LoginResponse;
|
||||
@Input()
|
||||
messages: Info[];
|
||||
messages: Info<EventJson>[];
|
||||
@Input()
|
||||
eventInfoStatus?: InfoResponse;
|
||||
@Input()
|
||||
|
@ -50,7 +51,7 @@ export class MessagesComponent implements OnInit {
|
|||
@Output()
|
||||
contextMenu = new EventEmitter<{
|
||||
event: MouseEvent;
|
||||
message: Info;
|
||||
message: Info<EventJson>;
|
||||
}>();
|
||||
|
||||
EventType = EventType;
|
||||
|
@ -94,7 +95,7 @@ export class MessagesComponent implements OnInit {
|
|||
return '';
|
||||
}
|
||||
|
||||
getUnreadCount(message: Info): string | number {
|
||||
getUnreadCount(message: Info<EventJson>): string | number {
|
||||
const unreadCnt = this.userInfos.filter(user => {
|
||||
if (message.senderSeq === user.seq) {
|
||||
// 본인 글은 unreadCount 에 포함하지 않는다.
|
||||
|
@ -110,7 +111,7 @@ export class MessagesComponent implements OnInit {
|
|||
* @description 정보성 event 일 경우 프로필, 일시 를 표현하지 않는다.
|
||||
* Edit with reducers.ts / sync / updateRoomForNewEventMessage
|
||||
*/
|
||||
getIsInformation(info: Info) {
|
||||
getIsInformation(info: Info<EventJson>) {
|
||||
if (
|
||||
info.type === EventType.Join ||
|
||||
info.type === EventType.Exit ||
|
||||
|
@ -166,7 +167,7 @@ export class MessagesComponent implements OnInit {
|
|||
}
|
||||
|
||||
/** [Event] Context Menu */
|
||||
onContextMenuMessage(event: MouseEvent, message: Info) {
|
||||
onContextMenuMessage(event: MouseEvent, message: Info<EventJson>) {
|
||||
this.contextMenu.emit({ event, message });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
<div class="ucap-image-viewer-container">
|
||||
<div class="ucap-image-viewer-header">
|
||||
<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
|
||||
>
|
||||
<mat-icon
|
||||
class="example-icon"
|
||||
aria-hidden="false"
|
||||
aria-label="Example delete icon"
|
||||
>delete</mat-icon
|
||||
>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
.ucap-image-viewer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.ucap-image-viewer-header {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
|
||||
.ucap-image-viewer-spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { FileViewerComponent } from './file-viewer.component';
|
||||
|
||||
describe('FileViewerComponent', () => {
|
||||
let component: FileViewerComponent;
|
||||
let fixture: ComponentFixture<FileViewerComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [FileViewerComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FileViewerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ucapAnimations } from '../animations';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-file-viewer',
|
||||
templateUrl: './file-viewer.component.html',
|
||||
styleUrls: ['./file-viewer.component.scss'],
|
||||
animations: ucapAnimations
|
||||
})
|
||||
export class FileViewerComponent implements OnInit {
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
ngOnInit() {}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<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
|
||||
>
|
||||
<mat-icon
|
||||
class="example-icon"
|
||||
aria-hidden="false"
|
||||
aria-label="Example delete icon"
|
||||
>delete</mat-icon
|
||||
>
|
||||
</mat-toolbar>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
.ucap-image-viewer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { DocumentViewerComponent } from './document-viewer.component';
|
||||
|
||||
describe('DocumentViewerComponent', () => {
|
||||
let component: DocumentViewerComponent;
|
||||
let fixture: ComponentFixture<DocumentViewerComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DocumentViewerComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DocumentViewerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ucapAnimations } from '../../animations';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-document-viewer',
|
||||
templateUrl: './document-viewer.component.html',
|
||||
styleUrls: ['./document-viewer.component.scss'],
|
||||
animations: ucapAnimations
|
||||
})
|
||||
export class DocumentViewerComponent implements OnInit {
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
ngOnInit() {}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<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
|
||||
>
|
||||
<mat-icon
|
||||
class="example-icon"
|
||||
aria-hidden="false"
|
||||
aria-label="Example delete icon"
|
||||
>delete</mat-icon
|
||||
>
|
||||
</mat-toolbar>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
.ucap-image-viewer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { ImageViewerComponent } from './image-viewer.component';
|
||||
|
||||
describe('ImageViewerComponent', () => {
|
||||
let component: ImageViewerComponent;
|
||||
let fixture: ComponentFixture<ImageViewerComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ImageViewerComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ImageViewerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ucapAnimations } from '../../animations';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-image-viewer',
|
||||
templateUrl: './image-viewer.component.html',
|
||||
styleUrls: ['./image-viewer.component.scss'],
|
||||
animations: ucapAnimations
|
||||
})
|
||||
export class ImageViewerComponent implements OnInit {
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
ngOnInit() {}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<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
|
||||
>
|
||||
<mat-icon
|
||||
class="example-icon"
|
||||
aria-hidden="false"
|
||||
aria-label="Example delete icon"
|
||||
>delete</mat-icon
|
||||
>
|
||||
</mat-toolbar>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
.ucap-image-viewer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { SoundViewerComponent } from './sound-viewer.component';
|
||||
|
||||
describe('SoundViewerComponent', () => {
|
||||
let component: SoundViewerComponent;
|
||||
let fixture: ComponentFixture<SoundViewerComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SoundViewerComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SoundViewerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ucapAnimations } from '../../animations';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-sound-viewer',
|
||||
templateUrl: './sound-viewer.component.html',
|
||||
styleUrls: ['./sound-viewer.component.scss'],
|
||||
animations: ucapAnimations
|
||||
})
|
||||
export class SoundViewerComponent implements OnInit {
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
ngOnInit() {}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<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
|
||||
>
|
||||
<mat-icon
|
||||
class="example-icon"
|
||||
aria-hidden="false"
|
||||
aria-label="Example delete icon"
|
||||
>delete</mat-icon
|
||||
>
|
||||
</mat-toolbar>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
.ucap-image-viewer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/* tslint:disable:no-unused-variable */
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
|
||||
import { ImageViewerComponent } from './image-viewer.component';
|
||||
|
||||
describe('ImageViewerComponent', () => {
|
||||
let component: ImageViewerComponent;
|
||||
let fixture: ComponentFixture<ImageViewerComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ImageViewerComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ImageViewerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||
import { ucapAnimations } from '../../animations';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-video-viewer',
|
||||
templateUrl: './video-viewer.component.html',
|
||||
styleUrls: ['./video-viewer.component.scss'],
|
||||
animations: ucapAnimations
|
||||
})
|
||||
export class VideoViewerComponent implements OnInit {
|
||||
@Output()
|
||||
closed = new EventEmitter<void>();
|
||||
|
||||
constructor() {}
|
||||
ngOnInit() {}
|
||||
}
|
|
@ -1,7 +1,19 @@
|
|||
import { FileUploadQueueComponent } from './file-upload-queue.component';
|
||||
import { FloatActionButtonComponent } from './float-action-button.component';
|
||||
import { FileViewerComponent } from './file-viewer.component';
|
||||
|
||||
import { DocumentViewerComponent } from './file-viewer/document-viewer.component';
|
||||
import { ImageViewerComponent } from './file-viewer/image-viewer.component';
|
||||
import { SoundViewerComponent } from './file-viewer/sound-viewer.component';
|
||||
import { VideoViewerComponent } from './file-viewer/video-viewer.component';
|
||||
|
||||
export const UI_COMMON_COMPONENTS = [
|
||||
FileUploadQueueComponent,
|
||||
FloatActionButtonComponent
|
||||
FloatActionButtonComponent,
|
||||
FileViewerComponent,
|
||||
|
||||
DocumentViewerComponent,
|
||||
ImageViewerComponent,
|
||||
SoundViewerComponent,
|
||||
VideoViewerComponent
|
||||
];
|
||||
|
|
|
@ -10,6 +10,7 @@ import { MatDialogModule } from '@angular/material/dialog';
|
|||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
|
||||
import { DragDropModule } from '@angular/cdk/drag-drop';
|
||||
|
||||
|
@ -61,6 +62,7 @@ const SERVICES = [
|
|||
MatIconModule,
|
||||
MatProgressBarModule,
|
||||
MatSnackBarModule,
|
||||
MatToolbarModule,
|
||||
MatTooltipModule,
|
||||
DragDropModule
|
||||
],
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { JsonObject } from 'type-fest';
|
||||
|
||||
export class StringUtil {
|
||||
/**
|
||||
* linefeed > <br>
|
||||
|
@ -39,7 +37,7 @@ export class StringUtil {
|
|||
* Json String Analization.
|
||||
* @description Editing with json.util.ts
|
||||
*/
|
||||
public static receiveAnalization(jsonStr: string): JsonObject {
|
||||
public static receiveAnalization(jsonStr: string): any {
|
||||
const startJson = jsonStr.indexOf('{');
|
||||
const endJson = jsonStr.lastIndexOf('}');
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user