0528 sync
This commit is contained in:
parent
02c2645e01
commit
176eb2bbd6
37
package-lock.json
generated
37
package-lock.json
generated
|
@ -3314,9 +3314,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@ucap/api-common": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/api-common/-/api-common-0.0.3.tgz",
|
||||
"integrity": "sha512-rifcCToIXdWZb9R3UXu2bXDqj/KBX3xfxHPFgx+Wp1TBUh6d+xszNYN7+mZbDKKSOYUgpVeuaF8leKkTwXUh9g==",
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/api-common/-/api-common-0.0.5.tgz",
|
||||
"integrity": "sha512-7V/Ea4FbLIKE+/ti414Spm6PQS3ZDcsHTZStLLYkMQrU7x+GbrGnHGOcvzzAQtE2SmpoNd3q6JzoYY+CK1eCkA==",
|
||||
"dev": true
|
||||
},
|
||||
"@ucap/api-external": {
|
||||
|
@ -3393,8 +3393,8 @@
|
|||
"dev": true
|
||||
},
|
||||
"@ucap/ng-core": {
|
||||
"version": "file:pack/ucap-ng-core-0.0.1.tgz",
|
||||
"integrity": "sha512-okuTcVh/9VkF9dA1d/nsPuLL8ft2+4E77mgqfuPI2mI6IA7udQpC+hdMtyqtAN4LmT39iF1VGFv7UJYRK7fN7w==",
|
||||
"version": "file:pack/ucap-ng-core-0.0.7.tgz",
|
||||
"integrity": "sha512-ZC6LE3A0bg+REGbzDI/i1ad7mGpKsw6X0UtZ+Q8TUthHNv0DfWEieHFCgfYTRY1u022XyQ4ViOsrq9KunU1vfw==",
|
||||
"dev": true
|
||||
},
|
||||
"@ucap/ng-i18n": {
|
||||
|
@ -3508,8 +3508,8 @@
|
|||
"dev": true
|
||||
},
|
||||
"@ucap/ng-store-chat": {
|
||||
"version": "file:pack/ucap-ng-store-chat-0.0.13.tgz",
|
||||
"integrity": "sha512-o+BCCSMxneUenRHEW47sSY22+Zt3lyr202Lg4bub9OVRbW5CVohHez8H+JwK+w+Lf8KbqG32V1ZjKLGclTpboA==",
|
||||
"version": "file:pack/ucap-ng-store-chat-0.0.16.tgz",
|
||||
"integrity": "sha512-Rwupo2Gqa+4+0ANBZLtup+mdlTU42Tj5gkGnJJ86Vylo4xQcdR+PUdedWZ/lG7DyzrfBc8ON80aUPRikpWUCow==",
|
||||
"dev": true
|
||||
},
|
||||
"@ucap/ng-store-group": {
|
||||
|
@ -3528,13 +3528,8 @@
|
|||
"dev": true
|
||||
},
|
||||
"@ucap/ng-ui-authentication": {
|
||||
"version": "file:pack/ucap-ng-ui-authentication-0.0.24.tgz",
|
||||
"integrity": "sha512-6QMJ8dieTnbPANsBzg2Ll3HH5q6Bzl2iSM19yHq8Ct7XOmElrYqrEZmxbDyYO+aCXIAwd2t7vu+rTsHfz3XOQg==",
|
||||
"dev": true
|
||||
},
|
||||
"@ucap/ng-ui-chat": {
|
||||
"version": "file:pack/ucap-ng-ui-chat-0.0.9.tgz",
|
||||
"integrity": "sha512-6qvzcTuylkxVjsqajsLW15laOyOskxVMy238/Ju1yYvwCRyHygwS1i67APoG5tv+SWu+l38f9uWIqzfy7WYHkQ==",
|
||||
"version": "file:pack/ucap-ng-ui-authentication-0.0.25.tgz",
|
||||
"integrity": "sha512-/KgMR8JHcnOm+XwMQVhHGud61Hivi/fX0NucxUglsOguYiWL6Ms61FdRezB3wuKs5h/5+zI4QIWLdWdT8GT/Dg==",
|
||||
"dev": true
|
||||
},
|
||||
"@ucap/ng-ui-group": {
|
||||
|
@ -3544,13 +3539,7 @@
|
|||
},
|
||||
"@ucap/ng-ui-material": {
|
||||
"version": "file:pack/ucap-ng-ui-material-0.0.4.tgz",
|
||||
"integrity": "sha512-ySPULAbP+nQ65hBG2VWZ2H5Hr7muuTGGNXs6A+S3lsxLaW452wM3GNyUBhvUopr8LaSsoOPpp4nK1JeC0fG6pA==",
|
||||
"dev": true
|
||||
},
|
||||
"@ucap/ng-ui-organization": {
|
||||
"version": "file:pack/ucap-ng-ui-organization-0.0.55.tgz",
|
||||
"integrity": "sha512-vfpKd3fbd+I0Od8aB2nIFfjuI7wj3Ziu/uiTEmZxKwZy7uZrNYm59BPbctKW3AQsQ4UtnLofhlBbAA7e9pT80Q==",
|
||||
"dev": true
|
||||
"integrity": "sha512-ySPULAbP+nQ65hBG2VWZ2H5Hr7muuTGGNXs6A+S3lsxLaW452wM3GNyUBhvUopr8LaSsoOPpp4nK1JeC0fG6pA=="
|
||||
},
|
||||
"@ucap/ng-ui-skin-default": {
|
||||
"version": "file:pack/ucap-ng-ui-skin-default-0.0.1.tgz",
|
||||
|
@ -3640,9 +3629,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@ucap/protocol-room": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-room/-/protocol-room-0.0.5.tgz",
|
||||
"integrity": "sha512-RSzLtnz5JVeDz9Y8gP17+LO6OG2NtLIPpW+JuHnqhfJyB93v2QmqqK5T7NbGYok72jRp4m7cJZEp1tLNYCKcmA==",
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-room/-/protocol-room-0.0.6.tgz",
|
||||
"integrity": "sha512-PaEztUnZgmsH/Vo4JijJxpu9DBSA4SuIBW7y+L8BcBdPtzl49jBolm08+S9vgIr8cf9MtONdU37Al+uOTVki2g==",
|
||||
"dev": true
|
||||
},
|
||||
"@ucap/protocol-service": {
|
||||
|
|
14
package.json
14
package.json
|
@ -148,7 +148,7 @@
|
|||
"@types/moment-timezone": "^0.5.12",
|
||||
"@types/node": "^12.12.30",
|
||||
"@ucap/api": "~0.0.1",
|
||||
"@ucap/api-common": "~0.0.1",
|
||||
"@ucap/api-common": "~0.0.5",
|
||||
"@ucap/api-external": "~0.0.2",
|
||||
"@ucap/api-message": "~0.0.1",
|
||||
"@ucap/api-prompt": "~0.0.1",
|
||||
|
@ -162,7 +162,7 @@
|
|||
"@ucap/ng-api-message": "file:pack/ucap-ng-api-message-0.0.1.tgz",
|
||||
"@ucap/ng-api-prompt": "file:pack/ucap-ng-api-prompt-0.0.1.tgz",
|
||||
"@ucap/ng-api-public": "file:pack/ucap-ng-api-public-0.0.1.tgz",
|
||||
"@ucap/ng-core": "file:pack/ucap-ng-core-0.0.1.tgz",
|
||||
"@ucap/ng-core": "file:pack/ucap-ng-core-0.0.7.tgz",
|
||||
"@ucap/ng-i18n": "file:pack/ucap-ng-i18n-0.0.6.tgz",
|
||||
"@ucap/ng-logger": "file:pack/ucap-ng-logger-0.0.2.tgz",
|
||||
"@ucap/ng-native": "file:pack/ucap-ng-native-0.0.1.tgz",
|
||||
|
@ -185,15 +185,15 @@
|
|||
"@ucap/ng-protocol-sync": "file:pack/ucap-ng-protocol-sync-0.0.3.tgz",
|
||||
"@ucap/ng-protocol-umg": "file:pack/ucap-ng-protocol-umg-0.0.3.tgz",
|
||||
"@ucap/ng-store-authentication": "file:pack/ucap-ng-store-authentication-0.0.11.tgz",
|
||||
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.13.tgz",
|
||||
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.16.tgz",
|
||||
"@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.14.tgz",
|
||||
"@ucap/ng-store-organization": "file:pack/ucap-ng-store-organization-0.0.8.tgz",
|
||||
"@ucap/ng-ui": "file:pack/ucap-ng-ui-0.0.19.tgz",
|
||||
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.24.tgz",
|
||||
"@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.9.tgz",
|
||||
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.25.tgz",
|
||||
"@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.12.tgz",
|
||||
"@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.33.tgz",
|
||||
"@ucap/ng-ui-material": "file:pack/ucap-ng-ui-material-0.0.4.tgz",
|
||||
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.55.tgz",
|
||||
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.83.tgz",
|
||||
"@ucap/ng-ui-skin-default": "file:pack/ucap-ng-ui-skin-default-0.0.1.tgz",
|
||||
"@ucap/ng-web-socket": "file:pack/ucap-ng-web-socket-0.0.2.tgz",
|
||||
"@ucap/ng-web-storage": "file:pack/ucap-ng-web-storage-0.0.3.tgz",
|
||||
|
@ -209,7 +209,7 @@
|
|||
"@ucap/protocol-option": "~0.0.7",
|
||||
"@ucap/protocol-ping": "~0.0.4",
|
||||
"@ucap/protocol-query": "~0.0.5",
|
||||
"@ucap/protocol-room": "~0.0.5",
|
||||
"@ucap/protocol-room": "~0.0.6",
|
||||
"@ucap/protocol-service": "~0.0.4",
|
||||
"@ucap/protocol-status": "~0.0.5",
|
||||
"@ucap/protocol-sync": "~0.0.4",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-core",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.7",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
|
24
projects/core/src/lib/utils/params.util.ts
Normal file
24
projects/core/src/lib/utils/params.util.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Params } from '@angular/router';
|
||||
|
||||
export class ParamsUtil {
|
||||
static getParameter<T>(params: Params, key: string, defaultValue: T): T {
|
||||
const v = params[key];
|
||||
if (undefined === v) {
|
||||
if (undefined !== defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
switch (typeof defaultValue) {
|
||||
case 'boolean':
|
||||
return ((v as string).toLowerCase() === 'true' ? true : false) as any;
|
||||
case 'number':
|
||||
return Number(v) as any;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ export interface StickerFilesInfo {
|
|||
index: string;
|
||||
path: string;
|
||||
}
|
||||
export const KEY_STICKER_HISTORY = 'ucap::Sticker_History';
|
||||
export const StickerMap: StickerInfo[] = [
|
||||
{
|
||||
index: '00',
|
||||
|
@ -138,15 +139,18 @@ export const StickerMap: StickerInfo[] = [
|
|||
}
|
||||
];
|
||||
|
||||
const ActiveAndOrdering: string[] = ['00', '01', '02', '03', '04', '05'];
|
||||
export const ActiveAndOrdering: string[] = ['00', '01', '02', '03', '04', '05'];
|
||||
|
||||
export class StickerUtil {
|
||||
static getStickerInfoList(): StickerInfo[] {
|
||||
static getStickerInfoList(
|
||||
stickerMap: StickerInfo[] = StickerMap,
|
||||
activeAndOrdering: string[] = ActiveAndOrdering
|
||||
): StickerInfo[] {
|
||||
const rtnStickerList: StickerInfo[] = [];
|
||||
|
||||
ActiveAndOrdering.forEach(idx => {
|
||||
const stickerInfos: StickerInfo[] = StickerMap.filter(
|
||||
sticker => sticker.index === idx && sticker.useYn
|
||||
activeAndOrdering.forEach((idx) => {
|
||||
const stickerInfos: StickerInfo[] = stickerMap.filter(
|
||||
(sticker) => sticker.index === idx && sticker.useYn
|
||||
);
|
||||
|
||||
if (!!stickerInfos && stickerInfos.length > 0) {
|
||||
|
@ -156,17 +160,4 @@ export class StickerUtil {
|
|||
|
||||
return rtnStickerList;
|
||||
}
|
||||
|
||||
static getStickerInfoSub(index: string): StickerFilesInfo[] {
|
||||
const stickerFilesList: StickerFilesInfo[] = [];
|
||||
const stickerInfos: StickerInfo[] = StickerMap.filter(
|
||||
sticker => sticker.index === index && sticker.useYn
|
||||
);
|
||||
|
||||
if (!!stickerInfos && stickerInfos.length > 0) {
|
||||
stickerFilesList.concat(stickerInfos[0].fileInfos);
|
||||
}
|
||||
|
||||
return stickerFilesList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Public API Surface of core
|
||||
*/
|
||||
|
||||
export * from './lib/utils/params.util';
|
||||
export * from './lib/utils/sticker.util';
|
||||
export * from './lib/utils/string.util';
|
||||
export * from './lib/utils/window.util';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-store-chat",
|
||||
"version": "0.0.13",
|
||||
"version": "0.0.16",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
|
|
@ -83,7 +83,22 @@ export const fileInfosFailure = createAction(
|
|||
*/
|
||||
export const addEvent = createAction(
|
||||
'[ucap::chat::chatting] addEvent',
|
||||
props<{ req: FileInfoRequest }>()
|
||||
props<{
|
||||
roomId: string;
|
||||
info: Info<EventJson>;
|
||||
SVC_TYPE?: number;
|
||||
SSVC_TYPE?: number;
|
||||
}>()
|
||||
);
|
||||
/**
|
||||
* add new event Success.
|
||||
*/
|
||||
export const addEventSuccess = createAction(
|
||||
'[ucap::chat::chatting] addEvent Success',
|
||||
props<{
|
||||
roomId: string;
|
||||
info: Info<EventJson>;
|
||||
}>()
|
||||
);
|
||||
|
||||
export const read = createAction(
|
||||
|
@ -105,3 +120,26 @@ export const readFailure = createAction(
|
|||
'[ucap::chat::chatting] read Failure',
|
||||
props<{ error: any }>()
|
||||
);
|
||||
|
||||
export const send = createAction(
|
||||
'[ucap::chat::chatting] Send',
|
||||
props<{ senderSeq: string; req: SendEventRequest }>()
|
||||
);
|
||||
|
||||
export const sendSuccess = createAction(
|
||||
'[ucap::chat::chatting] Send Success',
|
||||
props<{
|
||||
senderSeq: string;
|
||||
res: SendEventResponse;
|
||||
}>()
|
||||
);
|
||||
|
||||
export const sendFailure = createAction(
|
||||
'[ucap::chat::chatting] Send Failure',
|
||||
props<{ error: any }>()
|
||||
);
|
||||
|
||||
export const sendNotification = createAction(
|
||||
'[ucap::chat::chatting] Send Notification',
|
||||
props<{ noti: SendNotification }>()
|
||||
);
|
||||
|
|
|
@ -5,13 +5,16 @@ import {
|
|||
map,
|
||||
catchError,
|
||||
exhaustMap,
|
||||
concatMap,
|
||||
withLatestFrom,
|
||||
debounceTime
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
|
||||
import { Store } from '@ngrx/store';
|
||||
import { Store, select } from '@ngrx/store';
|
||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||
import { Dictionary } from '@ngrx/entity';
|
||||
|
||||
import { EventProtocolService } from '@ucap/ng-protocol-event';
|
||||
import { RoomProtocolService } from '@ucap/ng-protocol-room';
|
||||
|
@ -27,13 +30,24 @@ import {
|
|||
readSuccess,
|
||||
fileInfos,
|
||||
fileInfosFailure,
|
||||
fileInfosSuccess
|
||||
fileInfosSuccess,
|
||||
send,
|
||||
sendSuccess,
|
||||
sendFailure,
|
||||
addEvent,
|
||||
addEventSuccess
|
||||
} from './actions';
|
||||
|
||||
import { InfoRequest, ReadResponse, FileType } from '@ucap/protocol-event';
|
||||
import {
|
||||
InfoRequest,
|
||||
ReadResponse,
|
||||
FileType,
|
||||
EventType
|
||||
} from '@ucap/protocol-event';
|
||||
|
||||
import { ModuleConfig } from '../../config/module-config';
|
||||
import { _MODULE_CONFIG } from '../../config/token';
|
||||
import { Chatting } from './state';
|
||||
|
||||
@Injectable()
|
||||
export class Effects {
|
||||
|
@ -158,6 +172,89 @@ export class Effects {
|
|||
{ dispatch: false }
|
||||
);
|
||||
|
||||
addEvent$ = createEffect(
|
||||
() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(addEvent),
|
||||
withLatestFrom(
|
||||
this.store.pipe(
|
||||
select(
|
||||
(state: any) =>
|
||||
state.chat.chatting.chattings.entities as Dictionary<Chatting>
|
||||
)
|
||||
),
|
||||
this.store.pipe(
|
||||
select((state: any) => state.chat.chatting.activeRoomId)
|
||||
)
|
||||
),
|
||||
tap(([action, chattings, activeRoomId]) => {
|
||||
// Room in chattings state >> event add
|
||||
if (!!chattings && !!chattings[action.roomId]) {
|
||||
if (action.info.type === EventType.File) {
|
||||
// Retrieve event of FileType in rooms
|
||||
this.store.dispatch(
|
||||
fileInfos({
|
||||
req: {
|
||||
roomId: action.roomId,
|
||||
type: FileType.All
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// room > rooms :: finalEvent-Infos refresh
|
||||
this.store.dispatch(
|
||||
addEventSuccess({
|
||||
roomId: action.roomId,
|
||||
info: action.info
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
},
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
send$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(send),
|
||||
concatMap((action) =>
|
||||
this.eventProtocolService.send(action.req).pipe(
|
||||
map((res) => {
|
||||
return sendSuccess({
|
||||
senderSeq: action.senderSeq,
|
||||
res
|
||||
});
|
||||
}),
|
||||
catchError((error) => of(sendFailure({ error })))
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
sendSuccess$ = createEffect(
|
||||
() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(sendSuccess),
|
||||
tap((action) => {
|
||||
const res = action.res;
|
||||
console.log('sendSuccess', action);
|
||||
|
||||
this.store.dispatch(
|
||||
addEvent({
|
||||
roomId: res.roomId,
|
||||
info: res.info,
|
||||
SVC_TYPE: res.SVC_TYPE,
|
||||
SSVC_TYPE: res.SSVC_TYPE
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
},
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
constructor(
|
||||
private actions$: Actions,
|
||||
private store: Store<any>,
|
||||
|
|
|
@ -15,7 +15,8 @@ import {
|
|||
eventsFailure,
|
||||
readSuccess,
|
||||
fileInfosSuccess,
|
||||
fileInfosFailure
|
||||
fileInfosFailure,
|
||||
addEvent
|
||||
} from './actions';
|
||||
|
||||
export const reducer = createReducer(
|
||||
|
@ -163,5 +164,29 @@ export const reducer = createReducer(
|
|||
...state.chattings
|
||||
})
|
||||
};
|
||||
}),
|
||||
|
||||
on(addEvent, (state, action) => {
|
||||
const roomId = action.roomId;
|
||||
const eventInfo = action.info;
|
||||
|
||||
const chatting = state.chattings.entities[roomId];
|
||||
|
||||
if (!!chatting) {
|
||||
return {
|
||||
...state,
|
||||
chattings: adapterChatting.upsertOne(
|
||||
{
|
||||
...chatting,
|
||||
eventList: adapterEventList.upsertOne(eventInfo, {
|
||||
...chatting.eventList
|
||||
})
|
||||
},
|
||||
{ ...state.chattings }
|
||||
)
|
||||
};
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
|
@ -119,6 +119,20 @@ export const roomFailure = createAction(
|
|||
'[ucap::chat::room] room Failure',
|
||||
props<{ error: any }>()
|
||||
);
|
||||
/**
|
||||
* Success of rooms2 request
|
||||
* RoomUserData is detail, short type both.
|
||||
*/
|
||||
export const room2Success = createAction(
|
||||
'[ucap::chat::room] room2 Success',
|
||||
props<{
|
||||
roomInfo: RoomInfo;
|
||||
roomUserInfo: {
|
||||
userInfoShortList: UserInfoShort[];
|
||||
userInfoList: RoomUserInfo[];
|
||||
};
|
||||
}>()
|
||||
);
|
||||
|
||||
/**
|
||||
* create room
|
||||
|
|
|
@ -28,10 +28,10 @@ import {
|
|||
} from '@ucap/protocol-room';
|
||||
|
||||
import { RoomProtocolService } from '@ucap/ng-protocol-room';
|
||||
|
||||
import { SyncProtocolService } from '@ucap/ng-protocol-sync';
|
||||
|
||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
||||
import { addEventSuccess } from '../Chatting/actions';
|
||||
|
||||
import { RoomSelector } from '../state';
|
||||
|
||||
|
@ -77,8 +77,11 @@ import {
|
|||
delMulti,
|
||||
delMultiSuccess,
|
||||
delMultiFailure,
|
||||
selectedRoom
|
||||
selectedRoom,
|
||||
room2Success
|
||||
} from './actions';
|
||||
import { Router } from '@angular/router';
|
||||
import { LocaleCode } from '@ucap/core';
|
||||
|
||||
@Injectable()
|
||||
export class Effects {
|
||||
|
@ -100,8 +103,6 @@ export class Effects {
|
|||
isDetail: false
|
||||
};
|
||||
|
||||
console.log(req);
|
||||
|
||||
// retrieve room info
|
||||
this.store.dispatch(room({ req }));
|
||||
|
||||
|
@ -151,13 +152,25 @@ export class Effects {
|
|||
room$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(room),
|
||||
map((action) => action.req),
|
||||
switchMap((req) => {
|
||||
return this.roomProtocolService.info(req).pipe(
|
||||
switchMap((action) => {
|
||||
const req = action.req;
|
||||
// // CASE :: RoomUser Data 중 Detail data 만 수집.
|
||||
// return this.roomProtocolService.info(req).pipe(
|
||||
// map((res) =>
|
||||
// roomSuccess({
|
||||
// roomInfo: res.roomInfo,
|
||||
// userInfoList: res.userInfoList
|
||||
// })
|
||||
// ),
|
||||
// catchError((error) => of(roomFailure({ error })))
|
||||
// );
|
||||
|
||||
// CASE :: RoomUser Data 중 Detail data, Short data 수집.
|
||||
return this.roomProtocolService.info2(req).pipe(
|
||||
map((res) =>
|
||||
roomSuccess({
|
||||
room2Success({
|
||||
roomInfo: res.roomInfo,
|
||||
userInfoList: res.userInfoList
|
||||
roomUserInfo: res.roomUserInfo
|
||||
})
|
||||
),
|
||||
catchError((error) => of(roomFailure({ error })))
|
||||
|
@ -166,19 +179,40 @@ export class Effects {
|
|||
);
|
||||
});
|
||||
|
||||
create$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
create$ = createEffect(
|
||||
() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(create),
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) => {
|
||||
return this.roomProtocolService.open(req).pipe(
|
||||
map((res: CreateResponse) => {
|
||||
return createSuccess({ res });
|
||||
console.log('CreateResponse', res);
|
||||
|
||||
this.store.dispatch(createSuccess({ res }));
|
||||
|
||||
this.router.navigate(
|
||||
[
|
||||
'chat',
|
||||
{
|
||||
outlets: { content: 'chatroom' }
|
||||
}
|
||||
],
|
||||
{
|
||||
queryParams: { roomId: res.roomId }
|
||||
}
|
||||
);
|
||||
|
||||
// if (!res.newRoom) {
|
||||
// } else {
|
||||
// }
|
||||
}),
|
||||
catchError((error) => of(createFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
createTimer$ = createEffect(() =>
|
||||
|
@ -390,9 +424,30 @@ export class Effects {
|
|||
);
|
||||
});
|
||||
|
||||
/*******************************************************************
|
||||
* [Chatting Action watching.]
|
||||
*******************************************************************/
|
||||
addEventSuccess$ = createEffect(
|
||||
() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(addEventSuccess),
|
||||
withLatestFrom(this.store.pipe(select(RoomSelector.rooms))),
|
||||
map(([action, roomList]) => {
|
||||
const roomId = action.roomId;
|
||||
|
||||
if (!roomList.find((roomInfo) => roomInfo.roomId === roomId)) {
|
||||
this.store.dispatch(rooms({ localeCode: LocaleCode.Korean }));
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
{ dispatch: false }
|
||||
);
|
||||
|
||||
constructor(
|
||||
private actions$: Actions,
|
||||
private store: Store<any>,
|
||||
private router: Router,
|
||||
private syncProtocolService: SyncProtocolService,
|
||||
private roomProtocolService: RoomProtocolService
|
||||
) {}
|
||||
|
|
|
@ -24,9 +24,12 @@ import {
|
|||
delSuccess,
|
||||
rooms2Success,
|
||||
delMultiSuccess,
|
||||
updateSuccess
|
||||
updateSuccess,
|
||||
room2Success,
|
||||
createSuccess
|
||||
} from './actions';
|
||||
import { userInfo } from 'os';
|
||||
import { Info, EventJson } from '@ucap/protocol-event';
|
||||
import { ChatUtil } from '../../utils/chat.util';
|
||||
|
||||
export const reducer = createReducer(
|
||||
initialState,
|
||||
|
@ -136,6 +139,49 @@ export const reducer = createReducer(
|
|||
};
|
||||
}),
|
||||
|
||||
on(room2Success, (state, action) => {
|
||||
const roomUserMapList: RoomUserMap[] = [];
|
||||
const roomUserShortMapList: RoomUserShortMap[] = [];
|
||||
|
||||
if (
|
||||
!!action.roomUserInfo.userInfoList &&
|
||||
0 < action.roomUserInfo.userInfoList.length
|
||||
) {
|
||||
roomUserMapList.push({
|
||||
roomId: action.roomInfo.roomId,
|
||||
userInfos: action.roomUserInfo.userInfoList
|
||||
});
|
||||
}
|
||||
if (
|
||||
!!action.roomUserInfo.userInfoShortList &&
|
||||
0 < action.roomUserInfo.userInfoShortList.length
|
||||
) {
|
||||
roomUserShortMapList.push({
|
||||
roomId: action.roomInfo.roomId,
|
||||
userInfos: action.roomUserInfo.userInfoShortList
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
rooms: adapterRoom.upsertOne(action.roomInfo, {
|
||||
...state.rooms
|
||||
}),
|
||||
roomUsers:
|
||||
0 < roomUserMapList.length
|
||||
? adapterRoomUser.upsertMany(roomUserMapList, {
|
||||
...state.roomUsers
|
||||
})
|
||||
: state.roomUsers,
|
||||
roomUsersShort:
|
||||
0 < roomUserShortMapList.length
|
||||
? adapterRoomUserShort.upsertMany(roomUserShortMapList, {
|
||||
...state.roomUsersShort
|
||||
})
|
||||
: state.roomUsersShort
|
||||
};
|
||||
}),
|
||||
|
||||
on(excludeUserSuccess, (state, action) => {
|
||||
const roomId = action.roomId;
|
||||
const roomUserMap = state.roomUsers.entities[roomId];
|
||||
|
@ -191,6 +237,41 @@ export const reducer = createReducer(
|
|||
};
|
||||
}),
|
||||
|
||||
on(createSuccess, (state, action) => {
|
||||
const standby = state.standbyRooms;
|
||||
const curRoomId = action.res.roomId;
|
||||
if (standby.findIndex((roomId) => roomId === curRoomId) > -1) {
|
||||
// Exist.
|
||||
return state;
|
||||
} else {
|
||||
// Not Exist.
|
||||
return {
|
||||
...state,
|
||||
standbyRooms: [...state.standbyRooms, curRoomId]
|
||||
};
|
||||
}
|
||||
}),
|
||||
|
||||
on(delSuccess, (state, action) => {
|
||||
const roomId = action.res.roomId;
|
||||
const room = state.rooms.entities[roomId];
|
||||
|
||||
if (!room) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
rooms: adapterRoom.removeOne(roomId, { ...state.rooms }),
|
||||
roomUsers: adapterRoomUser.removeOne(roomId, {
|
||||
...state.roomUsers
|
||||
}),
|
||||
roomUsersShort: adapterRoomUserShort.removeOne(roomId, {
|
||||
...state.roomUsersShort
|
||||
})
|
||||
};
|
||||
}),
|
||||
|
||||
on(updateSuccess, (state, action) => {
|
||||
const roomInfo = {
|
||||
...state.rooms.entities[action.res.roomId],
|
||||
|
@ -243,9 +324,9 @@ export const reducer = createReducer(
|
|||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
/*******************************************************************
|
||||
* [Chatting Action watching.]
|
||||
*/
|
||||
*******************************************************************/
|
||||
on(chattingActions.readSuccess, (state, action) => {
|
||||
const roomId = action.roomId;
|
||||
const trgtUserSeq = action.SENDER_SEQ;
|
||||
|
@ -279,7 +360,10 @@ export const reducer = createReducer(
|
|||
userInfos: state.roomUsersShort.entities[roomId].userInfos.map(
|
||||
(roomUserInfo) => {
|
||||
if (roomUserInfo.seq === Number(trgtUserSeq)) {
|
||||
return { ...roomUserInfo, lastReadEventSeq: action.lastReadSeq };
|
||||
return {
|
||||
...roomUserInfo,
|
||||
lastReadEventSeq: action.lastReadSeq
|
||||
};
|
||||
} else {
|
||||
return roomUserInfo;
|
||||
}
|
||||
|
@ -301,5 +385,44 @@ export const reducer = createReducer(
|
|||
})
|
||||
: state.roomUsersShort
|
||||
};
|
||||
}),
|
||||
|
||||
on(chattingActions.addEventSuccess, (state, action) => {
|
||||
const roomId = action.roomId;
|
||||
const info: Info<EventJson> = action.info;
|
||||
|
||||
if (!roomId || !info) {
|
||||
return state;
|
||||
}
|
||||
|
||||
const currentRoomInfo = state.rooms.entities[roomId];
|
||||
const fixedStandByRooms = state.standbyRooms.filter(
|
||||
(standbyRoomId) => standbyRoomId !== roomId
|
||||
);
|
||||
|
||||
if (!!currentRoomInfo) {
|
||||
const finalEventMessage:
|
||||
| string
|
||||
| null = ChatUtil.convertFinalEventMessage(
|
||||
action.info.type,
|
||||
action.info.sentMessageJson || action.info.sentMessage
|
||||
);
|
||||
|
||||
const roomInfo = {
|
||||
...currentRoomInfo,
|
||||
finalEventType: info.type,
|
||||
finalEventDate: info.sendDate,
|
||||
finalEventMessage,
|
||||
finalEventSeq: info.seq
|
||||
} as RoomInfo;
|
||||
|
||||
return {
|
||||
...state,
|
||||
rooms: adapterRoom.upsertOne(roomInfo, { ...state.rooms }),
|
||||
standbyRooms: fixedStandByRooms
|
||||
};
|
||||
} else {
|
||||
return state;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
|
@ -47,6 +47,7 @@ export interface State {
|
|||
rooms: RoomState;
|
||||
roomUsers: RoomUserState;
|
||||
roomUsersShort: RoomUserShortState;
|
||||
standbyRooms: string[];
|
||||
}
|
||||
|
||||
const roomInitialState: RoomState = adapterRoom.getInitialState({
|
||||
|
@ -60,7 +61,8 @@ const roomUserShortInitialState: RoomUserShortState = adapterRoomUserShort.getIn
|
|||
export const initialState: State = {
|
||||
rooms: roomInitialState,
|
||||
roomUsers: roomUserInitialState,
|
||||
roomUsersShort: roomUserShortInitialState
|
||||
roomUsersShort: roomUserShortInitialState,
|
||||
standbyRooms: []
|
||||
};
|
||||
|
||||
const {
|
||||
|
@ -128,6 +130,10 @@ export function selectors<S>(selector: Selector<any, State>) {
|
|||
);
|
||||
}
|
||||
),
|
||||
standbyRooms: createSelector(
|
||||
selector,
|
||||
(state: State) => state.standbyRooms
|
||||
),
|
||||
unreadTotal: createSelector(
|
||||
selectRooms,
|
||||
selectAllForRoom,
|
||||
|
|
138
projects/store-chat/src/lib/utils/chat.util.ts
Normal file
138
projects/store-chat/src/lib/utils/chat.util.ts
Normal file
|
@ -0,0 +1,138 @@
|
|||
import {
|
||||
EventJson,
|
||||
EventType,
|
||||
FileEventJson,
|
||||
FileType,
|
||||
VideoConferenceEventJson,
|
||||
VideoConferenceContentsType,
|
||||
MassTextEventJson,
|
||||
TranslationEventJson,
|
||||
MassTranslationEventJson,
|
||||
PlanEventJson,
|
||||
PlanContentType
|
||||
} from '@ucap/protocol-event';
|
||||
|
||||
export class ChatUtil {
|
||||
public static convertFinalEventMessage(
|
||||
eventType: EventType,
|
||||
finalEventMessage: EventJson
|
||||
): string | null {
|
||||
let eventMessage: string = null;
|
||||
|
||||
switch (eventType) {
|
||||
case EventType.Join:
|
||||
case EventType.Exit:
|
||||
case EventType.ForcedExit:
|
||||
case EventType.RenameRoom:
|
||||
case EventType.NotificationForTimerRoom:
|
||||
case EventType.GuideForRoomTimerChanged:
|
||||
{
|
||||
/**
|
||||
* 해당 타입은 메시지를 갱신하지 않는다.
|
||||
* @description Edit with ui-chat > messages.component.ts
|
||||
*/
|
||||
}
|
||||
break;
|
||||
case EventType.Sticker:
|
||||
{
|
||||
eventMessage = '스티커';
|
||||
}
|
||||
break;
|
||||
case EventType.File:
|
||||
{
|
||||
const m = finalEventMessage as FileEventJson;
|
||||
|
||||
if (FileType.Image === m.fileType) {
|
||||
eventMessage = '이미지';
|
||||
} else {
|
||||
eventMessage = '첨부파일';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.VideoConference:
|
||||
{
|
||||
const m = finalEventMessage as VideoConferenceEventJson;
|
||||
switch (m.contents) {
|
||||
case VideoConferenceContentsType.Now:
|
||||
eventMessage = '화상회의가 개설되었습니다.';
|
||||
break;
|
||||
case VideoConferenceContentsType.New:
|
||||
eventMessage = '화상회의가 등록되었습니다.';
|
||||
break;
|
||||
case VideoConferenceContentsType.Update:
|
||||
eventMessage = '화상회의가 수정되었습니다.';
|
||||
break;
|
||||
case VideoConferenceContentsType.Delete:
|
||||
eventMessage = '화상회의가 취소되었습니다.';
|
||||
break;
|
||||
default:
|
||||
eventMessage = '화상회의';
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.MassText:
|
||||
{
|
||||
const m = finalEventMessage as MassTextEventJson;
|
||||
eventMessage = m.content;
|
||||
}
|
||||
break;
|
||||
case EventType.Translation:
|
||||
{
|
||||
const m = finalEventMessage as TranslationEventJson;
|
||||
eventMessage = m.original;
|
||||
}
|
||||
break;
|
||||
case EventType.MassTranslation:
|
||||
{
|
||||
const m = finalEventMessage as MassTranslationEventJson;
|
||||
eventMessage = m.original;
|
||||
}
|
||||
break;
|
||||
case EventType.Plan:
|
||||
{
|
||||
const m = finalEventMessage as PlanEventJson;
|
||||
switch (m.contents) {
|
||||
case PlanContentType.New:
|
||||
eventMessage = '새로운 일정이 등록되었습니다.';
|
||||
break;
|
||||
case PlanContentType.Update:
|
||||
eventMessage = '일정이 수정되었습니다.';
|
||||
break;
|
||||
case PlanContentType.Delete:
|
||||
eventMessage = '일정이 취소되었습니다.';
|
||||
break;
|
||||
default:
|
||||
eventMessage = '일정이 업데이트 되었습니다.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.NotificationForiOSCapture:
|
||||
{
|
||||
const m = finalEventMessage as string;
|
||||
eventMessage = `${m}님이 대화내용을 캡쳐하였습니다.`;
|
||||
}
|
||||
break;
|
||||
|
||||
case EventType.AllimElephant: // [daesang] 알림
|
||||
{
|
||||
eventMessage = '코끼리 알림';
|
||||
}
|
||||
break;
|
||||
case EventType.AllimTms: // [daesang] 알림
|
||||
{
|
||||
eventMessage = 'TMS 알림';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
const m = finalEventMessage as string;
|
||||
eventMessage = m;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
return eventMessage;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-ui-authentication",
|
||||
"version": "0.0.24",
|
||||
"version": "0.0.25",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
class="ucap-authentication-login-input-field"
|
||||
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
||||
>
|
||||
<mat-form-field>
|
||||
<mat-form-field color="accent">
|
||||
<mat-label>{{ 'login.fields.company' | ucapI18n }}</mat-label>
|
||||
<mat-select
|
||||
[formControl]="companyCodeFormControl"
|
||||
|
@ -26,16 +26,27 @@
|
|||
<div class="ucap-authentication-login-input-field">
|
||||
<!--<span class="input-icon"><i class="mid mdi-account-tie-outline"></i></span>-->
|
||||
<span class="ucap-authentication-login-input-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.82 22">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M0 0H24V24H0z" style="fill: none;" />
|
||||
<g transform="translate(.118 -1)">
|
||||
<path
|
||||
d="M14.39,11.88a5.7,5.7,0,1,0-4.78,0A10.41,10.41,0,0,0,1.59,22v1H22.41V22A10.41,10.41,0,0,0,14.39,11.88Z"
|
||||
transform="translate(-1.59 -1)"
|
||||
style="fill: #fff;"
|
||||
/>
|
||||
<path
|
||||
d="M14.39,11.88a5.7,5.7,0,1,0-4.78,0A10.41,10.41,0,0,0,1.59,22v1H22.41V22A10.41,10.41,0,0,0,14.39,11.88ZM8.3,6.7A3.71,3.71,0,1,1,12,10.41,3.7,3.7,0,0,1,8.3,6.7ZM3.65,21a8.42,8.42,0,0,1,7.58-7.37L10,16.58l2,2,2-2-1.23-2.95A8.42,8.42,0,0,1,20.35,21Z"
|
||||
transform="translate(-1.59 -1)"
|
||||
d="M58.571 48.146a8.3 8.3 0 1 0-16.571 0"
|
||||
transform="translate(-38.403 -25.592)"
|
||||
style="
|
||||
stroke-miterlimit: 10;
|
||||
stroke: #4c4c4c;
|
||||
stroke-width: 2px;
|
||||
fill: none;
|
||||
"
|
||||
/>
|
||||
<g
|
||||
transform="translate(7.037 3.808)"
|
||||
style="stroke: #4c4c4c; stroke-width: 2px; fill: none;"
|
||||
>
|
||||
<circle cx="4.5" cy="4.5" r="4.5" style="stroke: none;" />
|
||||
<circle cx="4.5" cy="4.5" r="3.5" style="fill: none;" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<mat-form-field>
|
||||
|
@ -47,30 +58,41 @@
|
|||
<div class="ucap-authentication-login-input-field">
|
||||
<!--<span class="input-icon"><i class="mid mdi-lock-outline"></i></span>-->
|
||||
<span class="ucap-authentication-login-input-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22.07">
|
||||
<title>login-lock</title>
|
||||
<path
|
||||
d="M19,10H18V7A6,6,0,0,0,6,7v3H5a3,3,0,0,0-3,3v7a3,3,0,0,0,3,3H19a3,3,0,0,0,3-3V13A3,3,0,0,0,19,10Z"
|
||||
transform="translate(-2 -0.93)"
|
||||
style="fill: #fff;"
|
||||
/>
|
||||
<rect x="9.08" y="12.94" width="1.84" height="3.72" />
|
||||
<path
|
||||
d="M19,22.93H5a3,3,0,0,1-3-3v-7a3,3,0,0,1,3-3H19a3,3,0,0,1,3,3v7A3,3,0,0,1,19,22.93Zm-14-11a1,1,0,0,0-1,1v7a1,1,0,0,0,1,1H19a1,1,0,0,0,1-1v-7a1,1,0,0,0-1-1Z"
|
||||
transform="translate(-2 -0.93)"
|
||||
/>
|
||||
<path
|
||||
d="M17,11.93a1,1,0,0,1-1-1v-4a4,4,0,0,0-8,0v4a1,1,0,0,1-2,0v-4a6,6,0,0,1,12,0v4A1,1,0,0,1,17,11.93Z"
|
||||
transform="translate(-2 -0.93)"
|
||||
/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path d="M0 0H24V24H0z" style="fill: none;" />
|
||||
<g
|
||||
transform="translate(2 9)"
|
||||
style="stroke-width: 2px; stroke: #4c4c4c; fill: #fff;"
|
||||
>
|
||||
<rect width="20" height="14" style="stroke: none;" rx="2" />
|
||||
<rect
|
||||
x="9.58"
|
||||
y="13.36"
|
||||
width="0.84"
|
||||
height="2.72"
|
||||
style="fill: #fff;"
|
||||
width="18"
|
||||
height="12"
|
||||
x="1"
|
||||
y="1"
|
||||
style="fill: none;"
|
||||
rx="1"
|
||||
/>
|
||||
<rect x="9.08" y="12.86" width="1.84" height="3.72" />
|
||||
</g>
|
||||
<g style="fill: #fff;">
|
||||
<path
|
||||
d="M11 8H1V6c0-2.757 2.243-5 5-5s5 2.243 5 5v2z"
|
||||
style="stroke: none;"
|
||||
transform="translate(6 2)"
|
||||
/>
|
||||
<path
|
||||
d="M10 7V6c0-2.205-1.794-4-4-4-2.205 0-4 1.795-4 4v1h8m1.917 2H.083C.028 8.67 0 8.332 0 8V6c0-3.308 2.692-6 6-6s6 2.692 6 6v2c0 .335-.028.67-.083.998V9z"
|
||||
style="stroke: none; fill: #4c4c4c;"
|
||||
transform="translate(6 2)"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
transform="translate(10 14)"
|
||||
style="fill: #4c4c4c; stroke: #4c4c4c;"
|
||||
>
|
||||
<circle cx="2" cy="2" r="2" style="stroke: none;" />
|
||||
<circle cx="2" cy="2" r="1.5" style="fill: none;" />
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<mat-form-field>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-ui-chat",
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.12",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
|
|
@ -107,7 +107,7 @@ export class ChatUiRootModule {}
|
|||
providers: [
|
||||
{
|
||||
provide: UCAP_I18N_NAMESPACE,
|
||||
useValue: ['chat']
|
||||
useValue: ['chat', 'common']
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
>
|
||||
<ucap-chat-message-box-attach-file
|
||||
*ngSwitchCase="FileType.File"
|
||||
[fileInfo]="fileInfo"
|
||||
[expired]="getExpiredFile()"
|
||||
(openViewer)="onClickFileViewer(fileInfo)"
|
||||
(save)="onSave($event)"
|
||||
|
@ -13,6 +14,7 @@
|
|||
|
||||
<ucap-chat-message-box-attach-file
|
||||
*ngSwitchCase="FileType.Sound"
|
||||
[fileInfo]="fileInfo"
|
||||
[expired]="getExpiredFile()"
|
||||
(openViewer)="onClickFileViewer(fileInfo)"
|
||||
(save)="onSave($event)"
|
||||
|
@ -21,6 +23,7 @@
|
|||
|
||||
<ucap-chat-message-box-image
|
||||
*ngSwitchCase="FileType.Image"
|
||||
[fileInfo]="fileInfo"
|
||||
[expired]="getExpiredFile()"
|
||||
(openViewer)="onClickFileViewer(fileInfo)"
|
||||
(save)="onSave($event)"
|
||||
|
@ -28,14 +31,14 @@
|
|||
|
||||
<ucap-chat-message-box-video
|
||||
*ngSwitchCase="FileType.Video"
|
||||
[fileInfo]="fileInfo"
|
||||
[expired]="getExpiredFile()"
|
||||
(openViewer)="onClickFileViewer(fileInfo)"
|
||||
(save)="onSave($event)"
|
||||
></ucap-chat-message-box-video>
|
||||
|
||||
<!--
|
||||
<ucap-chat-message-box-text
|
||||
*ngSwitchDefault
|
||||
[message]="message"
|
||||
></ucap-chat-message-box-text> -->
|
||||
></ucap-chat-message-box-text>
|
||||
</ng-container>
|
||||
|
|
|
@ -1,88 +1,53 @@
|
|||
<div class="ucap-chat-message-box-information">
|
||||
<ng-container [ngSwitch]="message.type">
|
||||
<ng-container *ngSwitchCase="EventType.Join">
|
||||
{{ getI18nForSir(getOwnerForJoinEventJson()) }}이
|
||||
{{ getI18nForSir(getInviterForJoinEventJson()) }}을 초대했습니다.
|
||||
<!-- {{
|
||||
'chat.event.inviteToRoomWith'
|
||||
| translate
|
||||
{{
|
||||
'event.inviteToRoomWith'
|
||||
| ucapI18n
|
||||
: {
|
||||
owner: getI18nForSir(message.sentMessageJson.owner),
|
||||
inviter: getI18nForSir(message.sentMessageJson.inviter)
|
||||
owner: getOwnerForJoinEventJson(),
|
||||
inviter: getInviterForJoinEventJson()
|
||||
}
|
||||
}} -->
|
||||
}}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EventType.Exit">
|
||||
{{ message.sentMessage }}님이 퇴장하셨습니다.
|
||||
<!-- {{
|
||||
'chat.event.exitFromRoomWith'
|
||||
| translate: { exitor: message.sentMessage }
|
||||
}} -->
|
||||
{{ 'event.exitFromRoomWith' | ucapI18n: { exitor: message.sentMessage } }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EventType.ForcedExit">
|
||||
{{ message.exitForcingRequestUserName }}님이 {{ message.sentMessage }}님을
|
||||
퇴장 시키셨습니다.
|
||||
<!-- {{
|
||||
'chat.event.ejectedFromRoomWith'
|
||||
| translate
|
||||
{{
|
||||
'event.ejectedFromRoomWith'
|
||||
| ucapI18n
|
||||
: {
|
||||
requester: message.exitForcingRequestUserName,
|
||||
ejected: message.sentMessage
|
||||
}
|
||||
}} -->
|
||||
}}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EventType.RenameRoom">
|
||||
{{ getRequesterForRenameRoom() }}님이 대화방명을 '{{
|
||||
getRoomNameForRenameRoom()
|
||||
}}'으로 변경하셨습니다.
|
||||
<!-- {{
|
||||
'chat.event.renamedRoomWith'
|
||||
| translate
|
||||
{{
|
||||
'event.renamedRoomWith'
|
||||
| ucapI18n
|
||||
: {
|
||||
requester: message.sentMessageJson.requester,
|
||||
roomName: message.sentMessageJson.roomName
|
||||
requester: getRequesterForRenameRoom(),
|
||||
roomName: getRoomNameForRenameRoom()
|
||||
}
|
||||
}} -->
|
||||
}}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EventType.NotificationForTimerRoom">
|
||||
{{ message.sentMessage }}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EventType.GuideForRoomTimerChanged">
|
||||
{{ senderName }}님이 타이머를 설정하였습니다. ({{
|
||||
getCalcTimerForGuideForRoomTimerChanged()
|
||||
}})
|
||||
<!-- {{
|
||||
'chat.event.setTimerWith'
|
||||
| translate
|
||||
{{
|
||||
'event.setTimerWith'
|
||||
| ucapI18n
|
||||
: {
|
||||
requester: senderName,
|
||||
timer: getCalcTimer(message.sentMessageJson.time * 1000)
|
||||
timer: getCalcTimerForGuideForRoomTimerChanged()
|
||||
}
|
||||
}} -->
|
||||
}}
|
||||
</ng-container>
|
||||
<ng-container *ngSwitchCase="EventType.NotificationForiOSCapture">
|
||||
{{ message.sentMessage }}님이 대화내용을 캡쳐하였습니다.
|
||||
<!-- {{
|
||||
'chat.event.iosCapture'
|
||||
| translate
|
||||
: {
|
||||
requester: message.sentMessage
|
||||
}
|
||||
}} -->
|
||||
{{ 'event.iosCapture' | ucapI18n: { requester: message.sentMessage } }}
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<!-- <div class="ucap-chat-info-event">
|
||||
<div class="info bg-primary-chat">
|
||||
<strong class="text-warn-chat"
|
||||
>김나영, 김준혁,강은정,나정미,박미영,박정은, 박훈, 문영준, 이지은, 이진현,
|
||||
이현준, 임영찬, 임찬우, 정민우,정현욱, 최남진, 최영은, 최진우, 한고은,
|
||||
한정후, 한지민</strong
|
||||
>님이 초대되었습니다.
|
||||
</div>
|
||||
|
||||
<div class="chat-event others bg-accent-chat">
|
||||
자동 삭제 타이머가 10분으로 변경되었습니다.
|
||||
</div>
|
||||
</div> -->
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
GuideForRoomTimerChangedEventJson,
|
||||
JoinEventJson
|
||||
} from '@ucap/protocol-event';
|
||||
import { TranslateService } from '@ucap/ng-ui-organization';
|
||||
import { I18nService } from '@ucap/ng-i18n';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-chat-message-box-information',
|
||||
|
@ -23,105 +23,64 @@ export class InformationComponent implements OnInit {
|
|||
|
||||
EventType = EventType;
|
||||
|
||||
constructor(private translateService: TranslateService) {}
|
||||
constructor(private i18nService: I18nService) {}
|
||||
|
||||
ngOnInit() {}
|
||||
|
||||
getI18nForSir(names: string | string[]) {
|
||||
protected getI18nForSir(names: string | string[]) {
|
||||
if (Array.isArray(names)) {
|
||||
const inviter: string[] = [];
|
||||
|
||||
// names.forEach((userName, idx) => {
|
||||
// inviter.push(
|
||||
// this.translateService.instant('common.messages.sirWith', {
|
||||
// sir: userName
|
||||
// })
|
||||
// );
|
||||
// });
|
||||
names.forEach((userName, idx) => {
|
||||
inviter.push(userName);
|
||||
});
|
||||
|
||||
return inviter.join(',');
|
||||
return names.join(',');
|
||||
} else {
|
||||
// return this.translateService.instant('common.messages.sirWith', {
|
||||
// sir: names
|
||||
// });
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
getOwnerForJoinEventJson() {
|
||||
return (this.message.sentMessageJson as JoinEventJson).owner;
|
||||
return this.getI18nForSir(
|
||||
(this.message.sentMessageJson as JoinEventJson).owner
|
||||
);
|
||||
}
|
||||
getInviterForJoinEventJson() {
|
||||
return (this.message.sentMessageJson as JoinEventJson).inviter;
|
||||
return this.getI18nForSir(
|
||||
(this.message.sentMessageJson as JoinEventJson).inviter
|
||||
);
|
||||
}
|
||||
getCalcTimerForGuideForRoomTimerChanged() {
|
||||
const millisec =
|
||||
Number(
|
||||
(this.message.sentMessageJson as GuideForRoomTimerChangedEventJson).time
|
||||
) * 1000;
|
||||
// const langs = this.translateService.instant([
|
||||
// 'common.units.hourFrom',
|
||||
// 'common.units.minute',
|
||||
// 'common.units.second'
|
||||
// ]);
|
||||
|
||||
// switch (millisec) {
|
||||
// case 5000:
|
||||
// return `5 ${langs['common.units.second']}`;
|
||||
// case 10000:
|
||||
// return `10 ${langs['common.units.second']}`;
|
||||
// case 30000:
|
||||
// return `30 ${langs['common.units.second']}`;
|
||||
// case 60000:
|
||||
// return `1 ${langs['common.units.minute']}`;
|
||||
// case 300000:
|
||||
// return `5 ${langs['common.units.minute']}`;
|
||||
// case 600000:
|
||||
// return `10 ${langs['common.units.minute']}`;
|
||||
// case 1800000:
|
||||
// return `30 ${langs['common.units.minute']}`;
|
||||
// case 3600000:
|
||||
// return `1 ${langs['common.units.hourFrom']}`;
|
||||
// case 21600000:
|
||||
// return `6 ${langs['common.units.hourFrom']}`;
|
||||
// case 43200000:
|
||||
// return `12 ${langs['common.units.hourFrom']}`;
|
||||
// case 86400000:
|
||||
// return `24 ${langs['common.units.hourFrom']}`;
|
||||
// default:
|
||||
// return `0 ${langs['common.units.second']}`;
|
||||
// }
|
||||
|
||||
const langs = ['초', '분', '시간'];
|
||||
const langs = this.i18nService.t([
|
||||
'common.units.hourFrom',
|
||||
'common.units.minute',
|
||||
'common.units.second'
|
||||
]);
|
||||
|
||||
switch (millisec) {
|
||||
case 5000:
|
||||
return `5 ${langs[0]}`;
|
||||
return `5 ${langs['common.units.second']}`;
|
||||
case 10000:
|
||||
return `10 ${langs[0]}`;
|
||||
return `10 ${langs['common.units.second']}`;
|
||||
case 30000:
|
||||
return `30 ${langs[0]}`;
|
||||
return `30 ${langs['common.units.second']}`;
|
||||
case 60000:
|
||||
return `1 ${langs[1]}`;
|
||||
return `1 ${langs['common.units.minute']}`;
|
||||
case 300000:
|
||||
return `5 ${langs[1]}`;
|
||||
return `5 ${langs['common.units.minute']}`;
|
||||
case 600000:
|
||||
return `10 ${langs[1]}`;
|
||||
return `10 ${langs['common.units.minute']}`;
|
||||
case 1800000:
|
||||
return `30 ${langs[1]}`;
|
||||
return `30 ${langs['common.units.minute']}`;
|
||||
case 3600000:
|
||||
return `1 ${langs[2]}`;
|
||||
return `1 ${langs['common.units.hourFrom']}`;
|
||||
case 21600000:
|
||||
return `6 ${langs[2]}`;
|
||||
return `6 ${langs['common.units.hourFrom']}`;
|
||||
case 43200000:
|
||||
return `12 ${langs[2]}`;
|
||||
return `12 ${langs['common.units.hourFrom']}`;
|
||||
case 86400000:
|
||||
return `24 ${langs[2]}`;
|
||||
return `24 ${langs['common.units.hourFrom']}`;
|
||||
default:
|
||||
return `0 ${langs[0]}`;
|
||||
return `0 ${langs['common.units.second']}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<div class="ucap-chat-message-box-sticker">
|
||||
<ul>
|
||||
<li *ngIf="stickerUrl">
|
||||
<img [src]="stickerUrl" />
|
||||
<li>
|
||||
<ng-content
|
||||
select="[ucapUiChatStickerComponent='stickerImage']"
|
||||
></ng-content>
|
||||
<!-- <img [src]="stickerUrl" /> -->
|
||||
</li>
|
||||
<li
|
||||
*ngIf="contents"
|
||||
|
|
|
@ -18,19 +18,23 @@ export class StickerComponent implements OnInit, AfterViewInit {
|
|||
@Input()
|
||||
message: Info<StickerEventJson>;
|
||||
|
||||
@Input()
|
||||
stickerUrl?: string;
|
||||
// @Input()
|
||||
// stickerImageRoot: string;
|
||||
|
||||
@Input()
|
||||
stickerDefaultImagePath: string;
|
||||
// @Input()
|
||||
// stickerDefaultImage: string;
|
||||
|
||||
@Output()
|
||||
openLink = new EventEmitter<string>();
|
||||
|
||||
// stickerUrl?: string;
|
||||
contents: string;
|
||||
constructor(private elementRef: ElementRef) {}
|
||||
|
||||
ngOnInit() {
|
||||
// if (!!this.message.sentMessageJson.file) {
|
||||
// this.stickerUrl = `assets/sticker/sticker_s_${this.message.sentMessageJson.file}.png`;
|
||||
// }
|
||||
if (!!this.message.sentMessageJson?.chat) {
|
||||
this.contents = this.message.sentMessageJson.chat;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-ui-organization",
|
||||
"version": "0.0.55",
|
||||
"version": "0.0.83",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
@import '~@ucap/ng-ui-material/material';
|
||||
|
||||
/// var
|
||||
/// --ucap-organization-profile-list-item-01-size: 70px
|
||||
|
||||
.ucap-organization-profile-list-item-01-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@ -23,6 +26,11 @@
|
|||
flex-grow: 1.07;
|
||||
padding-left: 17px;
|
||||
|
||||
.profile-image {
|
||||
width: var(--ucap-organization-profile-list-item-01-size);
|
||||
height: var(--ucap-organization-profile-list-item-01-size);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -37,7 +45,7 @@
|
|||
height: 22px;
|
||||
|
||||
.user-name {
|
||||
// @include ellipsis-column(1);
|
||||
@include ellipsis-column(1);
|
||||
height: 22px;
|
||||
font: {
|
||||
size: 14px;
|
||||
|
@ -49,7 +57,7 @@
|
|||
}
|
||||
|
||||
.user-grade {
|
||||
// @include ellipsis(1);
|
||||
@include ellipsis(1);
|
||||
align-self: stretch;
|
||||
font: {
|
||||
size: 13px;
|
||||
|
@ -62,7 +70,7 @@
|
|||
}
|
||||
|
||||
.dept-name {
|
||||
// @include ellipsis(1);
|
||||
@include ellipsis(1);
|
||||
font: {
|
||||
size: 12px;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ export class ProfileListItem01Component implements OnInit, OnDestroy {
|
|||
|
||||
@Output()
|
||||
changeCheck = new EventEmitter<{
|
||||
isChecked: boolean;
|
||||
checked: boolean;
|
||||
userInfo: UserInfoSS;
|
||||
}>();
|
||||
|
||||
|
@ -62,9 +62,9 @@ export class ProfileListItem01Component implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
onChangeCheck(isChecked: boolean): void {
|
||||
onChangeCheck(checked: boolean): void {
|
||||
this.changeCheck.emit({
|
||||
isChecked,
|
||||
checked,
|
||||
userInfo: this.userInfo
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<div class="ucap-organization-profile-list-item-01-container">
|
||||
<div class="user-profile-info">
|
||||
<div class="profile-image">
|
||||
<ng-content
|
||||
select="[ucapOrganizationProfileListItem01='profileImage']"
|
||||
></ng-content>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<div class="user-n-g">
|
||||
<div class="user-name">
|
||||
{{ userInfo | ucapOrganizationTranslate: 'name' }}
|
||||
</div>
|
||||
<div class="user-grade">
|
||||
{{ userInfo | ucapOrganizationTranslate: 'grade' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="dept-name">
|
||||
{{ userInfo | ucapOrganizationTranslate: 'deptName' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<ng-content
|
||||
class="user-info-content"
|
||||
select="[appProfileListItemUserInfo='info']"
|
||||
></ng-content>
|
||||
</div>
|
||||
<div class="user-action">
|
||||
<ng-content
|
||||
class="user-action-content"
|
||||
select="[appProfileListItemUserAction='action']"
|
||||
></ng-content>
|
||||
</div>
|
||||
<div *ngIf="checkable">
|
||||
<mat-checkbox
|
||||
#checkbox
|
||||
[checked]="checked"
|
||||
(change)="onChangeCheck(checkbox.checked)"
|
||||
></mat-checkbox>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,113 @@
|
|||
@import '~@ucap/ng-ui-material/material';
|
||||
|
||||
/// var
|
||||
/// --ucap-organization-profile-list-item-01-size: 70px
|
||||
|
||||
.ucap-organization-profile-list-item-01-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
padding: 0;
|
||||
height: 70px;
|
||||
align-items: center;
|
||||
&.line-top {
|
||||
border-top: 1px solid #cccccc;
|
||||
}
|
||||
&.line-bottom {
|
||||
border-bottom: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
.user-profile-info {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 1.07;
|
||||
padding-left: 17px;
|
||||
|
||||
.profile-image {
|
||||
width: var(--ucap-organization-profile-list-item-01-size);
|
||||
height: var(--ucap-organization-profile-list-item-01-size);
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
padding-left: 16px;
|
||||
|
||||
.user-n-g {
|
||||
display: flex;
|
||||
flex-flow: row-reverse nowrap;
|
||||
align-items: flex-end;
|
||||
height: 22px;
|
||||
|
||||
.user-name {
|
||||
@include ellipsis-column(1);
|
||||
height: 22px;
|
||||
font: {
|
||||
size: 14px;
|
||||
weight: 600;
|
||||
}
|
||||
color: #212121;
|
||||
order: 1;
|
||||
-ms-flex-order: 1;
|
||||
}
|
||||
|
||||
.user-grade {
|
||||
@include ellipsis(1);
|
||||
align-self: stretch;
|
||||
font: {
|
||||
size: 13px;
|
||||
}
|
||||
color: #707070;
|
||||
margin-left: 4px;
|
||||
order: 0;
|
||||
-ms-flex-order: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.dept-name {
|
||||
@include ellipsis(1);
|
||||
font: {
|
||||
size: 12px;
|
||||
}
|
||||
color: #666666;
|
||||
line-height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.company-info {
|
||||
flex-grow: 1.4;
|
||||
padding-left: 16px;
|
||||
position: relative;
|
||||
font: {
|
||||
size: 14px;
|
||||
}
|
||||
color: #333333;
|
||||
&:before {
|
||||
content: '';
|
||||
width: 1px;
|
||||
height: 36px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% - 18px);
|
||||
border-left: 1px solid #cccccc;
|
||||
}
|
||||
}
|
||||
.contact {
|
||||
flex-grow: 1;
|
||||
font: {
|
||||
size: 14px;
|
||||
}
|
||||
color: #333333;
|
||||
}
|
||||
.chk-box {
|
||||
flex: 0 0 56px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { ProfileListItem01Component } from './profile-list-item-01.component';
|
||||
|
||||
describe('ucap::ui-organization::ProfileListItem01Component', () => {
|
||||
let component: ProfileListItem01Component;
|
||||
let fixture: ComponentFixture<ProfileListItem01Component>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ProfileListItem01Component]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ProfileListItem01Component);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,68 @@
|
|||
import { moduleMetadata } from '@storybook/angular';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { linkTo } from '@storybook/addon-links';
|
||||
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { UserInfoF } from '@ucap/protocol-query';
|
||||
|
||||
import { OrganizationUiModule } from '../organization-ui.module';
|
||||
|
||||
import { ProfileListItem02Component } from './profile-list-item-02.component';
|
||||
import { RoleCode } from '@ucap/protocol-authentication';
|
||||
import { CallMode, EmployeeType } from '@ucap/core';
|
||||
|
||||
const userInfo: UserInfoF = {
|
||||
seq: 1234,
|
||||
name: '사용자1',
|
||||
profileImageFile: '사진파일',
|
||||
grade: '직급',
|
||||
intro: '업무소개',
|
||||
companyCode: '기관코드',
|
||||
hpNumber: '핸드폰번호',
|
||||
lineNumber: '내선번호',
|
||||
email: '이메일',
|
||||
isMobile: true,
|
||||
isBuddy: true,
|
||||
isFavorit: true,
|
||||
/** */
|
||||
deptName: '부서명',
|
||||
/** */
|
||||
order: '조회순서',
|
||||
|
||||
isActive: true,
|
||||
roleCd: RoleCode.Self,
|
||||
employeeNum: 'A34567',
|
||||
madn: 'MADN',
|
||||
hardSadn: 'HARDPHONE_SADN',
|
||||
fmcSadn: 'FMC_SADN',
|
||||
nickName: 'nickName',
|
||||
nameEn: '사용자명(영어)',
|
||||
nameCn: '사용자명(중국어)',
|
||||
gradeEn: '직급(영어)',
|
||||
gradeCn: '직급(중국어)',
|
||||
deptNameEn: '부서명(영어)',
|
||||
deptNameCn: '부서명(중국어)',
|
||||
isPrivacyAgree: true,
|
||||
isValidLogin: true,
|
||||
employeeType: EmployeeType.Regular
|
||||
};
|
||||
|
||||
export default {
|
||||
title: 'ui-organization::ProfileListItem02Component',
|
||||
component: ProfileListItem02Component,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [BrowserModule, BrowserAnimationsModule, OrganizationUiModule],
|
||||
providers: []
|
||||
})
|
||||
]
|
||||
};
|
||||
|
||||
export const Default = () => ({
|
||||
component: ProfileListItem02Component,
|
||||
props: {
|
||||
userInfo
|
||||
}
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
@import '~@ucap/ng-ui-material/material';
|
||||
|
||||
@mixin ucap-organization-profile-list-item-01-theme($theme) {
|
||||
$is-dark-theme: map-get($theme, is-dark);
|
||||
$primary: map-get($theme, primary);
|
||||
$accent: map-get($theme, accent);
|
||||
$warn: map-get($theme, warn);
|
||||
$background: map-get($theme, background);
|
||||
$foreground: map-get($theme, foreground);
|
||||
|
||||
.ucap-organization-profile-list-item-01-container {
|
||||
border-color: mat-color($foreground, secondary-text);
|
||||
}
|
||||
}
|
||||
|
||||
@mixin ucap-organization-profile-list-item-01-typography($config) {
|
||||
.ucap-organization-profile-list-item-01-container {
|
||||
.searchword-container {
|
||||
.form-field-input-searchword {
|
||||
font-family: mat-font-family($config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
import { Subject } from 'rxjs';
|
||||
|
||||
import {
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
|
||||
import { UserInfoF } from '@ucap/protocol-query';
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-organization-profile-list-item-02',
|
||||
templateUrl: './profile-list-item-02.component.html',
|
||||
styleUrls: ['./profile-list-item-02.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProfileListItem02Component implements OnInit, OnDestroy {
|
||||
@Input()
|
||||
userInfo: UserInfoF;
|
||||
|
||||
@Input()
|
||||
authCall = false;
|
||||
|
||||
@Input()
|
||||
checkable = true;
|
||||
|
||||
@Input()
|
||||
checked = false;
|
||||
|
||||
@Output()
|
||||
changeCheck = new EventEmitter<{
|
||||
checked: boolean;
|
||||
userInfo: UserInfoF;
|
||||
}>();
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ngOnDestroySubject = new Subject();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (!!this.ngOnDestroySubject) {
|
||||
this.ngOnDestroySubject.next();
|
||||
this.ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
onChangeCheck(checked: boolean): void {
|
||||
this.changeCheck.emit({
|
||||
checked,
|
||||
userInfo: this.userInfo
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
<div class="ucap-organization-profile-list-container">
|
||||
<div class="ucap-organization-profile-list-container" fxFlexFill>
|
||||
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||
<ng-container *cdkVirtualFor="let userInfo of userInfos">
|
||||
<ng-container
|
||||
|
|
|
@ -1,27 +1,31 @@
|
|||
<div class="ucap-organization-profile-selection-01-container">
|
||||
<div class="ucap-organization-profile-selection-01-body">
|
||||
<div class="ucap-organization-profile-selection-01-container" fxLayout="column">
|
||||
<div
|
||||
*ngIf="!!headerTemplate"
|
||||
class="ucap-organization-profile-selection-01-header"
|
||||
fxFlex="0 0 48px"
|
||||
>
|
||||
<ng-container [ngTemplateOutlet]="headerTemplate"></ng-container>
|
||||
</div>
|
||||
<div class="ucap-organization-profile-selection-01-body" fxFlex="1 1 auto">
|
||||
<mat-chip-list aria-label="User selection" perfectScrollbar>
|
||||
<mat-chip
|
||||
*ngFor="let profileSelection of _profileSelectionList"
|
||||
[color]="profileSelection.color"
|
||||
[selectable]="profileSelection.selectable"
|
||||
[selected]="profileSelection.selected"
|
||||
[removable]="profileSelection.removable"
|
||||
(selectionChange)="
|
||||
_onSelectionChange($event, profileSelection.userInfo)
|
||||
"
|
||||
(removed)="_onRemoved($event, profileSelection.userInfo)"
|
||||
*ngFor="let userInfo of _userInfoList"
|
||||
[color]="_colorFor(userInfo)"
|
||||
[removable]="_removableFor(userInfo)"
|
||||
(removed)="_onRemoved($event, userInfo)"
|
||||
>
|
||||
{{ profileSelection.userInfo.name }}
|
||||
<mat-icon matChipRemove *ngIf="profileSelection.removable">
|
||||
{{ userInfo.name }}
|
||||
<mat-icon matChipRemove *ngIf="removableFor(userInfo)">
|
||||
clear
|
||||
</mat-icon>
|
||||
</mat-chip>
|
||||
</mat-chip-list>
|
||||
</div>
|
||||
<div class="ucap-organization-profile-selection-01-actions">
|
||||
<ng-content
|
||||
select="[ucapOrganizationSelectedProfileList01='action']"
|
||||
></ng-content>
|
||||
<div
|
||||
*ngIf="!!actionsTemplate"
|
||||
class="ucap-organization-profile-selection-01-actions"
|
||||
fxFlex="0 0 60px"
|
||||
>
|
||||
<ng-container [ngTemplateOutlet]="actionsTemplate"></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,4 +3,9 @@
|
|||
.ucap-organization-profile-selection-01-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.ucap-organization-profile-selection-01-body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,11 +7,15 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Output,
|
||||
EventEmitter
|
||||
EventEmitter,
|
||||
Input,
|
||||
Directive,
|
||||
ContentChild,
|
||||
TemplateRef
|
||||
} from '@angular/core';
|
||||
|
||||
import { UserInfo } from '@ucap/protocol-sync';
|
||||
import { MatChipSelectionChange, MatChipEvent } from '@angular/material/chips';
|
||||
import { MatChipEvent } from '@angular/material/chips';
|
||||
|
||||
export interface ProfileSelection {
|
||||
userInfo: UserInfo;
|
||||
|
@ -21,6 +25,16 @@ export interface ProfileSelection {
|
|||
removable?: boolean;
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[ucapOrganizationProfileSelection01Header]'
|
||||
})
|
||||
export class ProfileSelection01HeaderDirective {}
|
||||
|
||||
@Directive({
|
||||
selector: '[ucapOrganizationProfileSelection01Actions]'
|
||||
})
|
||||
export class ProfileSelection01ActionsDirective {}
|
||||
|
||||
@Component({
|
||||
selector: 'ucap-organization-profile-selection-01',
|
||||
templateUrl: './profile-selection-01.component.html',
|
||||
|
@ -28,17 +42,34 @@ export interface ProfileSelection {
|
|||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ProfileSelection01Component implements OnInit, OnDestroy {
|
||||
@Output()
|
||||
selectionChange: EventEmitter<{
|
||||
selected: boolean;
|
||||
userInfo: UserInfo;
|
||||
}> = new EventEmitter();
|
||||
@Input()
|
||||
set userInfoList(value: UserInfo[]) {
|
||||
console.log('userInfoList', value);
|
||||
this._userInfoList = value;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_userInfoList: UserInfo[];
|
||||
|
||||
@Input()
|
||||
removableFor: (userInfo: UserInfo) => boolean;
|
||||
|
||||
@Input()
|
||||
colorFor: (userInfo: UserInfo) => string;
|
||||
|
||||
@Output()
|
||||
removed: EventEmitter<UserInfo> = new EventEmitter();
|
||||
|
||||
// tslint:disable-next-line: variable-name
|
||||
_profileSelectionList: ProfileSelection[] = [];
|
||||
@ContentChild(ProfileSelection01HeaderDirective, {
|
||||
read: TemplateRef,
|
||||
static: false
|
||||
})
|
||||
headerTemplate: TemplateRef<ProfileSelection01HeaderDirective>;
|
||||
|
||||
@ContentChild(ProfileSelection01ActionsDirective, {
|
||||
read: TemplateRef,
|
||||
static: false
|
||||
})
|
||||
actionsTemplate: TemplateRef<ProfileSelection01ActionsDirective>;
|
||||
|
||||
private ngOnDestroySubject: Subject<void>;
|
||||
|
||||
|
@ -55,54 +86,21 @@ export class ProfileSelection01Component implements OnInit, OnDestroy {
|
|||
}
|
||||
}
|
||||
|
||||
addProfileSelection(profileSelectionList: ProfileSelection[]): void {
|
||||
if (!profileSelectionList || 0 === profileSelectionList.length) {
|
||||
return;
|
||||
_colorFor(userInfo: UserInfo): string {
|
||||
if (!this.colorFor) {
|
||||
return undefined;
|
||||
}
|
||||
return this.colorFor(userInfo);
|
||||
}
|
||||
|
||||
profileSelectionList.forEach((v) => {
|
||||
const i = this.indexOfProfileSelection(v);
|
||||
if (-1 === i) {
|
||||
this._profileSelectionList.push(v);
|
||||
_removableFor(userInfo: UserInfo): boolean {
|
||||
if (!this.removableFor) {
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeProfileSelection(profileSelectionList: ProfileSelection[]): void {
|
||||
if (!profileSelectionList || 0 === profileSelectionList.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
profileSelectionList.forEach((v) => {
|
||||
const i = this.indexOfProfileSelection(v);
|
||||
if (-1 < i) {
|
||||
this._profileSelectionList.splice(i, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_onSelectionChange(event: MatChipSelectionChange, userInfo: UserInfo) {
|
||||
this.selectionChange.emit({
|
||||
selected: event.selected,
|
||||
userInfo
|
||||
});
|
||||
return this.removableFor(userInfo);
|
||||
}
|
||||
|
||||
_onRemoved(event: MatChipEvent, userInfo: UserInfo) {
|
||||
this.removed.emit(userInfo);
|
||||
}
|
||||
|
||||
private indexOfProfileSelection(profileSelection: ProfileSelection): number {
|
||||
if (
|
||||
!this._profileSelectionList ||
|
||||
0 === this._profileSelectionList.length ||
|
||||
!profileSelection
|
||||
) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this._profileSelectionList.findIndex(
|
||||
(v) => v.userInfo.seq === profileSelection.userInfo.seq
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
matInput
|
||||
#inputSearchWord
|
||||
[placeholder]="placeholder"
|
||||
[value]="!!defaultSearchWord ? defaultSearchWord : ''"
|
||||
(keyup.enter)="onKeyupEnter($event)"
|
||||
/>
|
||||
<button
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
display: flex;
|
||||
|
||||
border: {
|
||||
width: var(--ucap-organization-search-for-tenant-border-width, 1px);
|
||||
color: var(--ucap-organization-search-for-tenant-border-color, #e42f66);
|
||||
width: var(--ucap-organization-search-for-tenant-border-width);
|
||||
color: var(--ucap-organization-search-for-tenant-border-color);
|
||||
style: solid;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ export default {
|
|||
export const Default = () => ({
|
||||
component: SearchForTenantComponent,
|
||||
props: {
|
||||
text: 'Hello SearchForTenantComponent',
|
||||
companyList: [
|
||||
{
|
||||
companyCode: 'CD1',
|
||||
|
|
|
@ -29,6 +29,9 @@ export class SearchForTenantComponent implements OnInit, OnDestroy {
|
|||
@Input()
|
||||
defaultCompany: string;
|
||||
|
||||
@Input()
|
||||
defaultSearchWord: string;
|
||||
|
||||
@Input()
|
||||
placeholder: string;
|
||||
|
||||
|
|
|
@ -4,21 +4,25 @@
|
|||
*cdkVirtualFor="let node of dataSource.expandedData$"
|
||||
></ng-container>
|
||||
|
||||
<mat-tree #treeList [dataSource]="dataSource" [treeControl]="treeControl">
|
||||
<mat-tree
|
||||
#treeList
|
||||
[dataSource]="dataSource"
|
||||
[treeControl]="treeControl"
|
||||
[trackBy]="trackBy"
|
||||
>
|
||||
<mat-tree-node
|
||||
*matTreeNodeDef="let node"
|
||||
matTreeNodePadding
|
||||
matTreeNodePaddingIndent="20"
|
||||
class="tree-no-child"
|
||||
(click)="onClickNode($event, node)"
|
||||
>
|
||||
<li
|
||||
(click)="onClickNode(node)"
|
||||
matRipple
|
||||
[ngClass]="
|
||||
currentDeptSeq === node?.data?.deptInfo?.seq ? 'current' : ''
|
||||
"
|
||||
>
|
||||
<div class="tree-node-body">
|
||||
<div matRipple class="tree-node-body">
|
||||
{{ node?.data?.deptInfo | ucapOrganizationTranslate: 'name' }}
|
||||
</div>
|
||||
</li>
|
||||
|
@ -29,9 +33,11 @@
|
|||
matTreeNodePadding
|
||||
matTreeNodePaddingIndent="20"
|
||||
class="tree-has-child"
|
||||
(click)="onClickNode($event, node)"
|
||||
>
|
||||
<li (click)="onClickNode(node)" matRipple>
|
||||
<li>
|
||||
<div
|
||||
matRipple
|
||||
class="tree-node-body"
|
||||
[ngClass]="
|
||||
currentDeptSeq === node?.data?.deptInfo?.seq ? 'current' : ''
|
||||
|
|
|
@ -30,6 +30,7 @@ import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
|||
|
||||
export interface OrganizationNode {
|
||||
deptInfo: DeptInfo;
|
||||
ancestorSeq?: number;
|
||||
children?: OrganizationNode[];
|
||||
}
|
||||
|
||||
|
@ -94,7 +95,9 @@ export class TreeComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
if (nodeMap.has(deptInfo.parentSeq)) {
|
||||
nodeMap.get(deptInfo.parentSeq).children.push(node);
|
||||
const ancestor = nodeMap.get(deptInfo.parentSeq);
|
||||
node.ancestorSeq = ancestor.deptInfo.seq;
|
||||
ancestor.children.push(node);
|
||||
} else {
|
||||
remainChildNodeList.push(node);
|
||||
}
|
||||
|
@ -110,28 +113,32 @@ export class TreeComponent implements OnInit, OnDestroy {
|
|||
rootNodeList = [...rootNodeList[0].children];
|
||||
}
|
||||
|
||||
this._deptInfoList = deptInfoList;
|
||||
|
||||
remainChildNodeList.forEach((node) => {
|
||||
if (nodeMap.has(node.deptInfo.parentSeq)) {
|
||||
nodeMap.get(node.deptInfo.parentSeq).children.push(node);
|
||||
const ancestor = nodeMap.get(node.deptInfo.parentSeq);
|
||||
if (!!ancestor) {
|
||||
node.ancestorSeq = ancestor.deptInfo.seq;
|
||||
ancestor.children.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
this.dataSource.data = rootNodeList;
|
||||
|
||||
if (!!data.expanded) {
|
||||
data.expanded.forEach((s) => {
|
||||
this.expand(s);
|
||||
});
|
||||
this.expand(...data.expanded);
|
||||
}
|
||||
|
||||
this.changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
currentDeptSeq: number;
|
||||
@Output() currentDeptSeqChange: EventEmitter<number> = new EventEmitter();
|
||||
@Input() set currentDeptSeq(deptInfoSeq: number) {
|
||||
this._currentDeptSeq = deptInfoSeq;
|
||||
}
|
||||
get currentDeptSeq() {
|
||||
return this._currentDeptSeq;
|
||||
}
|
||||
// tslint:disable-next-line: variable-name
|
||||
_deptInfoList: DeptInfo[];
|
||||
_currentDeptSeq: number;
|
||||
|
||||
@Output()
|
||||
clickNode = new EventEmitter<DeptInfo>();
|
||||
|
@ -201,19 +208,32 @@ export class TreeComponent implements OnInit, OnDestroy {
|
|||
|
||||
hasChild = (_: number, node: FlatNode) => node.expandable;
|
||||
|
||||
onClickNode(node: FlatNode) {
|
||||
trackBy = (_: number, node: FlatNode) => node.data.deptInfo.seq;
|
||||
|
||||
onClickNode(event: Event, node: FlatNode) {
|
||||
event.stopPropagation();
|
||||
|
||||
this.clickNode.emit(node.data.deptInfo);
|
||||
}
|
||||
|
||||
expand(deptSeq: number) {
|
||||
if (!this.treeControl.dataNodes) {
|
||||
expand(...deptSeq: number[]) {
|
||||
if (!this.treeControl.dataNodes || !deptSeq || 0 === deptSeq.length) {
|
||||
return;
|
||||
}
|
||||
const flatNodes: FlatNode[] = [];
|
||||
|
||||
deptSeq.forEach((s) => {
|
||||
const node = this.treeControl.dataNodes.find(
|
||||
(n) => n.data.deptInfo.seq === deptSeq
|
||||
(n) => n.data.deptInfo.seq === s
|
||||
);
|
||||
if (!!node) {
|
||||
this.treeControl.expand(node);
|
||||
flatNodes.push(node);
|
||||
this.selectHierarchy(flatNodes, node);
|
||||
}
|
||||
});
|
||||
|
||||
if (0 < flatNodes.length) {
|
||||
this.treeControl.expansionModel.select(...flatNodes);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,4 +256,30 @@ export class TreeComponent implements OnInit, OnDestroy {
|
|||
collapseAll() {
|
||||
this.treeControl.collapseAll();
|
||||
}
|
||||
|
||||
private selectHierarchy(flatNodes: FlatNode[], flatNode: FlatNode): void {
|
||||
// tslint:disable-next-line: variable-name
|
||||
let _flatNode = flatNode;
|
||||
while (true) {
|
||||
if (!_flatNode) {
|
||||
return;
|
||||
}
|
||||
const ancestorSeq = _flatNode.data.ancestorSeq;
|
||||
if (!ancestorSeq) {
|
||||
return;
|
||||
}
|
||||
_flatNode = this.treeControl.dataNodes.find(
|
||||
(n) => n.data.deptInfo.seq === ancestorSeq
|
||||
);
|
||||
if (!_flatNode) {
|
||||
return;
|
||||
}
|
||||
const i = flatNodes.findIndex(
|
||||
(n) => n.data.deptInfo.seq === _flatNode.data.deptInfo.seq
|
||||
);
|
||||
if (-1 === i) {
|
||||
flatNodes.push(_flatNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,12 +25,17 @@ import { _MODULE_CONFIG } from './config/token';
|
|||
|
||||
import { Profile01Component } from './components/profile-01.component';
|
||||
import { ProfileListItem01Component } from './components/profile-list-item-01.component';
|
||||
import { ProfileListItem02Component } from './components/profile-list-item-02.component';
|
||||
import { ProfileImage01Component } from './components/profile-image-01.component';
|
||||
import {
|
||||
ProfileListComponent,
|
||||
ProfileListNodeDirective
|
||||
} from './components/profile-list.component';
|
||||
import { ProfileSelection01Component } from './components/profile-selection-01.component';
|
||||
import {
|
||||
ProfileSelection01Component,
|
||||
ProfileSelection01HeaderDirective,
|
||||
ProfileSelection01ActionsDirective
|
||||
} from './components/profile-selection-01.component';
|
||||
import { SearchForTenantComponent } from './components/search-for-tenant.component';
|
||||
import { TreeComponent } from './components/tree.component';
|
||||
|
||||
|
@ -42,6 +47,7 @@ import { UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n';
|
|||
const COMPONENTS = [
|
||||
Profile01Component,
|
||||
ProfileListItem01Component,
|
||||
ProfileListItem02Component,
|
||||
ProfileListComponent,
|
||||
ProfileImage01Component,
|
||||
ProfileSelection01Component,
|
||||
|
@ -50,7 +56,11 @@ const COMPONENTS = [
|
|||
];
|
||||
const DIALOGS = [];
|
||||
const PIPES = [TranslatePipe];
|
||||
const DIRECTIVES = [ProfileListNodeDirective];
|
||||
const DIRECTIVES = [
|
||||
ProfileListNodeDirective,
|
||||
ProfileSelection01HeaderDirective,
|
||||
ProfileSelection01ActionsDirective
|
||||
];
|
||||
const SERVICES = [TranslateService];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
@ -163,7 +163,7 @@ export class PresenceUtil {
|
|||
return statusBulkInfo.phoneStatus;
|
||||
case PresenceType.CONFERENCE:
|
||||
return statusBulkInfo.conferenceStatus;
|
||||
case PresenceType.IMESSENER:
|
||||
case PresenceType.IMESSENGER:
|
||||
return statusBulkInfo.imessengerStatus;
|
||||
default:
|
||||
return undefined;
|
||||
|
|
Loading…
Reference in New Issue
Block a user