0517 sync
This commit is contained in:
parent
aee13b8a4d
commit
b4ef37624c
|
@ -1,3 +1,5 @@
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
stories: ['../projects/**/*.stories.ts'],
|
stories: ['../projects/**/*.stories.ts'],
|
||||||
addons: [
|
addons: [
|
||||||
|
@ -5,5 +7,19 @@ module.exports = {
|
||||||
'@storybook/addon-links',
|
'@storybook/addon-links',
|
||||||
'@storybook/addon-notes',
|
'@storybook/addon-notes',
|
||||||
'@storybook/addon-knobs'
|
'@storybook/addon-knobs'
|
||||||
]
|
],
|
||||||
|
webpackFinal: async (config) => {
|
||||||
|
for (const rule of config.module.rules) {
|
||||||
|
if (-1 < String(rule.test).indexOf('scss')) {
|
||||||
|
rule.use.push({
|
||||||
|
loader: 'sass-resources-loader',
|
||||||
|
options: {
|
||||||
|
resources: [path.join(__dirname, 'styles.scss')]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
10
.storybook/styles.scss
Normal file
10
.storybook/styles.scss
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
:root {
|
||||||
|
--ucap-screen-max-xs: 575;
|
||||||
|
--ucap-screen-min-sm: 576;
|
||||||
|
--ucap-screen-max-sm: 767;
|
||||||
|
--ucap-screen-min-md: 768;
|
||||||
|
--ucap-screen-max-md: 991;
|
||||||
|
--ucap-screen-min-lg: 992;
|
||||||
|
--ucap-screen-max-lg: 1199;
|
||||||
|
--ucap-screen-min-xl: 1200;
|
||||||
|
}
|
43
angular.json
43
angular.json
|
@ -1436,6 +1436,49 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ui-chat": {
|
||||||
|
"projectType": "library",
|
||||||
|
"schematics": {
|
||||||
|
"@schematics/angular:component": {
|
||||||
|
"style": "scss"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "projects/ui-chat",
|
||||||
|
"sourceRoot": "projects/ui-chat/src",
|
||||||
|
"prefix": "ucap-chat",
|
||||||
|
"architect": {
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-ng-packagr:build",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": "projects/ui-chat/tsconfig.lib.json",
|
||||||
|
"project": "projects/ui-chat/ng-package.json"
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"production": {
|
||||||
|
"tsConfig": "projects/ui-chat/tsconfig.lib.prod.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"builder": "@angular-devkit/build-angular:karma",
|
||||||
|
"options": {
|
||||||
|
"main": "projects/ui-chat/src/test.ts",
|
||||||
|
"tsConfig": "projects/ui-chat/tsconfig.spec.json",
|
||||||
|
"karmaConfig": "projects/ui-chat/karma.conf.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"builder": "@angular-devkit/build-angular:tslint",
|
||||||
|
"options": {
|
||||||
|
"tsConfig": [
|
||||||
|
"projects/ui-chat/tsconfig.lib.json",
|
||||||
|
"projects/ui-chat/tsconfig.spec.json"
|
||||||
|
],
|
||||||
|
"exclude": ["**/node_modules/**"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"ui-skin-default": {
|
"ui-skin-default": {
|
||||||
"projectType": "library",
|
"projectType": "library",
|
||||||
"schematics": {
|
"schematics": {
|
||||||
|
|
1931
package-lock.json
generated
1931
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
|
@ -45,11 +45,12 @@
|
||||||
"build:store-chat": "node ./scripts/build.js store-chat",
|
"build:store-chat": "node ./scripts/build.js store-chat",
|
||||||
"build:store-group": "node ./scripts/build.js store-group",
|
"build:store-group": "node ./scripts/build.js store-group",
|
||||||
"build:store-organization": "node ./scripts/build.js store-organization",
|
"build:store-organization": "node ./scripts/build.js store-organization",
|
||||||
"build:ui:all": "npm-run-all -s build:ui build:ui-organization build:ui-authentication build:ui-group",
|
"build:ui:all": "npm-run-all -s build:ui build:ui-organization build:ui-authentication build:ui-group build:ui-chat",
|
||||||
"build:ui": "node ./scripts/build.js ui",
|
"build:ui": "node ./scripts/build.js ui",
|
||||||
"build:ui-organization": "node ./scripts/build.js ui-organization",
|
"build:ui-organization": "node ./scripts/build.js ui-organization",
|
||||||
"build:ui-authentication": "node ./scripts/build.js ui-authentication",
|
"build:ui-authentication": "node ./scripts/build.js ui-authentication",
|
||||||
"build:ui-group": "node ./scripts/build.js ui-group",
|
"build:ui-group": "node ./scripts/build.js ui-group",
|
||||||
|
"build:ui-chat": "node ./scripts/build.js ui-chat",
|
||||||
"build:ui-skin:all": "npm-run-all -s build:ui-skin-default",
|
"build:ui-skin:all": "npm-run-all -s build:ui-skin-default",
|
||||||
"build:ui-skin-default": "node ./scripts/build.js ui-skin-default useScssBundle",
|
"build:ui-skin-default": "node ./scripts/build.js ui-skin-default useScssBundle",
|
||||||
"publish:all": "npm-run-all -s publish:logger publish:core publish:util:all publish:api:all publish:protocol:all publish:native:all publish:store:all publish:ui:all publish:ui-skin:all",
|
"publish:all": "npm-run-all -s publish:logger publish:core publish:util:all publish:api:all publish:protocol:all publish:native:all publish:store:all publish:ui:all publish:ui-skin:all",
|
||||||
|
@ -91,11 +92,12 @@
|
||||||
"publish:store-chat": "cd ./dist/store-chat && npm publish",
|
"publish:store-chat": "cd ./dist/store-chat && npm publish",
|
||||||
"publish:store-group": "cd ./dist/store-group && npm publish",
|
"publish:store-group": "cd ./dist/store-group && npm publish",
|
||||||
"publish:store-organization": "cd ./dist/store-organization && npm publish",
|
"publish:store-organization": "cd ./dist/store-organization && npm publish",
|
||||||
"publish:ui:all": "npm-run-all -s publish:ui publish:ui-organization publish:ui-authentication publish:ui-group",
|
"publish:ui:all": "npm-run-all -s publish:ui publish:ui-organization publish:ui-authentication publish:ui-group publish:ui-chat",
|
||||||
"publish:ui": "cd ./dist/ui && npm publish",
|
"publish:ui": "cd ./dist/ui && npm publish",
|
||||||
"publish:ui-organization": "cd ./dist/ui-organization && npm publish",
|
"publish:ui-organization": "cd ./dist/ui-organization && npm publish",
|
||||||
"publish:ui-authentication": "cd ./dist/ui-authentication && npm publish",
|
"publish:ui-authentication": "cd ./dist/ui-authentication && npm publish",
|
||||||
"publish:ui-group": "cd ./dist/ui-group && npm publish",
|
"publish:ui-group": "cd ./dist/ui-group && npm publish",
|
||||||
|
"publish:ui-chat": "cd ./dist/ui-chat && npm publish",
|
||||||
"publish:ui-skin:all": "npm-run-all -s publish:ui-skin-default",
|
"publish:ui-skin:all": "npm-run-all -s publish:ui-skin-default",
|
||||||
"publish:ui-skin-default": "cd ./dist/ui-skin-default && npm publish",
|
"publish:ui-skin-default": "cd ./dist/ui-skin-default && npm publish",
|
||||||
"test": "ng test",
|
"test": "ng test",
|
||||||
|
@ -179,13 +181,14 @@
|
||||||
"@ucap/ng-protocol-sync": "file:pack/ucap-ng-protocol-sync-0.0.3.tgz",
|
"@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-protocol-umg": "file:pack/ucap-ng-protocol-umg-0.0.3.tgz",
|
||||||
"@ucap/ng-store-authentication": "file:pack/ucap-ng-store-authentication-0.0.10.tgz",
|
"@ucap/ng-store-authentication": "file:pack/ucap-ng-store-authentication-0.0.10.tgz",
|
||||||
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.5.tgz",
|
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.8.tgz",
|
||||||
"@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.6.tgz",
|
"@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.9.tgz",
|
||||||
"@ucap/ng-store-organization": "file:pack/ucap-ng-store-organization-0.0.4.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.5.tgz",
|
"@ucap/ng-ui": "file:pack/ucap-ng-ui-0.0.11.tgz",
|
||||||
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.19.tgz",
|
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.20.tgz",
|
||||||
"@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.27.tgz",
|
"@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.3.tgz",
|
||||||
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.12.tgz",
|
"@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.30.tgz",
|
||||||
|
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.27.tgz",
|
||||||
"@ucap/ng-ui-skin-default": "file:pack/ucap-ng-ui-skin-default-0.0.1.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-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",
|
"@ucap/ng-web-storage": "file:pack/ucap-ng-web-storage-0.0.3.tgz",
|
||||||
|
@ -206,6 +209,7 @@
|
||||||
"@ucap/protocol-status": "~0.0.5",
|
"@ucap/protocol-status": "~0.0.5",
|
||||||
"@ucap/protocol-sync": "~0.0.4",
|
"@ucap/protocol-sync": "~0.0.4",
|
||||||
"@ucap/protocol-umg": "~0.0.5",
|
"@ucap/protocol-umg": "~0.0.5",
|
||||||
|
"@ucap/ui-scss": "~0.0.3",
|
||||||
"@ucap/web-socket": "~0.0.5",
|
"@ucap/web-socket": "~0.0.5",
|
||||||
"@ucap/web-storage": "~0.0.5",
|
"@ucap/web-storage": "~0.0.5",
|
||||||
"autolinker": "^3.13.0",
|
"autolinker": "^3.13.0",
|
||||||
|
@ -236,6 +240,7 @@
|
||||||
"queueing-subject": "^0.3.4",
|
"queueing-subject": "^0.3.4",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"rxjs": "~6.5.4",
|
"rxjs": "~6.5.4",
|
||||||
|
"sass-resources-loader": "^2.0.3",
|
||||||
"scss-bundle": "^3.1.1",
|
"scss-bundle": "^3.1.1",
|
||||||
"ts-node": "~8.3.0",
|
"ts-node": "~8.3.0",
|
||||||
"tslib": "^1.11.1",
|
"tslib": "^1.11.1",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-store-chat",
|
"name": "@ucap/ng-store-chat",
|
||||||
"version": "0.0.5",
|
"version": "0.0.8",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,6 +13,8 @@ import {
|
||||||
Open3Response as CreateTimerResponse,
|
Open3Response as CreateTimerResponse,
|
||||||
ExitRequest as DeleteRequest,
|
ExitRequest as DeleteRequest,
|
||||||
ExitResponse as DeleteResponse,
|
ExitResponse as DeleteResponse,
|
||||||
|
ExitAllRequest as DeleteMultiRequest,
|
||||||
|
ExitAllResponse as DeleteMultiResponse,
|
||||||
UpdateRequest,
|
UpdateRequest,
|
||||||
UpdateResponse,
|
UpdateResponse,
|
||||||
InviteRequest,
|
InviteRequest,
|
||||||
|
@ -20,7 +22,8 @@ import {
|
||||||
ExitForcingRequest,
|
ExitForcingRequest,
|
||||||
ExitForcingResponse,
|
ExitForcingResponse,
|
||||||
UpdateTimerSetRequest,
|
UpdateTimerSetRequest,
|
||||||
UpdateTimerSetResponse
|
UpdateTimerSetResponse,
|
||||||
|
UserInfoShort
|
||||||
} from '@ucap/protocol-room';
|
} from '@ucap/protocol-room';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -32,6 +35,7 @@ export const rooms = createAction(
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
* Success of rooms request
|
* Success of rooms request
|
||||||
|
* RoomUserData is detail type.
|
||||||
*/
|
*/
|
||||||
export const roomsSuccess = createAction(
|
export const roomsSuccess = createAction(
|
||||||
'[ucap::chat::room] rooms Success',
|
'[ucap::chat::room] rooms Success',
|
||||||
|
@ -51,6 +55,31 @@ export const roomsFailure = createAction(
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success of rooms2 request
|
||||||
|
* RoomUserData is detail, short type both.
|
||||||
|
*/
|
||||||
|
export const rooms2Success = createAction(
|
||||||
|
'[ucap::chat::room] rooms2 Success',
|
||||||
|
props<{
|
||||||
|
roomList: RoomInfo[];
|
||||||
|
roomUserInfoMap: {
|
||||||
|
[param: string]: {
|
||||||
|
userInfoShortList: UserInfoShort[];
|
||||||
|
userInfoList: RoomUserInfo[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
syncDate: string;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Failure of rooms2 request
|
||||||
|
*/
|
||||||
|
export const rooms2Failure = createAction(
|
||||||
|
'[ucap::chat::room] rooms2 Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieve room information and list of user information
|
* retrieve room information and list of user information
|
||||||
*/
|
*/
|
||||||
|
@ -142,6 +171,28 @@ export const delFailure = createAction(
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* exit from multi room
|
||||||
|
*/
|
||||||
|
export const delMulti = createAction(
|
||||||
|
'[ucap::chat::room] delMulti',
|
||||||
|
props<{ req: DeleteMultiRequest }>()
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Success of delMulti request
|
||||||
|
*/
|
||||||
|
export const delMultiSuccess = createAction(
|
||||||
|
'[ucap::chat::room] delMulti Success',
|
||||||
|
props<{ res: DeleteMultiResponse }>()
|
||||||
|
);
|
||||||
|
/**
|
||||||
|
* Failure of delMulti request
|
||||||
|
*/
|
||||||
|
export const delMultiFailure = createAction(
|
||||||
|
'[ucap::chat::room] delMulti Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update room information
|
* update room information
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,6 +17,7 @@ import {
|
||||||
OpenResponse as CreateResponse,
|
OpenResponse as CreateResponse,
|
||||||
Open3Response as CreateTimerResponse,
|
Open3Response as CreateTimerResponse,
|
||||||
ExitResponse as DeleteResponse,
|
ExitResponse as DeleteResponse,
|
||||||
|
ExitAllResponse as DeleteMultiResponse,
|
||||||
UpdateResponse,
|
UpdateResponse,
|
||||||
InviteResponse,
|
InviteResponse,
|
||||||
ExitForcingResponse,
|
ExitForcingResponse,
|
||||||
|
@ -67,7 +68,12 @@ import {
|
||||||
expelFailure,
|
expelFailure,
|
||||||
updateTimeRoomInterval,
|
updateTimeRoomInterval,
|
||||||
updateTimeRoomIntervalSuccess,
|
updateTimeRoomIntervalSuccess,
|
||||||
updateTimeRoomIntervalFailure
|
updateTimeRoomIntervalFailure,
|
||||||
|
rooms2Success,
|
||||||
|
rooms2Failure,
|
||||||
|
delMulti,
|
||||||
|
delMultiSuccess,
|
||||||
|
delMultiFailure
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -75,7 +81,7 @@ export class Effects {
|
||||||
sessionCreatedForRooms$ = createEffect(() => {
|
sessionCreatedForRooms$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(LoginActions.sessionCreated),
|
ofType(LoginActions.sessionCreated),
|
||||||
map(action => rooms({ localeCode: action.loginSession.localeCode }))
|
map((action) => rooms({ localeCode: action.loginSession.localeCode }))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -84,17 +90,32 @@ export class Effects {
|
||||||
ofType(rooms),
|
ofType(rooms),
|
||||||
withLatestFrom(this.store.pipe(select(RoomSelector.roomsSyncDate))),
|
withLatestFrom(this.store.pipe(select(RoomSelector.roomsSyncDate))),
|
||||||
switchMap(([action, syncDate]) => {
|
switchMap(([action, syncDate]) => {
|
||||||
|
// // CASE :: RoomUser Data 중 Detail data 만 수집.
|
||||||
|
// return this.syncProtocolService
|
||||||
|
// .room({ syncDate, localeCode: action.localeCode })
|
||||||
|
// .pipe(
|
||||||
|
// map((res) => {
|
||||||
|
// return roomsSuccess({
|
||||||
|
// roomList: res.roomList,
|
||||||
|
// roomUserInfoMap: res.roomUserInfoMap,
|
||||||
|
// syncDate: res.res.syncDate
|
||||||
|
// });
|
||||||
|
// }),
|
||||||
|
// catchError((error) => of(roomsFailure({ error })))
|
||||||
|
// );
|
||||||
|
|
||||||
|
// CASE :: RoomUser Data 중 Detail data, Short data 수집.
|
||||||
return this.syncProtocolService
|
return this.syncProtocolService
|
||||||
.room({ syncDate, localeCode: action.localeCode })
|
.room2({ syncDate, localeCode: action.localeCode })
|
||||||
.pipe(
|
.pipe(
|
||||||
map(res =>
|
map((res) => {
|
||||||
roomsSuccess({
|
return rooms2Success({
|
||||||
roomList: res.roomList,
|
roomList: res.roomList,
|
||||||
roomUserInfoMap: res.roomUserInfoMap,
|
roomUserInfoMap: res.roomUserInfoMap,
|
||||||
syncDate: res.res.syncDate
|
syncDate: res.res.syncDate
|
||||||
})
|
});
|
||||||
),
|
}),
|
||||||
catchError(error => of(roomsFailure({ error })))
|
catchError((error) => of(rooms2Failure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -103,16 +124,16 @@ export class Effects {
|
||||||
room$ = createEffect(() => {
|
room$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(room),
|
ofType(room),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
switchMap(req => {
|
switchMap((req) => {
|
||||||
return this.roomProtocolService.info(req).pipe(
|
return this.roomProtocolService.info(req).pipe(
|
||||||
map(res =>
|
map((res) =>
|
||||||
roomSuccess({
|
roomSuccess({
|
||||||
roomInfo: res.roomInfo,
|
roomInfo: res.roomInfo,
|
||||||
userInfoList: res.userInfoList
|
userInfoList: res.userInfoList
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
catchError(error => of(roomFailure({ error })))
|
catchError((error) => of(roomFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -121,13 +142,13 @@ export class Effects {
|
||||||
create$ = createEffect(() =>
|
create$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(create),
|
ofType(create),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req => {
|
exhaustMap((req) => {
|
||||||
return this.roomProtocolService.open(req).pipe(
|
return this.roomProtocolService.open(req).pipe(
|
||||||
map((res: CreateResponse) => {
|
map((res: CreateResponse) => {
|
||||||
return createSuccess({ res });
|
return createSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(createFailure({ error })))
|
catchError((error) => of(createFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -136,13 +157,13 @@ export class Effects {
|
||||||
createTimer$ = createEffect(() =>
|
createTimer$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(createTimer),
|
ofType(createTimer),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req => {
|
exhaustMap((req) => {
|
||||||
return this.roomProtocolService.open3(req).pipe(
|
return this.roomProtocolService.open3(req).pipe(
|
||||||
map((res: CreateTimerResponse) => {
|
map((res: CreateTimerResponse) => {
|
||||||
return createTimerSuccess({ res });
|
return createTimerSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(createTimerFailure({ error })))
|
catchError((error) => of(createTimerFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -151,14 +172,30 @@ export class Effects {
|
||||||
del$ = createEffect(() =>
|
del$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(del),
|
ofType(del),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req => {
|
exhaustMap((req) => {
|
||||||
return this.roomProtocolService.exit(req).pipe(
|
return this.roomProtocolService.exit(req).pipe(
|
||||||
switchMap((res: DeleteResponse) => [
|
switchMap((res: DeleteResponse) => [
|
||||||
close({ roomIds: [res.roomId] }),
|
close({ roomIds: [res.roomId] }),
|
||||||
delSuccess({ res })
|
delSuccess({ res })
|
||||||
]),
|
]),
|
||||||
catchError(error => of(delFailure({ error })))
|
catchError((error) => of(delFailure({ error })))
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
delMulti$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(delMulti),
|
||||||
|
map((action) => action.req),
|
||||||
|
exhaustMap((req) => {
|
||||||
|
return this.roomProtocolService.exitAll(req).pipe(
|
||||||
|
switchMap((res: DeleteMultiResponse) => [
|
||||||
|
close({ roomIds: res.roomIds }),
|
||||||
|
delMultiSuccess({ res })
|
||||||
|
]),
|
||||||
|
catchError((error) => of(delMultiFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -167,13 +204,13 @@ export class Effects {
|
||||||
update$ = createEffect(() =>
|
update$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(update),
|
ofType(update),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req => {
|
exhaustMap((req) => {
|
||||||
return this.roomProtocolService.update(req).pipe(
|
return this.roomProtocolService.update(req).pipe(
|
||||||
map((res: UpdateResponse) => {
|
map((res: UpdateResponse) => {
|
||||||
return updateSuccess({ res });
|
return updateSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(updateFailure({ error })))
|
catchError((error) => of(updateFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -182,7 +219,7 @@ export class Effects {
|
||||||
excludeUser$ = createEffect(() => {
|
excludeUser$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(excludeUser),
|
ofType(excludeUser),
|
||||||
map(action =>
|
map((action) =>
|
||||||
excludeUserSuccess({
|
excludeUserSuccess({
|
||||||
roomId: action.roomId,
|
roomId: action.roomId,
|
||||||
userSeqs: action.userSeqs
|
userSeqs: action.userSeqs
|
||||||
|
@ -194,21 +231,21 @@ export class Effects {
|
||||||
open$ = createEffect(() => {
|
open$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(open),
|
ofType(open),
|
||||||
map(action => openSuccess({ roomIds: [...action.roomIds] }))
|
map((action) => openSuccess({ roomIds: [...action.roomIds] }))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
close$ = createEffect(() => {
|
close$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(close),
|
ofType(close),
|
||||||
map(action => closeSuccess({ roomIds: [...action.roomIds] }))
|
map((action) => closeSuccess({ roomIds: [...action.roomIds] }))
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
inviteOrCreate$ = createEffect(() =>
|
inviteOrCreate$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(inviteOrCreate),
|
ofType(inviteOrCreate),
|
||||||
map(action => {
|
map((action) => {
|
||||||
const roomInfo = action.roomInfo;
|
const roomInfo = action.roomInfo;
|
||||||
const localeCode = action.localeCode;
|
const localeCode = action.localeCode;
|
||||||
|
|
||||||
|
@ -235,7 +272,7 @@ export class Effects {
|
||||||
invite$ = createEffect(() =>
|
invite$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(invite),
|
ofType(invite),
|
||||||
exhaustMap(action => {
|
exhaustMap((action) => {
|
||||||
const req = action.req;
|
const req = action.req;
|
||||||
const localeCode = action.localeCode;
|
const localeCode = action.localeCode;
|
||||||
|
|
||||||
|
@ -252,7 +289,7 @@ export class Effects {
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
catchError(error => of(inviteFailure({ error })))
|
catchError((error) => of(inviteFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -261,13 +298,13 @@ export class Effects {
|
||||||
expel$ = createEffect(() =>
|
expel$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(expel),
|
ofType(expel),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req => {
|
exhaustMap((req) => {
|
||||||
return this.roomProtocolService.exitForcing(req).pipe(
|
return this.roomProtocolService.exitForcing(req).pipe(
|
||||||
map((res: ExitForcingResponse) => {
|
map((res: ExitForcingResponse) => {
|
||||||
return expelSuccess({ res });
|
return expelSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(expelFailure({ error })))
|
catchError((error) => of(expelFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -276,13 +313,13 @@ export class Effects {
|
||||||
updateTimeRoomInterval$ = createEffect(() =>
|
updateTimeRoomInterval$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(updateTimeRoomInterval),
|
ofType(updateTimeRoomInterval),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req => {
|
exhaustMap((req) => {
|
||||||
return this.roomProtocolService.updateTimerSet(req).pipe(
|
return this.roomProtocolService.updateTimerSet(req).pipe(
|
||||||
map((res: UpdateTimerSetResponse) => {
|
map((res: UpdateTimerSetResponse) => {
|
||||||
return updateTimeRoomIntervalSuccess({ res });
|
return updateTimeRoomIntervalSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(updateTimeRoomIntervalFailure({ error })))
|
catchError((error) => of(updateTimeRoomIntervalFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -291,7 +328,7 @@ export class Effects {
|
||||||
inviteNotification$ = createEffect(() => {
|
inviteNotification$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(inviteNotification),
|
ofType(inviteNotification),
|
||||||
map(action =>
|
map((action) =>
|
||||||
room({
|
room({
|
||||||
req: {
|
req: {
|
||||||
roomId: action.noti.roomId,
|
roomId: action.noti.roomId,
|
||||||
|
@ -306,7 +343,7 @@ export class Effects {
|
||||||
exitNotification$ = createEffect(() => {
|
exitNotification$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(exitNotification),
|
ofType(exitNotification),
|
||||||
switchMap(action => {
|
switchMap((action) => {
|
||||||
if (action.userSeq === action.senderSeq) {
|
if (action.userSeq === action.senderSeq) {
|
||||||
return [
|
return [
|
||||||
close({ roomIds: [action.roomId] }),
|
close({ roomIds: [action.roomId] }),
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
import { createReducer, on } from '@ngrx/store';
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
|
||||||
import { UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
import {
|
||||||
|
UserInfo as RoomUserInfo,
|
||||||
|
UserInfoShort as RoomUserInfoShort,
|
||||||
|
RoomInfo
|
||||||
|
} from '@ucap/protocol-room';
|
||||||
|
|
||||||
import * as chattingActions from '../chatting/actions';
|
import * as chattingActions from '../chatting/actions';
|
||||||
|
|
||||||
|
@ -8,14 +12,19 @@ import {
|
||||||
initialState,
|
initialState,
|
||||||
adapterRoom,
|
adapterRoom,
|
||||||
adapterRoomUser,
|
adapterRoomUser,
|
||||||
RoomUserMap
|
RoomUserMap,
|
||||||
|
RoomUserShortMap,
|
||||||
|
adapterRoomUserShort
|
||||||
} from './state';
|
} from './state';
|
||||||
import {
|
import {
|
||||||
roomsSuccess,
|
roomsSuccess,
|
||||||
roomSuccess,
|
roomSuccess,
|
||||||
excludeUser,
|
excludeUser,
|
||||||
excludeUserSuccess,
|
excludeUserSuccess,
|
||||||
delSuccess
|
delSuccess,
|
||||||
|
rooms2Success,
|
||||||
|
delMultiSuccess,
|
||||||
|
updateSuccess
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
export const reducer = createReducer(
|
export const reducer = createReducer(
|
||||||
|
@ -40,7 +49,7 @@ export const reducer = createReducer(
|
||||||
const value = state.rooms.entities[key];
|
const value = state.rooms.entities[key];
|
||||||
unreadTotal += isNaN(value.noReadCnt) ? 0 : value.noReadCnt;
|
unreadTotal += isNaN(value.noReadCnt) ? 0 : value.noReadCnt;
|
||||||
}
|
}
|
||||||
action.roomList.map(item => (unreadTotal += item.noReadCnt));
|
action.roomList.map((item) => (unreadTotal += item.noReadCnt));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -55,6 +64,53 @@ export const reducer = createReducer(
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
on(rooms2Success, (state, action) => {
|
||||||
|
const roomUserMapList: RoomUserMap[] = [];
|
||||||
|
const roomUserShortMapList: RoomUserShortMap[] = [];
|
||||||
|
|
||||||
|
for (const key in action.roomUserInfoMap) {
|
||||||
|
if (action.roomUserInfoMap.hasOwnProperty(key)) {
|
||||||
|
const userInfosMap = action.roomUserInfoMap[key];
|
||||||
|
if (!!userInfosMap && 0 < userInfosMap.userInfoList.length) {
|
||||||
|
roomUserMapList.push({
|
||||||
|
roomId: key,
|
||||||
|
userInfos: userInfosMap.userInfoList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!!userInfosMap && 0 < userInfosMap.userInfoShortList.length) {
|
||||||
|
roomUserShortMapList.push({
|
||||||
|
roomId: key,
|
||||||
|
userInfos: userInfosMap.userInfoShortList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let unreadTotal = 0;
|
||||||
|
|
||||||
|
// tslint:disable-next-line: forin
|
||||||
|
for (const key in state.rooms.entities) {
|
||||||
|
const value = state.rooms.entities[key];
|
||||||
|
unreadTotal += isNaN(value.noReadCnt) ? 0 : value.noReadCnt;
|
||||||
|
}
|
||||||
|
action.roomList.map((item) => (unreadTotal += item.noReadCnt));
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
rooms: adapterRoom.upsertMany(action.roomList, {
|
||||||
|
...state.rooms,
|
||||||
|
syncDate: action.syncDate
|
||||||
|
}),
|
||||||
|
roomUsers: adapterRoomUser.upsertMany(roomUserMapList, {
|
||||||
|
...state.roomUsers
|
||||||
|
}),
|
||||||
|
roomUsersShort: adapterRoomUserShort.upsertMany(roomUserShortMapList, {
|
||||||
|
...state.roomUsersShort
|
||||||
|
}),
|
||||||
|
unreadTotal
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
on(roomSuccess, (state, action) => {
|
on(roomSuccess, (state, action) => {
|
||||||
const roomUserMapList: RoomUserMap[] = [];
|
const roomUserMapList: RoomUserMap[] = [];
|
||||||
|
|
||||||
|
@ -82,17 +138,27 @@ export const reducer = createReducer(
|
||||||
on(excludeUserSuccess, (state, action) => {
|
on(excludeUserSuccess, (state, action) => {
|
||||||
const roomId = action.roomId;
|
const roomId = action.roomId;
|
||||||
const roomUserMap = state.roomUsers.entities[roomId];
|
const roomUserMap = state.roomUsers.entities[roomId];
|
||||||
|
const roomUserMapShort = state.roomUsersShort.entities[roomId];
|
||||||
|
|
||||||
const userInfos: RoomUserInfo[] = [...roomUserMap.userInfos];
|
const userInfos: RoomUserInfo[] = [...roomUserMap.userInfos];
|
||||||
|
const userInfosShort: RoomUserInfoShort[] = [...roomUserMapShort.userInfos];
|
||||||
|
|
||||||
action.userSeqs.forEach(userSeq => {
|
action.userSeqs.forEach((userSeq) => {
|
||||||
const userInfo: RoomUserInfo = userInfos.find(
|
const userInfo: RoomUserInfo = userInfos.find(
|
||||||
u => userSeq === String(u.seq)
|
(u) => userSeq === String(u.seq)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!!userInfo && !!userInfo.seq) {
|
if (!!userInfo && !!userInfo.seq) {
|
||||||
userInfo.isJoinRoom = false;
|
userInfo.isJoinRoom = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userInfoShort: RoomUserInfoShort = userInfosShort.find(
|
||||||
|
(u) => userSeq === String(u.seq)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!!userInfoShort && !!userInfoShort.seq) {
|
||||||
|
userInfoShort.isJoinRoom = false;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -108,10 +174,35 @@ export const reducer = createReducer(
|
||||||
{
|
{
|
||||||
...state.roomUsers
|
...state.roomUsers
|
||||||
}
|
}
|
||||||
|
),
|
||||||
|
roomUsersShort: adapterRoomUserShort.updateOne(
|
||||||
|
{
|
||||||
|
id: roomId,
|
||||||
|
changes: {
|
||||||
|
roomId,
|
||||||
|
userInfos: userInfosShort
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...state.roomUsersShort
|
||||||
|
}
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
on(updateSuccess, (state, action) => {
|
||||||
|
const roomInfo = {
|
||||||
|
...state.rooms.entities[action.res.roomId],
|
||||||
|
roomName: action.res.roomName,
|
||||||
|
receiveAlarm: action.res.receiveAlarm
|
||||||
|
} as RoomInfo;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
rooms: adapterRoom.upsertOne(roomInfo, { ...state.rooms })
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
on(delSuccess, (state, action) => {
|
on(delSuccess, (state, action) => {
|
||||||
const roomId = action.res.roomId;
|
const roomId = action.res.roomId;
|
||||||
const room = state.rooms.entities[roomId];
|
const room = state.rooms.entities[roomId];
|
||||||
|
@ -123,7 +214,27 @@ export const reducer = createReducer(
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
rooms: adapterRoom.removeOne(roomId, { ...state.rooms }),
|
rooms: adapterRoom.removeOne(roomId, { ...state.rooms }),
|
||||||
roomUsers: adapterRoomUser.removeOne(roomId, { ...state.roomUsers })
|
roomUsers: adapterRoomUser.removeOne(roomId, { ...state.roomUsers }),
|
||||||
|
roomUsersShort: adapterRoomUserShort.removeOne(roomId, {
|
||||||
|
...state.roomUsersShort
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(delMultiSuccess, (state, action) => {
|
||||||
|
const roomIds: string[] = action.res.roomIds;
|
||||||
|
|
||||||
|
if (!roomIds || roomIds.length === 0) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
rooms: adapterRoom.removeMany(roomIds, { ...state.rooms }),
|
||||||
|
roomUsers: adapterRoomUser.removeMany(roomIds, { ...state.roomUsers }),
|
||||||
|
roomUsersShort: adapterRoomUserShort.removeMany(roomIds, {
|
||||||
|
...state.roomUsersShort
|
||||||
|
})
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,22 +3,22 @@ import moment from 'moment';
|
||||||
import { Selector, createSelector } from '@ngrx/store';
|
import { Selector, createSelector } from '@ngrx/store';
|
||||||
import { EntityState, createEntityAdapter, Dictionary } from '@ngrx/entity';
|
import { EntityState, createEntityAdapter, Dictionary } from '@ngrx/entity';
|
||||||
|
|
||||||
import { RoomUserDetailData } from '@ucap/protocol-sync';
|
import { RoomUserDetailData, RoomUserData } from '@ucap/protocol-sync';
|
||||||
import { RoomInfo, UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
import {
|
||||||
|
RoomInfo,
|
||||||
|
UserInfo as RoomUserInfo,
|
||||||
|
UserInfoShort as RoomUserInfoShort
|
||||||
|
} from '@ucap/protocol-room';
|
||||||
|
|
||||||
export interface RoomState extends EntityState<RoomInfo> {
|
export interface RoomState extends EntityState<RoomInfo> {
|
||||||
syncDate: string;
|
syncDate: string;
|
||||||
}
|
}
|
||||||
export const adapterRoom = createEntityAdapter<RoomInfo>({
|
export const adapterRoom = createEntityAdapter<RoomInfo>({
|
||||||
selectId: roomInfo => roomInfo.roomId,
|
selectId: (roomInfo) => roomInfo.roomId,
|
||||||
sortComparer: (a, b) => {
|
sortComparer: (a, b) => {
|
||||||
return (
|
return (
|
||||||
moment(b.finalEventDate)
|
moment(b.finalEventDate).toDate().getTime() -
|
||||||
.toDate()
|
moment(a.finalEventDate).toDate().getTime()
|
||||||
.getTime() -
|
|
||||||
moment(a.finalEventDate)
|
|
||||||
.toDate()
|
|
||||||
.getTime()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -28,24 +28,39 @@ export interface RoomUserMap {
|
||||||
userInfos: RoomUserInfo[];
|
userInfos: RoomUserInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RoomUserShortMap {
|
||||||
|
roomId: string;
|
||||||
|
userInfos: RoomUserInfoShort[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface RoomUserState extends EntityState<RoomUserMap> {}
|
export interface RoomUserState extends EntityState<RoomUserMap> {}
|
||||||
export const adapterRoomUser = createEntityAdapter<RoomUserMap>({
|
export const adapterRoomUser = createEntityAdapter<RoomUserMap>({
|
||||||
selectId: roomUserDetailData => roomUserDetailData.roomId
|
selectId: (roomUserDetailData) => roomUserDetailData.roomId
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface RoomUserShortState extends EntityState<RoomUserShortMap> {}
|
||||||
|
export const adapterRoomUserShort = createEntityAdapter<RoomUserShortMap>({
|
||||||
|
selectId: (roomUserShortData) => roomUserShortData.roomId
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
rooms: RoomState;
|
rooms: RoomState;
|
||||||
roomUsers: RoomUserState;
|
roomUsers: RoomUserState;
|
||||||
|
roomUsersShort: RoomUserShortState;
|
||||||
}
|
}
|
||||||
|
|
||||||
const roomInitialState: RoomState = adapterRoom.getInitialState({
|
const roomInitialState: RoomState = adapterRoom.getInitialState({
|
||||||
syncDate: ''
|
syncDate: ''
|
||||||
});
|
});
|
||||||
const roomUserInitialState: RoomUserState = adapterRoomUser.getInitialState({});
|
const roomUserInitialState: RoomUserState = adapterRoomUser.getInitialState({});
|
||||||
|
const roomUserShortInitialState: RoomUserShortState = adapterRoomUserShort.getInitialState(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
export const initialState: State = {
|
export const initialState: State = {
|
||||||
rooms: roomInitialState,
|
rooms: roomInitialState,
|
||||||
roomUsers: roomUserInitialState
|
roomUsers: roomUserInitialState,
|
||||||
|
roomUsersShort: roomUserShortInitialState
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -62,6 +77,13 @@ const {
|
||||||
selectTotal: selectTotalForRoomUser
|
selectTotal: selectTotalForRoomUser
|
||||||
} = adapterRoomUser.getSelectors();
|
} = adapterRoomUser.getSelectors();
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectAll: selectAllForRoomUserShort,
|
||||||
|
selectEntities: selectEntitiesForRoomUserShort,
|
||||||
|
selectIds: selectIdsForRoomUserShort,
|
||||||
|
selectTotal: selectTotalForRoomUserShort
|
||||||
|
} = adapterRoomUserShort.getSelectors();
|
||||||
|
|
||||||
export function selectors<S>(selector: Selector<any, State>) {
|
export function selectors<S>(selector: Selector<any, State>) {
|
||||||
const selectRooms = createSelector(selector, (state: State) => state.rooms);
|
const selectRooms = createSelector(selector, (state: State) => state.rooms);
|
||||||
|
|
||||||
|
@ -70,6 +92,11 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
(state: State) => state.roomUsers
|
(state: State) => state.roomUsers
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const selectRoomUsersShort = createSelector(
|
||||||
|
selector,
|
||||||
|
(state: State) => state.roomUsersShort
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
rooms: createSelector(selectRooms, selectAllForRoom),
|
rooms: createSelector(selectRooms, selectAllForRoom),
|
||||||
room: createSelector(
|
room: createSelector(
|
||||||
|
@ -78,7 +105,10 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
(roomState: RoomState, entities: Dictionary<RoomInfo>, roomId: string) =>
|
(roomState: RoomState, entities: Dictionary<RoomInfo>, roomId: string) =>
|
||||||
entities && entities[roomId]
|
entities && entities[roomId]
|
||||||
),
|
),
|
||||||
roomsSyncDate: createSelector(selectRooms, roomState => roomState.syncDate),
|
roomsSyncDate: createSelector(
|
||||||
|
selectRooms,
|
||||||
|
(roomState) => roomState.syncDate
|
||||||
|
),
|
||||||
roomUsers: createSelector(selectRoomUsers, selectAllForRoomUser),
|
roomUsers: createSelector(selectRoomUsers, selectAllForRoomUser),
|
||||||
roomUser: createSelector(
|
roomUser: createSelector(
|
||||||
selectRoomUsers,
|
selectRoomUsers,
|
||||||
|
@ -89,6 +119,19 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
roomId: string
|
roomId: string
|
||||||
) => entities && entities[roomId]
|
) => entities && entities[roomId]
|
||||||
),
|
),
|
||||||
|
roomUsersShort: createSelector(
|
||||||
|
selectRoomUsersShort,
|
||||||
|
selectAllForRoomUserShort
|
||||||
|
),
|
||||||
|
roomUserShort: createSelector(
|
||||||
|
selectRoomUsersShort,
|
||||||
|
selectEntitiesForRoomUserShort,
|
||||||
|
(
|
||||||
|
roomUserShortState: RoomUserShortState,
|
||||||
|
entities: Dictionary<RoomUserData>,
|
||||||
|
roomId: string
|
||||||
|
) => entities && entities[roomId]
|
||||||
|
),
|
||||||
unreadTotal: createSelector(
|
unreadTotal: createSelector(
|
||||||
selectRooms,
|
selectRooms,
|
||||||
selectAllForRoom,
|
selectAllForRoom,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-store-group",
|
"name": "@ucap/ng-store-group",
|
||||||
"version": "0.0.6",
|
"version": "0.0.9",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -23,7 +23,10 @@ import { SyncProtocolService } from '@ucap/ng-protocol-sync';
|
||||||
|
|
||||||
import { BuddyProtocolService } from '@ucap/ng-protocol-buddy';
|
import { BuddyProtocolService } from '@ucap/ng-protocol-buddy';
|
||||||
|
|
||||||
import { DepartmentSelector } from '@ucap/ng-store-organization';
|
import {
|
||||||
|
DepartmentSelector,
|
||||||
|
PresenceActions
|
||||||
|
} from '@ucap/ng-store-organization';
|
||||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
import { LoginActions } from '@ucap/ng-store-authentication';
|
||||||
|
|
||||||
import * as groupActions from '../group/actions';
|
import * as groupActions from '../group/actions';
|
||||||
|
@ -53,7 +56,7 @@ export class Effects {
|
||||||
sessionCreatedForBuddy2$ = createEffect(() => {
|
sessionCreatedForBuddy2$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(LoginActions.sessionCreated),
|
ofType(LoginActions.sessionCreated),
|
||||||
map(action => buddy2())
|
map((action) => buddy2())
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -63,13 +66,21 @@ export class Effects {
|
||||||
withLatestFrom(this.store.pipe(select(BuddySelector.buddySyncDate))),
|
withLatestFrom(this.store.pipe(select(BuddySelector.buddySyncDate))),
|
||||||
switchMap(([action, syncDate]) => {
|
switchMap(([action, syncDate]) => {
|
||||||
return this.syncProtocolService.buddy2({ syncDate }).pipe(
|
return this.syncProtocolService.buddy2({ syncDate }).pipe(
|
||||||
map(res =>
|
map((res) => {
|
||||||
buddy2Success({
|
// Buddy Presence
|
||||||
|
this.store.dispatch(
|
||||||
|
PresenceActions.bulkInfo({
|
||||||
|
divCd: 'buddyBulk',
|
||||||
|
userSeqs: res.buddyInfos.map((userInfo) => userInfo.seq + '')
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return buddy2Success({
|
||||||
buddyList: res.buddyInfos,
|
buddyList: res.buddyInfos,
|
||||||
syncDate: res.res.syncDate
|
syncDate: res.res.syncDate
|
||||||
})
|
});
|
||||||
),
|
}),
|
||||||
catchError(error => of(buddy2Failure({ error })))
|
catchError((error) => of(buddy2Failure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -78,13 +89,13 @@ export class Effects {
|
||||||
add$ = createEffect(() =>
|
add$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(add),
|
ofType(add),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req =>
|
exhaustMap((req) =>
|
||||||
this.buddyProtocolService.add(req).pipe(
|
this.buddyProtocolService.add(req).pipe(
|
||||||
map((res: BuddyAddResponse) => {
|
map((res: BuddyAddResponse) => {
|
||||||
return buddy2();
|
return buddy2();
|
||||||
}),
|
}),
|
||||||
catchError(error => of(addFailure({ error })))
|
catchError((error) => of(addFailure({ error })))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -93,13 +104,13 @@ export class Effects {
|
||||||
del$ = createEffect(() =>
|
del$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(del),
|
ofType(del),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req =>
|
exhaustMap((req) =>
|
||||||
this.buddyProtocolService.del(req).pipe(
|
this.buddyProtocolService.del(req).pipe(
|
||||||
map((res: BuddyDelResponse) => {
|
map((res: BuddyDelResponse) => {
|
||||||
return delSuccess({ res });
|
return delSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(delFailure({ error })))
|
catchError((error) => of(delFailure({ error })))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -122,7 +133,7 @@ export class Effects {
|
||||||
this.moduleConfig.useMyDeptGroup &&
|
this.moduleConfig.useMyDeptGroup &&
|
||||||
!!this.moduleConfig.fixedGroupSeqs &&
|
!!this.moduleConfig.fixedGroupSeqs &&
|
||||||
this.moduleConfig.fixedGroupSeqs.filter(
|
this.moduleConfig.fixedGroupSeqs.filter(
|
||||||
fixedGroupSeq => fixedGroupSeq === group.seq
|
(fixedGroupSeq) => fixedGroupSeq === group.seq
|
||||||
).length > 0
|
).length > 0
|
||||||
) {
|
) {
|
||||||
// skip;;
|
// skip;;
|
||||||
|
@ -134,7 +145,7 @@ export class Effects {
|
||||||
groupSeq: group.seq,
|
groupSeq: group.seq,
|
||||||
groupName: group.name,
|
groupName: group.name,
|
||||||
userSeqs: group.userSeqs.filter(
|
userSeqs: group.userSeqs.filter(
|
||||||
userSeq => userSeq !== String(req.seq)
|
(userSeq) => userSeq !== String(req.seq)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -147,8 +158,9 @@ export class Effects {
|
||||||
if (
|
if (
|
||||||
!!this.moduleConfig.useMyDeptGroup &&
|
!!this.moduleConfig.useMyDeptGroup &&
|
||||||
this.moduleConfig.useMyDeptGroup &&
|
this.moduleConfig.useMyDeptGroup &&
|
||||||
myDeptUserList.filter(deptUser => deptUser.seq === String(req.seq))
|
myDeptUserList.filter(
|
||||||
.length > 0
|
(deptUser) => deptUser.seq === String(req.seq)
|
||||||
|
).length > 0
|
||||||
) {
|
) {
|
||||||
// skip;;
|
// skip;;
|
||||||
} else {
|
} else {
|
||||||
|
@ -174,13 +186,13 @@ export class Effects {
|
||||||
update$ = createEffect(() =>
|
update$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(update),
|
ofType(update),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap(req =>
|
exhaustMap((req) =>
|
||||||
this.buddyProtocolService.update(req).pipe(
|
this.buddyProtocolService.update(req).pipe(
|
||||||
map((res: BuddyUpdateResponse) => {
|
map((res: BuddyUpdateResponse) => {
|
||||||
return updateSuccess({ res });
|
return updateSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(updateFailure({ error })))
|
catchError((error) => of(updateFailure({ error })))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -196,13 +208,15 @@ export class Effects {
|
||||||
|
|
||||||
if (!!targetUserSeqs && 0 < targetUserSeqs.length) {
|
if (!!targetUserSeqs && 0 < targetUserSeqs.length) {
|
||||||
const addBuddyList: string[] = [];
|
const addBuddyList: string[] = [];
|
||||||
targetUserSeqs.forEach(userSeq => {
|
targetUserSeqs.forEach((userSeq) => {
|
||||||
if (!buddyList) {
|
if (!buddyList) {
|
||||||
addBuddyList.push(userSeq);
|
addBuddyList.push(userSeq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = buddyList.findIndex(b => String(b.seq) === userSeq);
|
const index = buddyList.findIndex(
|
||||||
|
(b) => String(b.seq) === userSeq
|
||||||
|
);
|
||||||
if (-1 < index) {
|
if (-1 < index) {
|
||||||
addBuddyList.push(userSeq);
|
addBuddyList.push(userSeq);
|
||||||
return;
|
return;
|
||||||
|
@ -230,13 +244,15 @@ export class Effects {
|
||||||
|
|
||||||
if (!!targetUserSeqs && 0 < targetUserSeqs.length) {
|
if (!!targetUserSeqs && 0 < targetUserSeqs.length) {
|
||||||
const addBuddyList: string[] = [];
|
const addBuddyList: string[] = [];
|
||||||
targetUserSeqs.forEach(userSeq => {
|
targetUserSeqs.forEach((userSeq) => {
|
||||||
if (!buddyList) {
|
if (!buddyList) {
|
||||||
addBuddyList.push(userSeq);
|
addBuddyList.push(userSeq);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = buddyList.findIndex(b => String(b.seq) === userSeq);
|
const index = buddyList.findIndex(
|
||||||
|
(b) => String(b.seq) === userSeq
|
||||||
|
);
|
||||||
if (-1 < index) {
|
if (-1 < index) {
|
||||||
addBuddyList.push(userSeq);
|
addBuddyList.push(userSeq);
|
||||||
return;
|
return;
|
||||||
|
@ -249,7 +265,7 @@ export class Effects {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!userSeqsForDelete && 0 < userSeqsForDelete.length) {
|
if (!!userSeqsForDelete && 0 < userSeqsForDelete.length) {
|
||||||
userSeqsForDelete.forEach(userSeq => {
|
userSeqsForDelete.forEach((userSeq) => {
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
update({ req: { seq: Number(userSeq), isFavorit: false } })
|
update({ req: { seq: Number(userSeq), isFavorit: false } })
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,7 +31,7 @@ import { _MODULE_CONFIG } from '../../config/token';
|
||||||
|
|
||||||
import * as buddyActions from '../buddy/actions';
|
import * as buddyActions from '../buddy/actions';
|
||||||
|
|
||||||
import { GroupSelector } from '../state';
|
import { GroupSelector, BuddySelector } from '../state';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
groups,
|
groups,
|
||||||
|
@ -57,7 +57,7 @@ export class Effects {
|
||||||
sessionCreatedForGroups$ = createEffect(() => {
|
sessionCreatedForGroups$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(LoginActions.sessionCreated),
|
ofType(LoginActions.sessionCreated),
|
||||||
map(action => groups())
|
map((action) => groups())
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -67,13 +67,13 @@ export class Effects {
|
||||||
withLatestFrom(this.store.pipe(select(GroupSelector.groupSyncDate))),
|
withLatestFrom(this.store.pipe(select(GroupSelector.groupSyncDate))),
|
||||||
switchMap(([action, syncDate]) => {
|
switchMap(([action, syncDate]) => {
|
||||||
return this.syncProtocolService.group2({ syncDate }).pipe(
|
return this.syncProtocolService.group2({ syncDate }).pipe(
|
||||||
map(res =>
|
map((res) =>
|
||||||
groupsSuccess({
|
groupsSuccess({
|
||||||
groupList: res.groupInfos,
|
groupList: res.groupInfos,
|
||||||
syncDate: res.res.syncDate
|
syncDate: res.res.syncDate
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
catchError(error => of(groupsFailure({ error })))
|
catchError((error) => of(groupsFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -82,7 +82,8 @@ export class Effects {
|
||||||
create$ = createEffect(() =>
|
create$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(create),
|
ofType(create),
|
||||||
exhaustMap(action => {
|
withLatestFrom(this.store.pipe(select(BuddySelector.buddies))),
|
||||||
|
exhaustMap(([action, buddies]) => {
|
||||||
return this.groupProtocolService
|
return this.groupProtocolService
|
||||||
.add({
|
.add({
|
||||||
groupName: action.groupName
|
groupName: action.groupName
|
||||||
|
@ -92,6 +93,19 @@ export class Effects {
|
||||||
const actions: any[] = [];
|
const actions: any[] = [];
|
||||||
|
|
||||||
if (!!action.targetUserSeqs && 0 < action.targetUserSeqs.length) {
|
if (!!action.targetUserSeqs && 0 < action.targetUserSeqs.length) {
|
||||||
|
const addBuddies: number[] = [];
|
||||||
|
action.targetUserSeqs.forEach((item) => {
|
||||||
|
if (!buddies[item]) {
|
||||||
|
addBuddies.push(Number(item));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (addBuddies.length > 0) {
|
||||||
|
actions.push(
|
||||||
|
buddyActions.add({
|
||||||
|
req: { userSeqs: action.targetUserSeqs }
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
actions.push(
|
actions.push(
|
||||||
update({
|
update({
|
||||||
req: {
|
req: {
|
||||||
|
@ -108,7 +122,7 @@ export class Effects {
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}),
|
}),
|
||||||
catchError(error => of(createFailure({ error })))
|
catchError((error) => of(createFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -128,7 +142,7 @@ export class Effects {
|
||||||
|
|
||||||
// Del Buddy
|
// Del Buddy
|
||||||
let userSeqsForDelete: string[] = targetGroup.userSeqs.filter(
|
let userSeqsForDelete: string[] = targetGroup.userSeqs.filter(
|
||||||
v => targetUserSeqs.indexOf(v) < 0
|
(v) => targetUserSeqs.indexOf(v) < 0
|
||||||
);
|
);
|
||||||
|
|
||||||
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
||||||
|
@ -137,18 +151,18 @@ export class Effects {
|
||||||
this.moduleConfig.useMyDeptGroup
|
this.moduleConfig.useMyDeptGroup
|
||||||
) {
|
) {
|
||||||
userSeqsForDelete = userSeqsForDelete.filter(
|
userSeqsForDelete = userSeqsForDelete.filter(
|
||||||
delbuddy =>
|
(delbuddy) =>
|
||||||
myDeptUserList.filter(deptUser => deptUser.seq === delbuddy)
|
myDeptUserList.filter((deptUser) => deptUser.seq === delbuddy)
|
||||||
.length === 0
|
.length === 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
userSeqsForDelete = userSeqsForDelete.filter(delbuddy => {
|
userSeqsForDelete = userSeqsForDelete.filter((delbuddy) => {
|
||||||
let exist = false;
|
let exist = false;
|
||||||
|
|
||||||
for (const group of groupList) {
|
for (const group of groupList) {
|
||||||
if (
|
if (
|
||||||
group.seq !== targetGroup.seq &&
|
group.seq !== targetGroup.seq &&
|
||||||
group.userSeqs.filter(v => v === delbuddy).length > 0
|
group.userSeqs.filter((v) => v === delbuddy).length > 0
|
||||||
) {
|
) {
|
||||||
exist = true;
|
exist = true;
|
||||||
break;
|
break;
|
||||||
|
@ -184,10 +198,10 @@ export class Effects {
|
||||||
withLatestFrom(this.store.pipe(select(GroupSelector.groups))),
|
withLatestFrom(this.store.pipe(select(GroupSelector.groups))),
|
||||||
exhaustMap(([action, groupList]) => {
|
exhaustMap(([action, groupList]) => {
|
||||||
// copy to
|
// copy to
|
||||||
const toGroup = groupList.find(g => g.seq === action.toGroup.seq);
|
const toGroup = groupList.find((g) => g.seq === action.toGroup.seq);
|
||||||
|
|
||||||
let toTrgtUserSeqs = toGroup.userSeqs;
|
let toTrgtUserSeqs = toGroup.userSeqs;
|
||||||
action.targetUserSeq.forEach(trgtSeq => {
|
action.targetUserSeq.forEach((trgtSeq) => {
|
||||||
if (toTrgtUserSeqs.indexOf(trgtSeq) > -1) {
|
if (toTrgtUserSeqs.indexOf(trgtSeq) > -1) {
|
||||||
// ignore
|
// ignore
|
||||||
} else {
|
} else {
|
||||||
|
@ -205,7 +219,7 @@ export class Effects {
|
||||||
exhaustMap((resTo: GroupUpdateResponse) => {
|
exhaustMap((resTo: GroupUpdateResponse) => {
|
||||||
// del from
|
// del from
|
||||||
const fromGroup = groupList.find(
|
const fromGroup = groupList.find(
|
||||||
g => g.seq === action.fromGroup.seq
|
(g) => g.seq === action.fromGroup.seq
|
||||||
);
|
);
|
||||||
|
|
||||||
const fromTrgtUserSeqs = fromGroup.userSeqs;
|
const fromTrgtUserSeqs = fromGroup.userSeqs;
|
||||||
|
@ -215,17 +229,17 @@ export class Effects {
|
||||||
groupSeq: action.fromGroup.seq,
|
groupSeq: action.fromGroup.seq,
|
||||||
groupName: action.fromGroup.name,
|
groupName: action.fromGroup.name,
|
||||||
userSeqs: fromTrgtUserSeqs.filter(
|
userSeqs: fromTrgtUserSeqs.filter(
|
||||||
trgtSeq => action.targetUserSeq.indexOf(trgtSeq) < 0
|
(trgtSeq) => action.targetUserSeq.indexOf(trgtSeq) < 0
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
map((resFrom: GroupUpdateResponse) => {
|
map((resFrom: GroupUpdateResponse) => {
|
||||||
return groups();
|
return groups();
|
||||||
}),
|
}),
|
||||||
catchError(error => of(moveFromFailure({ error })))
|
catchError((error) => of(moveFromFailure({ error })))
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
catchError(error => of(moveToFailure({ error })))
|
catchError((error) => of(moveToFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -246,21 +260,23 @@ export class Effects {
|
||||||
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
||||||
if (
|
if (
|
||||||
!!this.moduleConfig.useMyDeptGroup &&
|
!!this.moduleConfig.useMyDeptGroup &&
|
||||||
this.moduleConfig.useMyDeptGroup
|
this.moduleConfig.useMyDeptGroup &&
|
||||||
|
!!myDeptUserList &&
|
||||||
|
myDeptUserList.length > 0
|
||||||
) {
|
) {
|
||||||
userSeqsForDelete = targetUserSeqs.filter(
|
userSeqsForDelete = targetUserSeqs.filter(
|
||||||
delbuddy =>
|
(delbuddy) =>
|
||||||
myDeptUserList.filter(deptUser => deptUser.seq === delbuddy)
|
myDeptUserList.filter((deptUser) => deptUser.seq === delbuddy)
|
||||||
.length === 0
|
.length === 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
userSeqsForDelete = targetUserSeqs.filter(delbuddy => {
|
userSeqsForDelete = targetUserSeqs.filter((delbuddy) => {
|
||||||
let exist = false;
|
let exist = false;
|
||||||
for (const group of groupList) {
|
for (const group of groupList) {
|
||||||
if (
|
if (
|
||||||
group.seq !== action.group.seq &&
|
group.seq !== action.group.seq &&
|
||||||
group.userSeqs.filter(v => v === delbuddy).length > 0
|
group.userSeqs.filter((v) => v === delbuddy).length > 0
|
||||||
) {
|
) {
|
||||||
exist = true;
|
exist = true;
|
||||||
break;
|
break;
|
||||||
|
@ -271,7 +287,7 @@ export class Effects {
|
||||||
|
|
||||||
if (userSeqsForDelete.length > 0) {
|
if (userSeqsForDelete.length > 0) {
|
||||||
// 즐겨찾기 해제.
|
// 즐겨찾기 해제.
|
||||||
userSeqsForDelete.forEach(buddySeq => {
|
userSeqsForDelete.forEach((buddySeq) => {
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
buddyActions.update({
|
buddyActions.update({
|
||||||
req: {
|
req: {
|
||||||
|
@ -296,7 +312,7 @@ export class Effects {
|
||||||
map((res: GroupDelResponse) => {
|
map((res: GroupDelResponse) => {
|
||||||
return delSuccess({ res });
|
return delSuccess({ res });
|
||||||
}),
|
}),
|
||||||
catchError(error => of(delFailure({ error })))
|
catchError((error) => of(delFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -305,13 +321,13 @@ export class Effects {
|
||||||
update$ = createEffect(() =>
|
update$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(update),
|
ofType(update),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
concatMap(req =>
|
concatMap((req) =>
|
||||||
this.groupProtocolService.update2(req).pipe(
|
this.groupProtocolService.update2(req).pipe(
|
||||||
map((res: GroupUpdateResponse) => {
|
map((res: GroupUpdateResponse) => {
|
||||||
return groups();
|
return groups();
|
||||||
}),
|
}),
|
||||||
catchError(error => of(updateFailure({ error })))
|
catchError((error) => of(updateFailure({ error })))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,9 +6,13 @@
|
||||||
"umdModuleIds": {
|
"umdModuleIds": {
|
||||||
"@ngrx/store": "@ngrx/store",
|
"@ngrx/store": "@ngrx/store",
|
||||||
"@ngrx/effects": "@ngrx/effects",
|
"@ngrx/effects": "@ngrx/effects",
|
||||||
|
"@ngrx/entity": "@ngrx/entity",
|
||||||
|
|
||||||
"@ucap/protocol-query": "@ucap/protocol-query",
|
"@ucap/protocol-query": "@ucap/protocol-query",
|
||||||
|
"@ucap/protocol-status": "@ucap/protocol-status",
|
||||||
"@ucap/ng-api-external": "@ucap/ng-api-external",
|
"@ucap/ng-api-external": "@ucap/ng-api-external",
|
||||||
"@ucap/ng-protocol-query": "@ucap/ng-protocol-query",
|
"@ucap/ng-protocol-query": "@ucap/ng-protocol-query",
|
||||||
|
"@ucap/ng-protocol-status": "@ucap/ng-protocol-status",
|
||||||
"@ucap/ng-store-authentication": "@ucap/ng-store-authentication"
|
"@ucap/ng-store-authentication": "@ucap/ng-store-authentication"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-store-organization",
|
"name": "@ucap/ng-store-organization",
|
||||||
"version": "0.0.4",
|
"version": "0.0.8",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,9 +3,11 @@ import { Type } from '@angular/core';
|
||||||
import { Effects as CommonEffects } from './common/effects';
|
import { Effects as CommonEffects } from './common/effects';
|
||||||
import { Effects as CompanyEffects } from './company/effects';
|
import { Effects as CompanyEffects } from './company/effects';
|
||||||
import { Effects as DepartmentEffects } from './department/effects';
|
import { Effects as DepartmentEffects } from './department/effects';
|
||||||
|
import { Effects as PresenceEffects } from './presence/effects';
|
||||||
|
|
||||||
export const effects: Type<any>[] = [
|
export const effects: Type<any>[] = [
|
||||||
CommonEffects,
|
CommonEffects,
|
||||||
CompanyEffects,
|
CompanyEffects,
|
||||||
DepartmentEffects
|
DepartmentEffects,
|
||||||
|
PresenceEffects
|
||||||
];
|
];
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { createAction, props } from '@ngrx/store';
|
||||||
|
import {
|
||||||
|
BulkInfoRequest,
|
||||||
|
StatusBulkInfo,
|
||||||
|
StatusNotification,
|
||||||
|
StatusRequest,
|
||||||
|
StatusResponse,
|
||||||
|
MessageUpdateRequest
|
||||||
|
} from '@ucap/protocol-status';
|
||||||
|
|
||||||
|
export const bulkInfo = createAction(
|
||||||
|
'[ucap::organization::presence] Bulk Info',
|
||||||
|
props<BulkInfoRequest>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const bulkInfoSuccess = createAction(
|
||||||
|
'[ucap::organization::presence] Bulk Info Success',
|
||||||
|
props<{ statusBulkInfoList: StatusBulkInfo[] }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const bulkInfoFailure = createAction(
|
||||||
|
'[ucap::organization::presence] Bulk Info Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const statusNotification = createAction(
|
||||||
|
'[ucap::organization::presence] Status Notification',
|
||||||
|
props<{ noti: StatusNotification }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const status = createAction(
|
||||||
|
'[ucap::organization::presence] Status',
|
||||||
|
props<{ req: StatusRequest }>()
|
||||||
|
);
|
||||||
|
export const statusSuccess = createAction(
|
||||||
|
'[ucap::organization::presence] Status Success',
|
||||||
|
props<{
|
||||||
|
res: StatusResponse;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
export const statusFailure = createAction(
|
||||||
|
'[ucap::organization::presence] Status Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const changeMyIdleCheckTime = createAction(
|
||||||
|
'[ucap::organization::presence] Change MyIdleCheckTime',
|
||||||
|
props<{ checkTime: number }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const messageUpdate = createAction(
|
||||||
|
'[ucap::organization::presence] status message update of Others',
|
||||||
|
props<{ req: MessageUpdateRequest }>()
|
||||||
|
);
|
||||||
|
export const messageUpdateFailure = createAction(
|
||||||
|
'[ucap::organization::presence] status message update of Others Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { map, exhaustMap, catchError, tap, switchMap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
|
|
||||||
|
import {
|
||||||
|
status,
|
||||||
|
statusSuccess,
|
||||||
|
statusFailure,
|
||||||
|
bulkInfo,
|
||||||
|
bulkInfoSuccess,
|
||||||
|
bulkInfoFailure
|
||||||
|
} from './actions';
|
||||||
|
import { StatusProtocolService } from '@ucap/ng-protocol-status';
|
||||||
|
import { StatusBulkInfo } from '@ucap/protocol-status';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class Effects {
|
||||||
|
bulkInfo$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(bulkInfo),
|
||||||
|
switchMap((req) => {
|
||||||
|
return this.statusProtocolService.bulkInfo(req).pipe(
|
||||||
|
map((res) => {
|
||||||
|
this.store.dispatch(
|
||||||
|
bulkInfoSuccess({
|
||||||
|
statusBulkInfoList: res.statusBulkInfoList
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
catchError((error) => of(bulkInfoFailure({ error })))
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
status$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(status),
|
||||||
|
map((action) => action.req),
|
||||||
|
exhaustMap((req) => {
|
||||||
|
return this.statusProtocolService.status(req).pipe(
|
||||||
|
map((res) => {
|
||||||
|
return statusSuccess({ res });
|
||||||
|
}),
|
||||||
|
catchError((error) => of(statusFailure({ error })))
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private actions$: Actions,
|
||||||
|
private store: Store<any>,
|
||||||
|
private statusProtocolService: StatusProtocolService
|
||||||
|
) {}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
import { initialState, State, adapterStatusBulkInfo } from './state';
|
||||||
|
import {
|
||||||
|
bulkInfoSuccess,
|
||||||
|
statusNotification,
|
||||||
|
statusSuccess,
|
||||||
|
bulkInfo
|
||||||
|
} from './actions';
|
||||||
|
import {
|
||||||
|
StatusBulkInfo,
|
||||||
|
TerminalStatusInfo,
|
||||||
|
TerminalStatusNumber
|
||||||
|
} from '@ucap/protocol-status';
|
||||||
|
|
||||||
|
export const reducer = createReducer(
|
||||||
|
initialState,
|
||||||
|
on(bulkInfo, (state, action) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
statusBulkInfoProcessing: true
|
||||||
|
} as State;
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(bulkInfoSuccess, (state, action) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
statusBulkInfo: adapterStatusBulkInfo.upsertMany(
|
||||||
|
action.statusBulkInfoList,
|
||||||
|
{
|
||||||
|
...state.statusBulkInfo
|
||||||
|
}
|
||||||
|
),
|
||||||
|
statusBulkInfoProcessing: false
|
||||||
|
} as State;
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(statusNotification, (state, action) => {
|
||||||
|
const noti = action.noti;
|
||||||
|
|
||||||
|
const statusBulkInfoState: StatusBulkInfo = {
|
||||||
|
userSeq: noti.userSeq,
|
||||||
|
conferenceStatus: noti.conferenceStatus,
|
||||||
|
imessengerStatus: noti.imessengerStatus,
|
||||||
|
mobileConferenceStatus: noti.mobileConferenceStatus,
|
||||||
|
mobileStatus: noti.mobileStatus,
|
||||||
|
pcStatus: noti.pcStatus,
|
||||||
|
phoneStatus: noti.phoneStatus,
|
||||||
|
statusMessage: noti.statusMessage,
|
||||||
|
terminalStatus: TerminalStatusInfo.Unknown,
|
||||||
|
terminalStatusNumber: TerminalStatusNumber.Unknown,
|
||||||
|
workstatus: noti.workstatus
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
statusBulkInfo: adapterStatusBulkInfo.upsertOne(statusBulkInfoState, {
|
||||||
|
...state.statusBulkInfo
|
||||||
|
}),
|
||||||
|
statusBulkInfoProcessing: false
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(statusSuccess, (state, action) => {
|
||||||
|
const statusBulkInfoState: StatusBulkInfo = {
|
||||||
|
...state.statusBulkInfo.entities[action.res.SENDER_SEQ],
|
||||||
|
pcStatus: action.res.statusType,
|
||||||
|
statusMessage: action.res.statusMessage
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
statusBulkInfo: adapterStatusBulkInfo.updateOne(
|
||||||
|
{ id: action.res.SENDER_SEQ, changes: statusBulkInfoState },
|
||||||
|
{ ...state.statusBulkInfo }
|
||||||
|
),
|
||||||
|
statusBulkInfoProcessing: false
|
||||||
|
} as State;
|
||||||
|
})
|
||||||
|
);
|
56
projects/store-organization/src/lib/store/presence/state.ts
Normal file
56
projects/store-organization/src/lib/store/presence/state.ts
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { Selector, createSelector } from '@ngrx/store';
|
||||||
|
import { EntityState, createEntityAdapter } from '@ngrx/entity';
|
||||||
|
import { StatusBulkInfo } from '@ucap/protocol-status';
|
||||||
|
|
||||||
|
export interface StatusBulkInfoState extends EntityState<StatusBulkInfo> {}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
statusBulkInfo: StatusBulkInfoState;
|
||||||
|
statusBulkInfoProcessing: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const adapterStatusBulkInfo = createEntityAdapter<StatusBulkInfo>({
|
||||||
|
selectId: (statusBulkInfo) => statusBulkInfo.userSeq
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusBulkInfoInitialState: StatusBulkInfoState = adapterStatusBulkInfo.getInitialState(
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const initialState: State = {
|
||||||
|
statusBulkInfo: statusBulkInfoInitialState,
|
||||||
|
statusBulkInfoProcessing: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectAll: ngeSelectAllStatusBulkInfo,
|
||||||
|
selectEntities: ngeSelectEntitiesStatusBulkInfo
|
||||||
|
} = adapterStatusBulkInfo.getSelectors();
|
||||||
|
|
||||||
|
export function selectors<S>(selector: Selector<any, State>) {
|
||||||
|
const selectStatusBulkInfo = createSelector(
|
||||||
|
selector,
|
||||||
|
(state: State) => state.statusBulkInfo
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectAllStatusBulkInfo: createSelector(
|
||||||
|
selectStatusBulkInfo,
|
||||||
|
ngeSelectAllStatusBulkInfo
|
||||||
|
),
|
||||||
|
selectEntitiesStatusBulkInfo: createSelector(
|
||||||
|
selectStatusBulkInfo,
|
||||||
|
ngeSelectEntitiesStatusBulkInfo
|
||||||
|
),
|
||||||
|
selectStatusBulkInfo: (userSeq: number) =>
|
||||||
|
createSelector(
|
||||||
|
selectStatusBulkInfo,
|
||||||
|
ngeSelectEntitiesStatusBulkInfo,
|
||||||
|
(_, entities) => (!!entities ? entities[userSeq] : undefined)
|
||||||
|
),
|
||||||
|
statusBulkInfoProcessing: createSelector(
|
||||||
|
selector,
|
||||||
|
(state: State) => state.statusBulkInfoProcessing
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
|
@ -3,11 +3,13 @@ import { combineReducers, Action } from '@ngrx/store';
|
||||||
import { reducer as CommonReducer } from './common/reducers';
|
import { reducer as CommonReducer } from './common/reducers';
|
||||||
import { reducer as CompanyReducer } from './company/reducers';
|
import { reducer as CompanyReducer } from './company/reducers';
|
||||||
import { reducer as DepartmentReducer } from './department/reducers';
|
import { reducer as DepartmentReducer } from './department/reducers';
|
||||||
|
import { reducer as PresenceReducer } from './presence/reducers';
|
||||||
|
|
||||||
export function reducers(state: any | undefined, action: Action) {
|
export function reducers(state: any | undefined, action: Action) {
|
||||||
return combineReducers({
|
return combineReducers({
|
||||||
common: CommonReducer,
|
common: CommonReducer,
|
||||||
company: CompanyReducer,
|
company: CompanyReducer,
|
||||||
department: DepartmentReducer
|
department: DepartmentReducer,
|
||||||
|
presence: PresenceReducer
|
||||||
})(state, action);
|
})(state, action);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { createFeatureSelector, createSelector } from '@ngrx/store';
|
||||||
import * as CommonState from './common/state';
|
import * as CommonState from './common/state';
|
||||||
import * as CompanyState from './company/state';
|
import * as CompanyState from './company/state';
|
||||||
import * as DepartmentState from './department/state';
|
import * as DepartmentState from './department/state';
|
||||||
|
import * as PresenceState from './presence/state';
|
||||||
|
|
||||||
export const KEY_FEATURE = 'organization';
|
export const KEY_FEATURE = 'organization';
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ export interface State {
|
||||||
common: CommonState.State;
|
common: CommonState.State;
|
||||||
company: CompanyState.State;
|
company: CompanyState.State;
|
||||||
department: DepartmentState.State;
|
department: DepartmentState.State;
|
||||||
|
presence: PresenceState.State;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Selector = createFeatureSelector<State>(KEY_FEATURE);
|
export const Selector = createFeatureSelector<State>(KEY_FEATURE);
|
||||||
|
@ -25,3 +27,7 @@ export const CompanySelector = CompanyState.selectors(
|
||||||
export const DepartmentSelector = DepartmentState.selectors(
|
export const DepartmentSelector = DepartmentState.selectors(
|
||||||
createSelector(Selector, (state: State) => state.department)
|
createSelector(Selector, (state: State) => state.department)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const PresenceSelector = PresenceState.selectors(
|
||||||
|
createSelector(Selector, (state: State) => state.presence)
|
||||||
|
);
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
import * as CommonActions from './lib/store/common/actions';
|
import * as CommonActions from './lib/store/common/actions';
|
||||||
import * as CompanyActions from './lib/store/company/actions';
|
import * as CompanyActions from './lib/store/company/actions';
|
||||||
import * as DepartmentActions from './lib/store/department/actions';
|
import * as DepartmentActions from './lib/store/department/actions';
|
||||||
|
import * as PresenceActions from './lib/store/presence/actions';
|
||||||
|
|
||||||
export * from './lib/config/module-config';
|
export * from './lib/config/module-config';
|
||||||
|
|
||||||
export { CommonActions, CompanyActions, DepartmentActions };
|
export { CommonActions, CompanyActions, DepartmentActions, PresenceActions };
|
||||||
|
|
||||||
export * from './lib/store/state';
|
export * from './lib/store/state';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-ui-authentication",
|
"name": "@ucap/ng-ui-authentication",
|
||||||
"version": "0.0.19",
|
"version": "0.0.20",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
"@angular/core": "^9.0.2",
|
"@angular/core": "^9.0.2",
|
||||||
"@angular/material": "^9.0.0",
|
"@angular/material": "^9.0.0",
|
||||||
"@ucap/core": "~0.0.1",
|
"@ucap/core": "~0.0.1",
|
||||||
|
"@ucap/uc-scss": "~0.0.1",
|
||||||
"@ucap/ng-i18n": "~0.0.1",
|
"@ucap/ng-i18n": "~0.0.1",
|
||||||
"@ucap/ng-ui": "~0.0.1",
|
"@ucap/ng-ui": "~0.0.1",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
<div class="ucap-login-container">
|
<div class="ucap-authentication-login-container">
|
||||||
<ng-content select="[ucapAuthenticationLogin='header']"></ng-content>
|
<ng-content select="[ucapAuthenticationLogin='header']"></ng-content>
|
||||||
|
|
||||||
<form name="loginForm" [formGroup]="loginForm" novalidate>
|
<form name="loginForm" [formGroup]="loginForm" novalidate>
|
||||||
<mat-form-field
|
<div
|
||||||
|
class="ucap-authentication-login-input-field"
|
||||||
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
||||||
class="login-company"
|
|
||||||
>
|
>
|
||||||
<mat-label>{{ 'login.fields.company' | ucapI18n }}</mat-label>
|
<mat-form-field>
|
||||||
<mat-select
|
<mat-label>{{ 'login.fields.company' | ucapI18n }}</mat-label>
|
||||||
[formControl]="companyCodeFormControl"
|
<mat-select
|
||||||
[value]="companyCode || fixedCompanyCode"
|
[formControl]="companyCodeFormControl"
|
||||||
placeholder="{{ 'login.labels.selectCompany' | ucapI18n }}"
|
[value]="companyCode || fixedCompanyCode"
|
||||||
>
|
placeholder="{{ 'login.labels.selectCompany' | ucapI18n }}"
|
||||||
<mat-option
|
|
||||||
*ngFor="let company of companyList"
|
|
||||||
[value]="company.companyCode"
|
|
||||||
>
|
>
|
||||||
{{ company.companyName }}
|
<mat-option
|
||||||
</mat-option>
|
*ngFor="let company of companyList"
|
||||||
</mat-select>
|
[value]="company.companyCode"
|
||||||
</mat-form-field>
|
>
|
||||||
|
{{ company.companyName }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="input-lineless">
|
<div class="ucap-authentication-login-input-field">
|
||||||
<!--<span class="icon-img"><i class="mid mdi-account-tie-outline"></i></span>-->
|
<!--<span class="input-icon"><i class="mid mdi-account-tie-outline"></i></span>-->
|
||||||
<span class="icon-img">
|
<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 20.82 22">
|
||||||
<path
|
<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"
|
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"
|
||||||
|
@ -36,15 +38,15 @@
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<mat-form-field class="login-id">
|
<mat-form-field>
|
||||||
<mat-label>{{ 'login.fields.loginId' | ucapI18n }}</mat-label>
|
<mat-label>{{ 'login.fields.loginId' | ucapI18n }}</mat-label>
|
||||||
<input matInput [formControl]="loginIdFormControl" />
|
<input matInput [formControl]="loginIdFormControl" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-lineless">
|
<div class="ucap-authentication-login-input-field">
|
||||||
<!--<span class="icon-img"><i class="mid mdi-lock-outline"></i></span>-->
|
<!--<span class="input-icon"><i class="mid mdi-lock-outline"></i></span>-->
|
||||||
<span class="icon-img">
|
<span class="ucap-authentication-login-input-icon">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22.07">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22.07">
|
||||||
<title>login-lock</title>
|
<title>login-lock</title>
|
||||||
<path
|
<path
|
||||||
|
@ -71,7 +73,7 @@
|
||||||
<rect x="9.08" y="12.86" width="1.84" height="3.72" />
|
<rect x="9.08" y="12.86" width="1.84" height="3.72" />
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<mat-form-field class="login-pw">
|
<mat-form-field>
|
||||||
<mat-label>{{ 'login.fields.loginPw' | ucapI18n }}</mat-label>
|
<mat-label>{{ 'login.fields.loginPw' | ucapI18n }}</mat-label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
|
@ -82,13 +84,14 @@
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="error-container">
|
<div class="ucap-authentication-login-error-container">
|
||||||
<mat-error
|
<mat-error
|
||||||
*ngIf="
|
*ngIf="
|
||||||
companyCodeFormControl.dirty &&
|
companyCodeFormControl.dirty &&
|
||||||
companyCodeFormControl.invalid &&
|
companyCodeFormControl.invalid &&
|
||||||
companyCodeFormControl.hasError('required')
|
companyCodeFormControl.hasError('required')
|
||||||
"
|
"
|
||||||
|
class="ucap-authentication-login-error"
|
||||||
>
|
>
|
||||||
{{ 'login.errors.requireCompany' | ucapI18n }}
|
{{ 'login.errors.requireCompany' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
@ -98,6 +101,7 @@
|
||||||
loginIdFormControl.invalid &&
|
loginIdFormControl.invalid &&
|
||||||
loginIdFormControl.hasError('required')
|
loginIdFormControl.hasError('required')
|
||||||
"
|
"
|
||||||
|
class="ucap-authentication-login-error"
|
||||||
>
|
>
|
||||||
{{ 'login.errors.requireLoginId' | ucapI18n }}
|
{{ 'login.errors.requireLoginId' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
|
@ -107,17 +111,18 @@
|
||||||
loginPwFormControl.invalid &&
|
loginPwFormControl.invalid &&
|
||||||
loginPwFormControl.hasError('required')
|
loginPwFormControl.hasError('required')
|
||||||
"
|
"
|
||||||
|
class="ucap-authentication-login-error"
|
||||||
>
|
>
|
||||||
{{ 'login.errors.requireLoginPw' | ucapI18n }}
|
{{ 'login.errors.requireLoginPw' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-error *ngIf="loginFailed">
|
<mat-error *ngIf="loginFailed" class="ucap-authentication-login-error">
|
||||||
{{ 'login.errors.failed' | ucapI18n }}
|
{{ 'login.errors.failed' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
mat-raised-button
|
mat-raised-button
|
||||||
class="submit-button bg-accent-dark"
|
class="ucap-authentication-login-button-submit"
|
||||||
aria-label="LOG IN"
|
aria-label="LOG IN"
|
||||||
[disabled]="
|
[disabled]="
|
||||||
loginForm.invalid ||
|
loginForm.invalid ||
|
||||||
|
|
|
@ -1,67 +1,30 @@
|
||||||
$desktop-l-width: 1440px;
|
@import '~@ucap/ui-scss/ucap';
|
||||||
$tablet-l-width: 1024px;
|
|
||||||
$tablet-s-width: 768px;
|
|
||||||
$mob-l-width: 640px;
|
|
||||||
$login-max-height: 800px;
|
|
||||||
|
|
||||||
@mixin desktop-m {
|
.ucap-authentication-login-container {
|
||||||
@media screen and (max-width: #{$desktop-l-width}) {
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 태블릿
|
|
||||||
@mixin tab {
|
|
||||||
@media screen and (max-width: #{$tablet-l-width}) {
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 모바일 large
|
|
||||||
@mixin mob-l {
|
|
||||||
@media screen and (max-width: #{$mob-l-width}) {
|
|
||||||
@content;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ucap-login-container {
|
|
||||||
position: relative;
|
|
||||||
transform: scale(1);
|
|
||||||
width: 100%;
|
|
||||||
min-width: 100%;
|
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
text-align: center;
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
background-color: rgba(255, 255, 255, 1);
|
background-color: rgba(255, 255, 255, 1);
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
|
||||||
.mat-title {
|
text-align: center;
|
||||||
margin: 10px 0 10px 0;
|
|
||||||
text-indent: -10000000px;
|
|
||||||
width: 120px;
|
|
||||||
height: 120px;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
display: inline-flex;
|
|
||||||
background-size: 100% auto;
|
|
||||||
flex: 1 1 auto;
|
|
||||||
background-position-x: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
form {
|
form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
|
|
||||||
.input-lineless {
|
.ucap-authentication-login-input-field {
|
||||||
background-color: #e0e0e0;
|
background-color: #e0e0e0;
|
||||||
padding: 14px 20px;
|
padding: 14px 20px;
|
||||||
margin-top: 6px;
|
margin-top: 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row;
|
flex-flow: row;
|
||||||
@media screen and (max-width: #{$tablet-s-width}) {
|
|
||||||
padding: 8px 20px;
|
.ucap-authentication-login-input-icon {
|
||||||
}
|
|
||||||
.icon-img {
|
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
@ -74,33 +37,15 @@ $login-max-height: 800px;
|
||||||
mat-form-field {
|
mat-form-field {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
&.login-pw {
|
|
||||||
//margin-top: 10px;
|
|
||||||
@media screen and (max-width: #{$tablet-s-width}) {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mat-checkbox {
|
.ucap-authentication-login-error-container {
|
||||||
margin: 0;
|
.ucap-authentication-login-error {
|
||||||
}
|
|
||||||
|
|
||||||
.remember-forgot-password {
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
.remember-me {
|
|
||||||
}
|
|
||||||
|
|
||||||
.auto-login {
|
|
||||||
font-size: 0.9em;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 2vh;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.submit-button {
|
.ucap-authentication-login-button-submit {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
margin: 20px 0 10px;
|
margin: 20px 0 10px;
|
||||||
|
@ -108,115 +53,6 @@ $login-max-height: 800px;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
font-size: 1.1em;
|
font-size: 1.1em;
|
||||||
@media screen and (max-width: #{$tablet-s-width}) {
|
|
||||||
line-height: 38px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.register {
|
|
||||||
margin: 32px auto 24px auto;
|
|
||||||
font-weight: 600;
|
|
||||||
|
|
||||||
.text {
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
font-size: 15px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin: 24px auto;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100px;
|
|
||||||
|
|
||||||
.text {
|
|
||||||
display: inline-flex;
|
|
||||||
position: relative;
|
|
||||||
padding: 0 8px;
|
|
||||||
z-index: 9999;
|
|
||||||
|
|
||||||
&:before,
|
|
||||||
&:after {
|
|
||||||
content: '';
|
|
||||||
display: block;
|
|
||||||
width: 30px;
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
border-top: 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:before {
|
|
||||||
right: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:after {
|
|
||||||
left: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.policy {
|
|
||||||
cursor: pointer;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 2vh;
|
|
||||||
display: block;
|
|
||||||
background-color: #58b0b1;
|
|
||||||
width: 100%;
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 0.86em;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
align-items: center;
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.icon-img {
|
|
||||||
padding: 12px 10px;
|
|
||||||
width: 50px;
|
|
||||||
color: #ffffff;
|
|
||||||
padding-right: 10px;
|
|
||||||
border-right: 1px solid rgba(255, 255, 255, 0.7);
|
|
||||||
flex: 0 0 auto;
|
|
||||||
@media screen and (max-width: #{$tablet-s-width}) {
|
|
||||||
padding: 9px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
&.google,
|
|
||||||
&.facebook {
|
|
||||||
width: 192px;
|
|
||||||
text-transform: none;
|
|
||||||
color: #ffffff;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@include tab {
|
|
||||||
.ucap-login-container {
|
|
||||||
font-size: 0.9em;
|
|
||||||
.mat-title {
|
|
||||||
background-size: 90% auto;
|
|
||||||
}
|
|
||||||
.mat-form-field .mat-form-field-infix input {
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
.policy {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.9em;
|
|
||||||
padding: 0px;
|
|
||||||
line-height: 3em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,47 +2,23 @@ import { moduleMetadata } from '@storybook/angular';
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { linkTo } from '@storybook/addon-links';
|
import { linkTo } from '@storybook/addon-links';
|
||||||
|
|
||||||
import { AuthenticationUiModule } from '../authentication-ui.module';
|
|
||||||
import { LoginComponent } from './login.component';
|
|
||||||
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
|
|
||||||
import { ChangeDetectorRef } from '@angular/core';
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { CommonModule } from '@angular/common';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
|
||||||
import { MatInputModule } from '@angular/material/input';
|
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
|
||||||
import { I18nService, UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n';
|
|
||||||
import { LogService } from '@ucap/logger';
|
import { LogService } from '@ucap/logger';
|
||||||
import { Company } from '@ucap/api-external';
|
import { Company } from '@ucap/api-external';
|
||||||
|
|
||||||
|
import { I18nService, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||||
|
|
||||||
|
import { AuthenticationUiModule } from '../authentication-ui.module';
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'LoginComponent',
|
title: 'LoginComponent',
|
||||||
decorators: [
|
decorators: [
|
||||||
moduleMetadata({
|
moduleMetadata({
|
||||||
imports: [
|
imports: [BrowserModule, BrowserAnimationsModule, AuthenticationUiModule],
|
||||||
BrowserModule,
|
|
||||||
BrowserAnimationsModule,
|
|
||||||
|
|
||||||
CommonModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
|
|
||||||
MatButtonModule,
|
|
||||||
MatCheckboxModule,
|
|
||||||
MatFormFieldModule,
|
|
||||||
MatIconModule,
|
|
||||||
MatInputModule,
|
|
||||||
MatProgressSpinnerModule,
|
|
||||||
MatSelectModule,
|
|
||||||
|
|
||||||
I18nModule
|
|
||||||
],
|
|
||||||
providers: [
|
providers: [
|
||||||
AuthenticationUiModule,
|
|
||||||
{ provide: I18nService, useValue: new I18nService(new LogService({})) },
|
{ provide: I18nService, useValue: new I18nService(new LogService({})) },
|
||||||
{
|
{
|
||||||
provide: UCAP_I18N_NAMESPACE,
|
provide: UCAP_I18N_NAMESPACE,
|
||||||
|
|
25
projects/ui-chat/README.md
Normal file
25
projects/ui-chat/README.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# UiChat
|
||||||
|
|
||||||
|
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.2.
|
||||||
|
|
||||||
|
## Code scaffolding
|
||||||
|
|
||||||
|
Run `ng generate component component-name --project ui-chat` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project ui-chat`.
|
||||||
|
|
||||||
|
> Note: Don't forget to add `--project ui-chat` or else it will be added to the default project in your `angular.json` file.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Run `ng build ui-chat` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||||
|
|
||||||
|
## Publishing
|
||||||
|
|
||||||
|
After building your library with `ng build ui-chat`, go to the dist folder `cd dist/ui-chat` and run `npm publish`.
|
||||||
|
|
||||||
|
## Running unit tests
|
||||||
|
|
||||||
|
Run `ng test ui-chat` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||||
|
|
||||||
|
## Further help
|
||||||
|
|
||||||
|
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
32
projects/ui-chat/karma.conf.js
Normal file
32
projects/ui-chat/karma.conf.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
// Karma configuration file, see link for more information
|
||||||
|
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||||
|
|
||||||
|
module.exports = function (config) {
|
||||||
|
config.set({
|
||||||
|
basePath: '',
|
||||||
|
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||||
|
plugins: [
|
||||||
|
require('karma-jasmine'),
|
||||||
|
require('karma-chrome-launcher'),
|
||||||
|
require('karma-jasmine-html-reporter'),
|
||||||
|
require('karma-coverage-istanbul-reporter'),
|
||||||
|
require('@angular-devkit/build-angular/plugins/karma')
|
||||||
|
],
|
||||||
|
client: {
|
||||||
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||||
|
},
|
||||||
|
coverageIstanbulReporter: {
|
||||||
|
dir: require('path').join(__dirname, '../../coverage/ui-organization'),
|
||||||
|
reports: ['html', 'lcovonly', 'text-summary'],
|
||||||
|
fixWebpackSourcePaths: true
|
||||||
|
},
|
||||||
|
reporters: ['progress', 'kjhtml'],
|
||||||
|
port: 9876,
|
||||||
|
colors: true,
|
||||||
|
logLevel: config.LOG_INFO,
|
||||||
|
autoWatch: true,
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
singleRun: false,
|
||||||
|
restartOnFileChange: true
|
||||||
|
});
|
||||||
|
};
|
14
projects/ui-chat/ng-package.json
Normal file
14
projects/ui-chat/ng-package.json
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||||
|
"dest": "../../dist/ui-chat",
|
||||||
|
"lib": {
|
||||||
|
"entryFile": "src/public-api.ts",
|
||||||
|
"umdModuleIds": {
|
||||||
|
"ngx-perfect-scrollbar": "ngx-perfect-scrollbar",
|
||||||
|
"@ucap/core": "@ucap/core",
|
||||||
|
"@ucap/protocol-room": "@ucap/protocol-room",
|
||||||
|
"@ucap/ng-logger": "@ucap/ng-logger",
|
||||||
|
"@ucap/ng-ui": "@ucap/ng-ui"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
projects/ui-chat/package.json
Normal file
18
projects/ui-chat/package.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "@ucap/ng-ui-chat",
|
||||||
|
"version": "0.0.3",
|
||||||
|
"publishConfig": {
|
||||||
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/cdk": "^9.0.0",
|
||||||
|
"@angular/common": "^9.0.2",
|
||||||
|
"@angular/core": "^9.0.2",
|
||||||
|
"@angular/material": "^9.0.0",
|
||||||
|
"@ucap/core": "~0.0.1",
|
||||||
|
"@ucap/protocol-room": "~0.0.1",
|
||||||
|
"@ucap/uc-scss": "~0.0.1",
|
||||||
|
"@ucap/ng-ui": "~0.0.1",
|
||||||
|
"tslib": "^1.10.0"
|
||||||
|
}
|
||||||
|
}
|
83
projects/ui-chat/src/lib/chat-ui.module.ts
Normal file
83
projects/ui-chat/src/lib/chat-ui.module.ts
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
|
import { MatBadgeModule } from '@angular/material/badge';
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatRippleModule } from '@angular/material/core';
|
||||||
|
import { MatTreeModule } from '@angular/material/tree';
|
||||||
|
|
||||||
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
|
import { UiModule } from '@ucap/ng-ui';
|
||||||
|
|
||||||
|
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||||
|
|
||||||
|
import { ModuleConfig } from './config/module-config';
|
||||||
|
import { _MODULE_CONFIG } from './config/token';
|
||||||
|
|
||||||
|
import {
|
||||||
|
RoomExpansionComponent,
|
||||||
|
RoomExpansionHeaderDirective,
|
||||||
|
RoomExpansionNodeDirective
|
||||||
|
} from './components/room-expansion.component';
|
||||||
|
import { RoomListItem01Component } from './components/room-list-item-01.component';
|
||||||
|
|
||||||
|
const COMPONENTS = [RoomExpansionComponent, RoomListItem01Component];
|
||||||
|
const DIALOGS = [];
|
||||||
|
const PIPES = [];
|
||||||
|
const DIRECTIVES = [RoomExpansionHeaderDirective, RoomExpansionNodeDirective];
|
||||||
|
const SERVICES = [];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [],
|
||||||
|
imports: [],
|
||||||
|
exports: []
|
||||||
|
})
|
||||||
|
export class ChatUiRootModule {}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
ScrollingModule,
|
||||||
|
|
||||||
|
MatBadgeModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatCheckboxModule,
|
||||||
|
MatIconModule,
|
||||||
|
MatRippleModule,
|
||||||
|
MatTreeModule,
|
||||||
|
MatMenuModule,
|
||||||
|
|
||||||
|
PerfectScrollbarModule,
|
||||||
|
|
||||||
|
I18nModule,
|
||||||
|
UiModule
|
||||||
|
],
|
||||||
|
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
||||||
|
declarations: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
||||||
|
entryComponents: [...DIALOGS],
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: UCAP_I18N_NAMESPACE,
|
||||||
|
useValue: ['chat']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ChatUiModule {
|
||||||
|
public static forRoot(
|
||||||
|
config: ModuleConfig
|
||||||
|
): ModuleWithProviders<ChatUiRootModule> {
|
||||||
|
return {
|
||||||
|
ngModule: ChatUiRootModule,
|
||||||
|
providers: [{ provide: _MODULE_CONFIG, useValue: config }, ...SERVICES]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<div class="ucap-chat-room-expansion-container" fxFlexFill>
|
||||||
|
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||||
|
<ng-container
|
||||||
|
*cdkVirtualFor="
|
||||||
|
let node of dataSource.expandedData$;
|
||||||
|
templateCacheSize: 0
|
||||||
|
"
|
||||||
|
></ng-container>
|
||||||
|
|
||||||
|
<mat-tree #treeList [dataSource]="dataSource" [treeControl]="treeControl">
|
||||||
|
<mat-tree-node
|
||||||
|
*matTreeNodeDef="let node"
|
||||||
|
[attr.node-type]="node?.nodeType"
|
||||||
|
matRipple
|
||||||
|
>
|
||||||
|
<li>
|
||||||
|
<div>
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="nodeTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: node?.node }"
|
||||||
|
></ng-container>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</mat-tree-node>
|
||||||
|
|
||||||
|
<mat-tree-node
|
||||||
|
*matTreeNodeDef="let node; when: isHeader"
|
||||||
|
class="tree-node-frame ucap-clickable"
|
||||||
|
[attr.node-type]="node?.nodeType"
|
||||||
|
matRipple
|
||||||
|
>
|
||||||
|
<li class="tree-node-header" matTreeNodeToggle>
|
||||||
|
<div class="path">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
[attr.aria-label]="'toggle '"
|
||||||
|
class="btn-toggle"
|
||||||
|
>
|
||||||
|
<mat-icon class="mat-icon-rtl-mirror">
|
||||||
|
{{
|
||||||
|
treeControl.isExpanded(node) ? 'expand_less' : 'expand_more'
|
||||||
|
}}
|
||||||
|
</mat-icon>
|
||||||
|
</button>
|
||||||
|
<div class="group-info">
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="headerTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: node?.node }"
|
||||||
|
>
|
||||||
|
</ng-container>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ul [class.group-tree-node-invisible]="!treeControl.isExpanded(node)">
|
||||||
|
<div *ngIf="treeControl.isExpanded(node)" class="boxnone">
|
||||||
|
<div class="vertical-line"></div>
|
||||||
|
<ng-container matTreeNodeOutlet></ng-container>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</mat-tree-node>
|
||||||
|
</mat-tree>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
</div>
|
|
@ -0,0 +1,2 @@
|
||||||
|
.ucap-chat-room-expansion-container {
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { RoomExpansionComponent } from './room-expansion.component';
|
||||||
|
|
||||||
|
describe('ucap::chat::RoomExpansionComponent', () => {
|
||||||
|
let component: RoomExpansionComponent;
|
||||||
|
let fixture: ComponentFixture<RoomExpansionComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [RoomExpansionComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RoomExpansionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
181
projects/ui-chat/src/lib/components/room-expansion.component.ts
Normal file
181
projects/ui-chat/src/lib/components/room-expansion.component.ts
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
Input,
|
||||||
|
ViewChild,
|
||||||
|
ContentChild,
|
||||||
|
TemplateRef,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Directive
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
import { FlatTreeControl } from '@angular/cdk/tree';
|
||||||
|
|
||||||
|
import { MatTreeFlattener, MatTree } from '@angular/material/tree';
|
||||||
|
|
||||||
|
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
|
import { RoomInfo } from '@ucap/protocol-room';
|
||||||
|
|
||||||
|
import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
||||||
|
|
||||||
|
export interface RoomNode {
|
||||||
|
nodeType: string;
|
||||||
|
roomInfo?: RoomInfo;
|
||||||
|
children?: RoomNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FlatNode {
|
||||||
|
expandable: boolean;
|
||||||
|
level: number;
|
||||||
|
node: RoomNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapChatRoomExpansionNode]'
|
||||||
|
})
|
||||||
|
export class RoomExpansionNodeDirective {}
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapChatRoomExpansionHeader]'
|
||||||
|
})
|
||||||
|
export class RoomExpansionHeaderDirective {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-room-expansion',
|
||||||
|
templateUrl: './room-expansion.component.html',
|
||||||
|
styleUrls: ['./room-expansion.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class RoomExpansionComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
set roomGroup(list: { division: string; roomList: RoomInfo[] }[]) {
|
||||||
|
if (!list || 0 === list.length) {
|
||||||
|
} else {
|
||||||
|
list.sort((a, b) =>
|
||||||
|
a.division < b.division ? 1 : a.division > b.division ? -1 : 0
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const item of list) {
|
||||||
|
const nodeType = item.division;
|
||||||
|
const node: RoomNode = {
|
||||||
|
nodeType,
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
item.roomList.sort((a, b) =>
|
||||||
|
a.finalEventDate < b.finalEventDate
|
||||||
|
? 1
|
||||||
|
: a.finalEventDate > b.finalEventDate
|
||||||
|
? -1
|
||||||
|
: 0
|
||||||
|
);
|
||||||
|
|
||||||
|
item.roomList.forEach((roomInfo) => {
|
||||||
|
node.children.push({
|
||||||
|
nodeType,
|
||||||
|
roomInfo
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!!this.nodeMap.get(item.division)) {
|
||||||
|
this.nodeMap.get(item.division)[0].children = node.children;
|
||||||
|
} else {
|
||||||
|
this.nodeMap.set(item.division, [node]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.refreshNodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild('treeList', { static: false })
|
||||||
|
treeList: MatTree<FlatNode>;
|
||||||
|
|
||||||
|
@ViewChild('cvsvList', { static: false })
|
||||||
|
cvsvList: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
|
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||||
|
psDirectiveRef?: PerfectScrollbarDirective;
|
||||||
|
|
||||||
|
@ContentChild(RoomExpansionNodeDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
nodeTemplate: TemplateRef<RoomExpansionNodeDirective>;
|
||||||
|
|
||||||
|
@ContentChild(RoomExpansionHeaderDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
headerTemplate: TemplateRef<RoomExpansionHeaderDirective>;
|
||||||
|
|
||||||
|
treeControl: FlatTreeControl<FlatNode>;
|
||||||
|
treeFlattener: MatTreeFlattener<RoomNode, FlatNode>;
|
||||||
|
dataSource: VirtualScrollTreeFlatDataSource<RoomNode, FlatNode>;
|
||||||
|
|
||||||
|
private nodeMap: Map<string, RoomNode[]> = new Map();
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
||||||
|
this.treeControl = new FlatTreeControl<FlatNode>(
|
||||||
|
(node) => node.level,
|
||||||
|
(node) => node.expandable
|
||||||
|
);
|
||||||
|
|
||||||
|
this.treeFlattener = new MatTreeFlattener<RoomNode, FlatNode>(
|
||||||
|
(node: RoomNode, level: number) => {
|
||||||
|
return {
|
||||||
|
expandable: !!node.children && node.children.length > 0,
|
||||||
|
level,
|
||||||
|
nodeType: node.nodeType,
|
||||||
|
node
|
||||||
|
};
|
||||||
|
},
|
||||||
|
(node) => node.level,
|
||||||
|
(node) => node.expandable,
|
||||||
|
(node) => node.children
|
||||||
|
);
|
||||||
|
|
||||||
|
this.dataSource = new VirtualScrollTreeFlatDataSource<RoomNode, FlatNode>(
|
||||||
|
this.treeControl,
|
||||||
|
this.treeFlattener
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this._ngOnDestroySubject = new Subject();
|
||||||
|
|
||||||
|
this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
|
||||||
|
this.treeControl.expansionModel.changed
|
||||||
|
.pipe(takeUntil(this._ngOnDestroySubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.cvsvList.checkViewportSize();
|
||||||
|
this.psDirectiveRef.update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this._ngOnDestroySubject) {
|
||||||
|
this._ngOnDestroySubject.next();
|
||||||
|
this._ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isHeader = (_: number, node: FlatNode) => 0 === node.level;
|
||||||
|
|
||||||
|
private refreshNodes() {
|
||||||
|
const rootNode: RoomNode[] = [];
|
||||||
|
this.nodeMap.forEach((node) => rootNode.push(...node));
|
||||||
|
|
||||||
|
this.dataSource.data = rootNode;
|
||||||
|
this.treeControl.expandAll();
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
<div class="ucap-chat-room-list-item1">
|
||||||
|
<div class="ucap-chat-room-list-item1-profile-image">
|
||||||
|
<img
|
||||||
|
ucapImage
|
||||||
|
[base]="profileImageRoot"
|
||||||
|
[path]="profileImage"
|
||||||
|
[default]="defaultProfileImage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="ucap-chat-room-list-item1-info">
|
||||||
|
<div class="roomName">
|
||||||
|
{{ roomName }}
|
||||||
|
<strong *ngIf="roomInfo.roomType === RoomType.Multi"
|
||||||
|
>({{ roomInfo.joinUserCount }})</strong
|
||||||
|
>
|
||||||
|
/ {{ roomInfo.receiveAlarm }}
|
||||||
|
</div>
|
||||||
|
<div class="lastMessage">{{ roomInfo.finalEventMessage }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="date">{{ roomInfo.finalEventDate | ucapDate: 'LT' }}</div>
|
||||||
|
|
||||||
|
<span
|
||||||
|
class="noti-sum"
|
||||||
|
*ngIf="!!roomInfo.noReadCnt && roomInfo.noReadCnt > 0"
|
||||||
|
[matBadgeHidden]="roomInfo.noReadCnt === 0"
|
||||||
|
[matBadge]="roomInfo.noReadCnt"
|
||||||
|
matBadgeOverlap="true"
|
||||||
|
matBadgeColor="accent"
|
||||||
|
matBadgePosition="below after"
|
||||||
|
></span>
|
||||||
|
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="room-menu"
|
||||||
|
*ngIf="!checkable"
|
||||||
|
[matMenuTriggerFor]="roomMenu"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
>
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-checkbox
|
||||||
|
*ngIf="checkable"
|
||||||
|
#checkbox
|
||||||
|
[checked]="checked"
|
||||||
|
(change)="onToggleItem(checkbox.checked)"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
class="group-check"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-menu #roomMenu="matMenu">
|
||||||
|
<span class="manu-title">{{ roomName }}</span>
|
||||||
|
<button mat-menu-item (click)="onOpenChatRoom()">
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>{{ 'room.openRoom' | ucapI18n }}</span>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item (click)="onToggleAlarm()">
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>
|
||||||
|
{{
|
||||||
|
(roomInfo.receiveAlarm
|
||||||
|
? 'room.turnOffRoomAlert'
|
||||||
|
: 'room.turnOnRoomAlert'
|
||||||
|
) | ucapI18n
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item (click)="onDelRoom()">
|
||||||
|
<mat-icon>dialpad</mat-icon>
|
||||||
|
<span>{{ 'room.exitFromRoom' | ucapI18n }}</span>
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
|
@ -0,0 +1,7 @@
|
||||||
|
.ucap-chat-room-list-item1 {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.ucap-chat-room-list-item1-profile-image {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { RoomListItem01Component } from './room-list-item-01.component';
|
||||||
|
|
||||||
|
describe('ucap::chat::RoomListItem01Component', () => {
|
||||||
|
let component: RoomListItem01Component;
|
||||||
|
let fixture: ComponentFixture<RoomListItem01Component>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [RoomListItem01Component]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RoomListItem01Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,84 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
Input,
|
||||||
|
OnDestroy,
|
||||||
|
EventEmitter,
|
||||||
|
Output,
|
||||||
|
ChangeDetectorRef
|
||||||
|
} from '@angular/core';
|
||||||
|
import { RoomInfo, RoomType } from '@ucap/protocol-room';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-room-list-item-01',
|
||||||
|
templateUrl: './room-list-item-01.component.html',
|
||||||
|
styleUrls: ['./room-list-item-01.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class RoomListItem01Component implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImageRoot: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
defaultProfileImage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
roomName: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checkable = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checked = false;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
toggleItem = new EventEmitter<{
|
||||||
|
checked: boolean;
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openChatRoom = new EventEmitter<RoomInfo>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
toggleAlarm = new EventEmitter<RoomInfo>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
delRoom = new EventEmitter<RoomInfo>();
|
||||||
|
|
||||||
|
RoomType = RoomType;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {}
|
||||||
|
|
||||||
|
onToggleItem(value: boolean): void {
|
||||||
|
this.toggleItem.emit({
|
||||||
|
checked: value,
|
||||||
|
roomInfo: this.roomInfo
|
||||||
|
});
|
||||||
|
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpenChatRoom(): void {
|
||||||
|
this.openChatRoom.emit(this.roomInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggleAlarm(): void {
|
||||||
|
this.toggleAlarm.emit(this.roomInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
onDelRoom(): void {
|
||||||
|
this.delRoom.emit(this.roomInfo);
|
||||||
|
}
|
||||||
|
}
|
4
projects/ui-chat/src/lib/config/module-config.ts
Normal file
4
projects/ui-chat/src/lib/config/module-config.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { ModuleConfig as CoreModuleConfig } from '@ucap/core';
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-empty-interface
|
||||||
|
export interface ModuleConfig extends CoreModuleConfig {}
|
5
projects/ui-chat/src/lib/config/token.ts
Normal file
5
projects/ui-chat/src/lib/config/token.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
|
||||||
|
export const _MODULE_CONFIG = new InjectionToken(
|
||||||
|
'@ucap/ui-chat config of module'
|
||||||
|
);
|
10
projects/ui-chat/src/public-api.ts
Normal file
10
projects/ui-chat/src/public-api.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Public API Surface of ui-chat
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './lib/config/module-config';
|
||||||
|
|
||||||
|
export * from './lib/components/room-expansion.component';
|
||||||
|
export * from './lib/components/room-list-item-01.component';
|
||||||
|
|
||||||
|
export * from './lib/chat-ui.module';
|
26
projects/ui-chat/src/test.ts
Normal file
26
projects/ui-chat/src/test.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||||
|
|
||||||
|
import 'zone.js/dist/zone';
|
||||||
|
import 'zone.js/dist/zone-testing';
|
||||||
|
import { getTestBed } from '@angular/core/testing';
|
||||||
|
import {
|
||||||
|
BrowserDynamicTestingModule,
|
||||||
|
platformBrowserDynamicTesting
|
||||||
|
} from '@angular/platform-browser-dynamic/testing';
|
||||||
|
|
||||||
|
declare const require: {
|
||||||
|
context(path: string, deep?: boolean, filter?: RegExp): {
|
||||||
|
keys(): string[];
|
||||||
|
<T>(id: string): T;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// First, initialize the Angular testing environment.
|
||||||
|
getTestBed().initTestEnvironment(
|
||||||
|
BrowserDynamicTestingModule,
|
||||||
|
platformBrowserDynamicTesting()
|
||||||
|
);
|
||||||
|
// Then we find all the tests.
|
||||||
|
const context = require.context('./', true, /\.spec\.ts$/);
|
||||||
|
// And load the modules.
|
||||||
|
context.keys().map(context);
|
23
projects/ui-chat/tsconfig.lib.json
Normal file
23
projects/ui-chat/tsconfig.lib.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/lib",
|
||||||
|
"target": "es2015",
|
||||||
|
"declaration": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"types": [],
|
||||||
|
"lib": [
|
||||||
|
"dom",
|
||||||
|
"es2018"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
|
"strictMetadataEmit": true,
|
||||||
|
"enableResourceInlining": true
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"src/test.ts",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
6
projects/ui-chat/tsconfig.lib.prod.json
Normal file
6
projects/ui-chat/tsconfig.lib.prod.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"extends": "./tsconfig.lib.json",
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableIvy": false
|
||||||
|
}
|
||||||
|
}
|
17
projects/ui-chat/tsconfig.spec.json
Normal file
17
projects/ui-chat/tsconfig.spec.json
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../out-tsc/spec",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"src/test.ts"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"**/*.spec.ts",
|
||||||
|
"**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
7
projects/ui-chat/tslint.json
Normal file
7
projects/ui-chat/tslint.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tslint.json",
|
||||||
|
"rules": {
|
||||||
|
"directive-selector": [true, "attribute", "ucapChat", "camelCase"],
|
||||||
|
"component-selector": [true, "element", "ucap-chat", "kebab-case"]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-ui-group",
|
"name": "@ucap/ng-ui-group",
|
||||||
"version": "0.0.27",
|
"version": "0.0.30",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
"@angular/core": "^9.0.2",
|
"@angular/core": "^9.0.2",
|
||||||
"@angular/material": "^9.0.0",
|
"@angular/material": "^9.0.0",
|
||||||
"@ucap/core": "~0.0.1",
|
"@ucap/core": "~0.0.1",
|
||||||
|
"@ucap/uc-scss": "~0.0.1",
|
||||||
"@ucap/ng-ui": "~0.0.1",
|
"@ucap/ng-ui": "~0.0.1",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<div class="ucap-group-expansion-container" fxFlexFill>
|
<div class="ucap-group-expansion-container" fxFlexFill>
|
||||||
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||||
<ng-container
|
<ng-container
|
||||||
*cdkVirtualFor="let node of dataSource.expandedData$"
|
*cdkVirtualFor="
|
||||||
|
let node of dataSource.expandedData$;
|
||||||
|
templateCacheSize: 0
|
||||||
|
"
|
||||||
></ng-container>
|
></ng-container>
|
||||||
|
|
||||||
<mat-tree #treeList [dataSource]="dataSource" [treeControl]="treeControl">
|
<mat-tree #treeList [dataSource]="dataSource" [treeControl]="treeControl">
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
.ucap-group-expansion-container {
|
||||||
|
background-position: var(
|
||||||
|
--ucap-group-expansion-container-backgroupd-position,
|
||||||
|
absolute
|
||||||
|
);
|
||||||
|
}
|
|
@ -22,6 +22,9 @@ import { UserInfo, GroupDetailData } from '@ucap/protocol-sync';
|
||||||
|
|
||||||
import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
||||||
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
export enum NodeType {
|
export enum NodeType {
|
||||||
None = 'None',
|
None = 'None',
|
||||||
|
@ -129,6 +132,8 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
||||||
this.nodeMap.set(NodeType.Default, []);
|
this.nodeMap.set(NodeType.Default, []);
|
||||||
this.nodeMap.set(NodeType.Buddy, []);
|
this.nodeMap.set(NodeType.Buddy, []);
|
||||||
|
|
||||||
|
this.groupList = list;
|
||||||
|
|
||||||
for (const item of list) {
|
for (const item of list) {
|
||||||
let nodeType = NodeType.Buddy;
|
let nodeType = NodeType.Buddy;
|
||||||
if (0 === item.group.seq) {
|
if (0 === item.group.seq) {
|
||||||
|
@ -186,12 +191,27 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
||||||
| UserInfoDN
|
| UserInfoDN
|
||||||
)[] = [];
|
)[] = [];
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
clickMoreMenu = new EventEmitter<{
|
||||||
|
event: MouseEvent;
|
||||||
|
node: FlatNode;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
checkGroup = new EventEmitter<{
|
||||||
|
isChecked: boolean;
|
||||||
|
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||||
|
}>();
|
||||||
|
|
||||||
@ViewChild('treeList', { static: false })
|
@ViewChild('treeList', { static: false })
|
||||||
treeList: MatTree<FlatNode>;
|
treeList: MatTree<FlatNode>;
|
||||||
|
|
||||||
@ViewChild('cvsvList', { static: false })
|
@ViewChild('cvsvList', { static: false })
|
||||||
cvsvList: CdkVirtualScrollViewport;
|
cvsvList: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
|
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||||
|
psDirectiveRef?: PerfectScrollbarDirective;
|
||||||
|
|
||||||
@ContentChild(ExpansionNodeDirective, {
|
@ContentChild(ExpansionNodeDirective, {
|
||||||
read: TemplateRef,
|
read: TemplateRef,
|
||||||
static: false
|
static: false
|
||||||
|
@ -221,8 +241,11 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
||||||
dataSource: VirtualScrollTreeFlatDataSource<GroupNode, FlatNode>;
|
dataSource: VirtualScrollTreeFlatDataSource<GroupNode, FlatNode>;
|
||||||
|
|
||||||
NodeType = NodeType;
|
NodeType = NodeType;
|
||||||
|
groupList: { group: GroupDetailData; buddyList: UserInfo[] }[];
|
||||||
|
|
||||||
private nodeMap: Map<NodeType, GroupNode[]> = new Map();
|
private nodeMap: Map<NodeType, GroupNode[]> = new Map();
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
||||||
this.treeControl = new FlatTreeControl<FlatNode>(
|
this.treeControl = new FlatTreeControl<FlatNode>(
|
||||||
|
@ -251,12 +274,27 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this._ngOnDestroySubject = new Subject();
|
||||||
|
|
||||||
this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
|
this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
|
||||||
|
this.treeControl.expansionModel.changed
|
||||||
|
.pipe(takeUntil(this._ngOnDestroySubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.cvsvList.checkViewportSize();
|
||||||
|
this.psDirectiveRef.update();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {}
|
ngOnDestroy(): void {
|
||||||
|
if (!!this._ngOnDestroySubject) {
|
||||||
|
this._ngOnDestroySubject.next();
|
||||||
|
this._ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onClickHeaderMenu(event: MouseEvent, node: FlatNode) {}
|
onClickHeaderMenu(event: MouseEvent, node: FlatNode) {
|
||||||
|
this.clickMoreMenu.emit({ event, node });
|
||||||
|
}
|
||||||
|
|
||||||
isCheckedGroup(node: FlatNode): boolean {
|
isCheckedGroup(node: FlatNode): boolean {
|
||||||
const groupDetail = node.node.groupDetail;
|
const groupDetail = node.node.groupDetail;
|
||||||
|
@ -306,7 +344,20 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChangeCheckGroup(value: boolean, node: FlatNode) {}
|
onChangeCheckGroup(value: boolean, node: FlatNode) {
|
||||||
|
const trgGroup = node.node.groupDetail;
|
||||||
|
|
||||||
|
const groupInfos = this.groupList.filter(
|
||||||
|
(groupInfo) => groupInfo.group.seq === trgGroup.seq
|
||||||
|
);
|
||||||
|
|
||||||
|
if (groupInfos.length > 0) {
|
||||||
|
this.checkGroup.emit({
|
||||||
|
isChecked: value,
|
||||||
|
groupBuddyList: groupInfos[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
isHeader = (_: number, node: FlatNode) =>
|
isHeader = (_: number, node: FlatNode) =>
|
||||||
NodeType.Profile !== node.node.nodeType && 0 === node.level;
|
NodeType.Profile !== node.node.nodeType && 0 === node.level;
|
||||||
|
@ -324,4 +375,18 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
||||||
this.dataSource.data = rootNode;
|
this.dataSource.data = rootNode;
|
||||||
this.changeDetectorRef.detectChanges();
|
this.changeDetectorRef.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expandMore() {
|
||||||
|
this.treeList.treeControl.expandAll();
|
||||||
|
if (!!this.psDirectiveRef) {
|
||||||
|
this.psDirectiveRef.scrollToTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expandLess() {
|
||||||
|
this.treeList.treeControl.collapseAll();
|
||||||
|
if (!!this.psDirectiveRef) {
|
||||||
|
this.psDirectiveRef.scrollToTop();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-ui-organization",
|
"name": "@ucap/ng-ui-organization",
|
||||||
"version": "0.0.12",
|
"version": "0.0.27",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
"@angular/core": "^9.0.2",
|
"@angular/core": "^9.0.2",
|
||||||
"@angular/material": "^9.0.0",
|
"@angular/material": "^9.0.0",
|
||||||
"@ucap/core": "~0.0.1",
|
"@ucap/core": "~0.0.1",
|
||||||
|
"@ucap/uc-scss": "~0.0.1",
|
||||||
"@ucap/ng-ui": "~0.0.1",
|
"@ucap/ng-ui": "~0.0.1",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
<div class="ucap-organization-profile-01-container">
|
||||||
|
<!--Profile -->
|
||||||
|
<div class="profile-card-box">
|
||||||
|
<div class="user-profile-info">
|
||||||
|
<ng-container
|
||||||
|
*ngIf="isMe; then mineProfile; else otherProfile"
|
||||||
|
></ng-container>
|
||||||
|
<ng-template #otherProfile>
|
||||||
|
<!--[[ 대화상대 프로필-->
|
||||||
|
<!-- 모바일이 온라인일 경우 + mobile-ing -->
|
||||||
|
<div class="user-profile-thumb mobile-ing">
|
||||||
|
<span class="presence">온라인</span>
|
||||||
|
<div class="profileImage">
|
||||||
|
<img
|
||||||
|
src="https://material.angular.io/assets/img/examples/shiba2.jpg"
|
||||||
|
style="width: 122px; height: 122px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--]]-->
|
||||||
|
</ng-template>
|
||||||
|
<ng-template #mineProfile>
|
||||||
|
<!--[[ 내프로필-->
|
||||||
|
<div class="user-profile-thumb">
|
||||||
|
<span class="presence">온라인</span>
|
||||||
|
<div class="profileImage">
|
||||||
|
<img
|
||||||
|
src="../../../assets/images/ico/img_nophoto.svg"
|
||||||
|
style="width: 122px; height: 122px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="btn-profile-ctrl">
|
||||||
|
<button mat-mini-fab color="primary" class="mat-mini36-fab">
|
||||||
|
<mat-icon class="material-icons-outlined">camera_alt</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--]]-->
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<div class="userInfo">
|
||||||
|
<div class="user-n-g">
|
||||||
|
<div class="name">홍길동</div>
|
||||||
|
<div class="grade">대리</div>
|
||||||
|
</div>
|
||||||
|
<div class="deptName">
|
||||||
|
(Hong Gi Dong)
|
||||||
|
</div>
|
||||||
|
<!-- + 대화상대 프로필 추가 -->
|
||||||
|
<!--
|
||||||
|
<div class="nickName">
|
||||||
|
<div class="nickName-info">닉네임 미설정</div>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="icon create"
|
||||||
|
class="color-white"
|
||||||
|
>
|
||||||
|
<mat-icon>create</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="address-txt">
|
||||||
|
마곡 사이언스 파크 E14동 9층
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
|
<div class="btn-profile-add">
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="btn-star-add"
|
||||||
|
aria-label="Example icon-button with a heart icon"
|
||||||
|
>
|
||||||
|
<img src="../../../assets/images/ico/btn_favorite_w24_s.svg" alt="" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="btn-star-add"
|
||||||
|
aria-label="Example icon-button with a heart icon"
|
||||||
|
>
|
||||||
|
<img src="../../../assets/images/ico/btn_group_add_w24.svg" alt="" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- + 대회상대 프로필-->
|
||||||
|
<!--
|
||||||
|
<div class="btn-partner-set">
|
||||||
|
<button mat-icon-button aria-label="chat">
|
||||||
|
<img
|
||||||
|
src="../../../assets/images/ico/btn_lise_chat_a24.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button aria-label="message">
|
||||||
|
<img
|
||||||
|
src="../../../assets/images/ico/btn_list_message_a24.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button aria-label="mobile">
|
||||||
|
<img
|
||||||
|
src="../../../assets/images/ico/btn_list_mobile_a24.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button aria-label="call">
|
||||||
|
<img
|
||||||
|
src="../../../assets/images/ico/btn_list_call_a24.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button mat-icon-button aria-label="vc">
|
||||||
|
<img
|
||||||
|
src="../../../assets/images/ico/btn_list_vc-a24.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<!-- + 내프로필 -->
|
||||||
|
<div class="my-input inputtype">
|
||||||
|
<mat-form-field class="example-full-width my-in-input" appearance="none">
|
||||||
|
<mat-label></mat-label>
|
||||||
|
<input matInput placeholder="" value="마곡 사이언스 파크 E14동 9층" />
|
||||||
|
</mat-form-field>
|
||||||
|
<button mat-icon-button aria-label="icon create" class="color-white">
|
||||||
|
<mat-icon>create</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="user-profile-info-list">
|
||||||
|
<ul>
|
||||||
|
<li><span>회사</span> LGCNS</li>
|
||||||
|
<li><span>부서</span> 아키텍쳐 솔루션</li>
|
||||||
|
<li><span>이메일</span> gildonghong@cns.com</li>
|
||||||
|
<li><span>사무실</span> 02-9876-5432</li>
|
||||||
|
<li><span>모바일</span> 010-1234-5678</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- //Profile-->
|
||||||
|
</div>
|
|
@ -0,0 +1,178 @@
|
||||||
|
@import '~@ucap/ui-scss/ucap';
|
||||||
|
|
||||||
|
.ucap-organization-profile-01-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
min-width: 450px;
|
||||||
|
@include screen(mid) {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-card-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 60px 8.7%;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
.user-profile-info {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
// Profile thumb//////////////////
|
||||||
|
.user-profile-thumb {
|
||||||
|
}
|
||||||
|
//////////////////Profile thumb //
|
||||||
|
.userInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
padding-left: 38px;
|
||||||
|
.user-n-g {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row-reverse nowrap;
|
||||||
|
align-items: flex-end;
|
||||||
|
height: 44px;
|
||||||
|
.name {
|
||||||
|
font: {
|
||||||
|
size: 26px;
|
||||||
|
weight: 600;
|
||||||
|
}
|
||||||
|
order: 1;
|
||||||
|
-ms-flex-order: 1;
|
||||||
|
}
|
||||||
|
.grade {
|
||||||
|
font: {
|
||||||
|
size: 18px;
|
||||||
|
}
|
||||||
|
color: #f1f1f1;
|
||||||
|
margin-left: 6px;
|
||||||
|
order: 0;
|
||||||
|
-ms-flex-order: 0;
|
||||||
|
}
|
||||||
|
& + .deptName {
|
||||||
|
margin-top: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.deptName {
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 25px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.nickName {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin-top: 18px;
|
||||||
|
align-items: center;
|
||||||
|
.nickName-info {
|
||||||
|
padding: 0 16px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
border-radius: 15px;
|
||||||
|
border: solid 1px #fc5182;
|
||||||
|
background-color: rgba(255, 255, 255, 0.95);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.address-txt {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 21px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn-profile-add {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 5;
|
||||||
|
top: 40px;
|
||||||
|
right: 40px;
|
||||||
|
button {
|
||||||
|
margin: 0 2px;
|
||||||
|
&.btn-star-add {
|
||||||
|
line-height: 24px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn-partner-set {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 25px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.8);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.8);
|
||||||
|
height: 70px;
|
||||||
|
margin-top: 30px;
|
||||||
|
img {
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.my-input {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 78px;
|
||||||
|
.my-in-input {
|
||||||
|
font-size: 16px;
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 24px;
|
||||||
|
line-height: 24px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.user-profile-info-list {
|
||||||
|
margin-top: 60px;
|
||||||
|
ul {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 250px;
|
||||||
|
li {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
span {
|
||||||
|
width: 100px;
|
||||||
|
height: 34px;
|
||||||
|
border-radius: 18px;
|
||||||
|
border: solid 1px #f8f9fd;
|
||||||
|
background-color: #aaa0a5;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////// 제거
|
||||||
|
.profile-card {
|
||||||
|
//mat-card
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
.profileImage {
|
||||||
|
width: 126px;
|
||||||
|
height: 126px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
img {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { Profile01Component } from './profile-01.component';
|
||||||
|
|
||||||
|
describe('ucap::organization::Profile01Component', () => {
|
||||||
|
let component: Profile01Component;
|
||||||
|
let fixture: ComponentFixture<Profile01Component>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [Profile01Component]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(Profile01Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,28 @@
|
||||||
|
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 { OrganizationUiModule } from '../organization-ui.module';
|
||||||
|
|
||||||
|
import { Profile01Component } from './profile-01.component';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Profile01Component',
|
||||||
|
component: Profile01Component,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [BrowserModule, BrowserAnimationsModule, OrganizationUiModule],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Text = () => ({
|
||||||
|
component: Profile01Component,
|
||||||
|
props: {
|
||||||
|
text: 'Hello Profile01Component'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,108 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef
|
||||||
|
} from '@angular/core';
|
||||||
|
import { UserInfoSS, AuthResponse } from '@ucap/protocol-query';
|
||||||
|
import { OpenProfileOptions } from '@ucap/protocol-buddy';
|
||||||
|
import { FileUploadItem } from '@ucap/api';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-organization-profile-01',
|
||||||
|
templateUrl: './profile-01.component.html',
|
||||||
|
styleUrls: ['./profile-01.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class Profile01Component implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
authRes: AuthResponse;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
userInfo: UserInfoSS;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImageRoot: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
isMe: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
isBuddy: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
isFavorite: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
myMadn: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
canVideoConfernece: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
canBuddyToggleButton: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
openProfileOptions: OpenProfileOptions;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
toggleFavorite: EventEmitter<{
|
||||||
|
userInfo: UserInfoSS;
|
||||||
|
isFavorite: boolean;
|
||||||
|
}> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
toggleBuddy: EventEmitter<{
|
||||||
|
userInfo: UserInfoSS;
|
||||||
|
isBuddy: boolean;
|
||||||
|
}> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openChat: EventEmitter<UserInfoSS> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
sendCall: EventEmitter<string> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
sendSms: EventEmitter<string> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
createConference: EventEmitter<number> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
sendMessage: EventEmitter<UserInfoSS> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
profileImageView: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
uploadProfileImage: EventEmitter<FileUploadItem> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changeIntro: EventEmitter<string> = new EventEmitter();
|
||||||
|
|
||||||
|
@ViewChild('profileImageFileInput', { static: false })
|
||||||
|
profileImageFileInput: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
<div class="ucap-organization-profile-list-item-01-container">
|
||||||
|
<div class="user-profile-info">
|
||||||
|
<div class="user-profile-thumb mobile-ing">
|
||||||
|
<!-- 모바일이 온라인일 경우 + mobile-ing -->
|
||||||
|
<span class="presence other-business">다른용무</span>
|
||||||
|
<!-- 기본값:온라인, +offline:오프라인, +absence:부재중, +other-business:다른용무 -->
|
||||||
|
<!-- <span class="ucap-organization-profile-list-item-presence">bullet</span> -->
|
||||||
|
<div class="profile-image" (click)="onClickOpenProfile($event)">
|
||||||
|
<img
|
||||||
|
ucapImage
|
||||||
|
[base]="profileImageRoot"
|
||||||
|
[path]="userInfo.profileImageFile"
|
||||||
|
[default]="defaultProfileImage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</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="company-info">
|
||||||
|
<div class="companyName">{{ userInfo.companyName }}</div>
|
||||||
|
<div class="email">{{ userInfo.email }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="contact">
|
||||||
|
<div class="mobileNumber" (click)="onSendCall($event, CallType.Mobile)">
|
||||||
|
{{ userInfo.hpNumber | ucapPhoneNumber }}
|
||||||
|
</div>
|
||||||
|
<div class="officNumber" (click)="onSendCall($event, CallType.Office)">
|
||||||
|
{{ userInfo.lineNumber | ucapPhoneNumber }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="checkable">
|
||||||
|
<mat-checkbox
|
||||||
|
#checkbox
|
||||||
|
[checked]="checked"
|
||||||
|
(change)="onChangeCheck(checkbox.checked)"
|
||||||
|
></mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import '~@ucap/ui-scss/ucap';
|
||||||
|
|
||||||
|
.ucap-organization-profile-list-item-01-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -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,16 @@
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { linkTo } from '@storybook/addon-links';
|
||||||
|
|
||||||
|
import { ProfileListItem01Component } from './profile-list-item-01.component';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'ProfileListItem01Component',
|
||||||
|
component: ProfileListItem01Component
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Text = () => ({
|
||||||
|
component: ProfileListItem01Component,
|
||||||
|
props: {
|
||||||
|
text: 'Hello ProfileListItem01Component'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,97 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
Output,
|
||||||
|
EventEmitter
|
||||||
|
} from '@angular/core';
|
||||||
|
import { UserInfoSS } from '@ucap/protocol-query';
|
||||||
|
import { StatusBulkInfo } from '@ucap/protocol-status';
|
||||||
|
import { CallType } from '../types/call.type';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-organization-profile-list-item-01',
|
||||||
|
templateUrl: './profile-list-item-01.component.html',
|
||||||
|
styleUrls: ['./profile-list-item-01.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ProfileListItem01Component implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
userInfo: UserInfoSS;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
presenceInfo?: StatusBulkInfo;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
profileImageRoot: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
defaultProfileImage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
authCall = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checkable = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checked = false;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openProfile = new EventEmitter<UserInfoSS>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changeCheck = new EventEmitter<{
|
||||||
|
isChecked: boolean;
|
||||||
|
userInfo: UserInfoSS;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
sendCall = new EventEmitter<{
|
||||||
|
type: CallType;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
CallType = CallType;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickOpenProfile(event: MouseEvent): void {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
this.openProfile.emit(this.userInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeCheck(isChecked: boolean): void {
|
||||||
|
this.changeCheck.emit({
|
||||||
|
isChecked,
|
||||||
|
userInfo: this.userInfo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendCall(event: MouseEvent, type: CallType): void {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
this.sendCall.emit({
|
||||||
|
type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<div class="ucap-organization-profile-list-container">
|
||||||
|
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||||
|
<ng-container *cdkVirtualFor="let userInfo of userInfos">
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="nodeTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: userInfo }"
|
||||||
|
></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</cdk-virtual-scroll-viewport>
|
||||||
|
</div>
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import '~@ucap/ui-scss/ucap';
|
||||||
|
|
||||||
|
.ucap-organization-profile-list-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { ProfileListComponent } from './profile-list.component';
|
||||||
|
|
||||||
|
describe('ucap::organization::ProfileListComponent', () => {
|
||||||
|
let component: ProfileListComponent;
|
||||||
|
let fixture: ComponentFixture<ProfileListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ProfileListComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfileListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { linkTo } from '@storybook/addon-links';
|
||||||
|
|
||||||
|
import { ProfileListComponent } from './profile-list.component';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'ProfileListComponent',
|
||||||
|
component: ProfileListComponent
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Text = () => ({
|
||||||
|
component: ProfileListComponent,
|
||||||
|
props: {
|
||||||
|
text: 'Hello ProfileListComponent'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
Directive,
|
||||||
|
ContentChild,
|
||||||
|
TemplateRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { UserInfo } from '@ucap/protocol-sync';
|
||||||
|
import { UserInfoSS, UserInfoF, UserInfoDN } from '@ucap/protocol-query';
|
||||||
|
|
||||||
|
export type UserInfoTypes = UserInfo | UserInfoSS | UserInfoF | UserInfoDN;
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapOrganizationProfileListNode]'
|
||||||
|
})
|
||||||
|
export class ProfileListNodeDirective {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-organization-profile-list',
|
||||||
|
templateUrl: './profile-list.component.html',
|
||||||
|
styleUrls: ['./profile-list.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ProfileListComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
userInfos: UserInfo[];
|
||||||
|
|
||||||
|
@ContentChild(ProfileListNodeDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
nodeTemplate: TemplateRef<ProfileListNodeDirective>;
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<div class="ucap-organization-search-for-tenant-container">
|
||||||
|
<div
|
||||||
|
class="ucap-organization-search-for-tenant-inner ucap-mat-input-container"
|
||||||
|
fxLayout="row"
|
||||||
|
>
|
||||||
|
<div fxFlex="0 0 auto">
|
||||||
|
<mat-form-field appearance="none" class="ucap-mat-form-field-for-select">
|
||||||
|
<mat-select
|
||||||
|
placeholder=""
|
||||||
|
class="ucap-mat-select"
|
||||||
|
[value]="defaultCompany"
|
||||||
|
disableOptionCentering
|
||||||
|
(selectionChange)="onChangeTenant($event)"
|
||||||
|
>
|
||||||
|
<mat-option
|
||||||
|
*ngFor="let company of companyList"
|
||||||
|
[value]="company.companyCode"
|
||||||
|
>{{ company.companyName }}
|
||||||
|
</mat-option>
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div fxFlex="1 1 auto">
|
||||||
|
<mat-form-field
|
||||||
|
fxFlexFill
|
||||||
|
class="ucap-mat-form-field-for-input"
|
||||||
|
appearance="none"
|
||||||
|
enabled="true"
|
||||||
|
matAutosize="true"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
matInput
|
||||||
|
#searchWordInput
|
||||||
|
[placeholder]="placeholder"
|
||||||
|
(keydown.enter)="onKeyDownEnter(searchWordInput.value)"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
mat-button
|
||||||
|
matSuffix
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="Clear"
|
||||||
|
(click)="searchWordInput.value = ''; onClickCancel()"
|
||||||
|
class="btn-close"
|
||||||
|
>
|
||||||
|
<mat-icon>close</mat-icon>
|
||||||
|
</button>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
<div fxFlex="0 0 40px">
|
||||||
|
<button mat-flat-button color="primary" class="btn-ico-search">
|
||||||
|
<mat-icon>search</mat-icon>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,9 @@
|
||||||
|
@import '~@ucap/ui-scss/ucap';
|
||||||
|
|
||||||
|
.ucap-organization-search-for-tenant-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.ucap-organization-search-for-tenant-inner {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { SearchForTenantComponent } from './search-for-tenant.component';
|
||||||
|
|
||||||
|
describe('ucap::organization::SearchForTenantComponent', () => {
|
||||||
|
let component: SearchForTenantComponent;
|
||||||
|
let fixture: ComponentFixture<SearchForTenantComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SearchForTenantComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SearchForTenantComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,28 @@
|
||||||
|
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 { OrganizationUiModule } from '../organization-ui.module';
|
||||||
|
|
||||||
|
import { SearchForTenantComponent } from './search-for-tenant.component';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'SearchForTenantComponent',
|
||||||
|
component: SearchForTenantComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [BrowserModule, BrowserAnimationsModule, OrganizationUiModule],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Text = () => ({
|
||||||
|
component: SearchForTenantComponent,
|
||||||
|
props: {
|
||||||
|
text: 'Hello SearchForTenantComponent'
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { Company } from '@ucap/api-external';
|
||||||
|
import { MatSelectChange } from '@angular/material/select';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-organization-search-for-tenant',
|
||||||
|
templateUrl: './search-for-tenant.component.html',
|
||||||
|
styleUrls: ['./search-for-tenant.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class SearchForTenantComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
companyList: Company[];
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
defaultCompany: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
placeholder: string;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changed: EventEmitter<{
|
||||||
|
companyCode: string;
|
||||||
|
searchWord: string;
|
||||||
|
}> = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
canceled: EventEmitter<void> = new EventEmitter();
|
||||||
|
|
||||||
|
companyCode: string;
|
||||||
|
|
||||||
|
private ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject();
|
||||||
|
|
||||||
|
this.companyCode = this.defaultCompany;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeTenant(event: MatSelectChange) {
|
||||||
|
this.companyCode = event.value;
|
||||||
|
}
|
||||||
|
onKeyDownEnter(searchWord: string) {
|
||||||
|
this.changed.emit({ companyCode: this.companyCode, searchWord });
|
||||||
|
}
|
||||||
|
onClickCancel() {
|
||||||
|
this.canceled.emit();
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@
|
||||||
<div
|
<div
|
||||||
class="tree-node-body"
|
class="tree-node-body"
|
||||||
[ngClass]="
|
[ngClass]="
|
||||||
currentDeptSeq === node?.data?.deptInfo.seq ? 'current' : ''
|
currentDeptSeq === node?.data?.deptInfo?.seq ? 'current' : ''
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<span class="horizontal-line"></span>
|
<span class="horizontal-line"></span>
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
@import '~@ucap/ui-scss/ucap';
|
||||||
|
|
||||||
|
.ucap-organization-tree-container {
|
||||||
|
padding-left: 17px;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.mat-tree {
|
||||||
|
background-color: transparent;
|
||||||
|
padding-right: 17px;
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
width: 21px;
|
||||||
|
height: 100%;
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
left: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
width: 21px;
|
||||||
|
height: 100%;
|
||||||
|
border-left: 1px solid #ccc;
|
||||||
|
border-right: 1px solid #ccc;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
left: 50px;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,14 +3,11 @@ import {
|
||||||
OnInit,
|
OnInit,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
Input,
|
Input,
|
||||||
Output,
|
|
||||||
EventEmitter,
|
|
||||||
ViewChild,
|
ViewChild,
|
||||||
ContentChild,
|
|
||||||
TemplateRef,
|
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
ChangeDetectorRef,
|
ChangeDetectorRef,
|
||||||
Directive
|
EventEmitter,
|
||||||
|
Output
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||||
|
@ -24,6 +21,9 @@ import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
||||||
import { LogService } from '@ucap/ng-logger';
|
import { LogService } from '@ucap/ng-logger';
|
||||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||||
import { trigger, transition, style, animate } from '@angular/animations';
|
import { trigger, transition, style, animate } from '@angular/animations';
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { takeUntil } from 'rxjs/operators';
|
||||||
|
import { PerfectScrollbarDirective } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
export interface OrganizationNode {
|
export interface OrganizationNode {
|
||||||
deptInfo: DeptInfo;
|
deptInfo: DeptInfo;
|
||||||
|
@ -119,16 +119,25 @@ export class TreeComponent implements OnInit, OnDestroy {
|
||||||
// tslint:disable-next-line: variable-name
|
// tslint:disable-next-line: variable-name
|
||||||
_deptInfoList: DeptInfo[];
|
_deptInfoList: DeptInfo[];
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
clickNode = new EventEmitter<DeptInfo>();
|
||||||
|
|
||||||
@ViewChild('treeList', { static: false })
|
@ViewChild('treeList', { static: false })
|
||||||
treeList: MatTree<FlatNode>;
|
treeList: MatTree<FlatNode>;
|
||||||
|
|
||||||
@ViewChild('cvsvList', { static: false })
|
@ViewChild('cvsvList', { static: false })
|
||||||
cvsvList: CdkVirtualScrollViewport;
|
cvsvList: CdkVirtualScrollViewport;
|
||||||
|
|
||||||
|
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||||
|
psDirectiveRef?: PerfectScrollbarDirective;
|
||||||
|
|
||||||
treeControl: FlatTreeControl<FlatNode>;
|
treeControl: FlatTreeControl<FlatNode>;
|
||||||
treeFlattener: MatTreeFlattener<OrganizationNode, FlatNode>;
|
treeFlattener: MatTreeFlattener<OrganizationNode, FlatNode>;
|
||||||
dataSource: VirtualScrollTreeFlatDataSource<OrganizationNode, FlatNode>;
|
dataSource: VirtualScrollTreeFlatDataSource<OrganizationNode, FlatNode>;
|
||||||
|
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private changeDetectorRef: ChangeDetectorRef,
|
private changeDetectorRef: ChangeDetectorRef,
|
||||||
private logService: LogService
|
private logService: LogService
|
||||||
|
@ -158,12 +167,27 @@ export class TreeComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
this._ngOnDestroySubject = new Subject();
|
||||||
|
|
||||||
this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
|
this.dataSource.cdkVirtualScrollViewport = this.cvsvList;
|
||||||
|
this.treeControl.expansionModel.changed
|
||||||
|
.pipe(takeUntil(this._ngOnDestroySubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.cvsvList.checkViewportSize();
|
||||||
|
this.psDirectiveRef.update();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {}
|
ngOnDestroy(): void {
|
||||||
|
if (!!this._ngOnDestroySubject) {
|
||||||
|
this._ngOnDestroySubject.next();
|
||||||
|
this._ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hasChild = (_: number, node: FlatNode) => node.expandable;
|
hasChild = (_: number, node: FlatNode) => node.expandable;
|
||||||
|
|
||||||
onClickNode(node: FlatNode) {}
|
onClickNode(node: FlatNode) {
|
||||||
|
this.clickNode.emit(node.data.deptInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,11 @@ import { ScrollingModule } from '@angular/cdk/scrolling';
|
||||||
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
import { MatRippleModule } from '@angular/material/core';
|
import { MatRippleModule } from '@angular/material/core';
|
||||||
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatTreeModule } from '@angular/material/tree';
|
import { MatTreeModule } from '@angular/material/tree';
|
||||||
|
|
||||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
@ -18,17 +21,29 @@ import { UiModule } from '@ucap/ng-ui';
|
||||||
import { ModuleConfig } from './config/module-config';
|
import { ModuleConfig } from './config/module-config';
|
||||||
import { _MODULE_CONFIG } from './config/token';
|
import { _MODULE_CONFIG } from './config/token';
|
||||||
|
|
||||||
import { ProfileListItemComponent } from './components/profile-list-item.component';
|
import { Profile01Component } from './components/profile-01.component';
|
||||||
|
import { ProfileListItem01Component } from './components/profile-list-item-01.component';
|
||||||
|
import {
|
||||||
|
ProfileListComponent,
|
||||||
|
ProfileListNodeDirective
|
||||||
|
} from './components/profile-list.component';
|
||||||
|
import { SearchForTenantComponent } from './components/search-for-tenant.component';
|
||||||
import { TreeComponent } from './components/tree.component';
|
import { TreeComponent } from './components/tree.component';
|
||||||
|
|
||||||
import { TranslatePipe } from './pipes/translate.pipe';
|
import { TranslatePipe } from './pipes/translate.pipe';
|
||||||
|
|
||||||
import { TranslateService } from './services/translate.service';
|
import { TranslateService } from './services/translate.service';
|
||||||
|
|
||||||
const COMPONENTS = [TreeComponent, ProfileListItemComponent];
|
const COMPONENTS = [
|
||||||
|
TreeComponent,
|
||||||
|
Profile01Component,
|
||||||
|
ProfileListItem01Component,
|
||||||
|
ProfileListComponent,
|
||||||
|
SearchForTenantComponent
|
||||||
|
];
|
||||||
const DIALOGS = [];
|
const DIALOGS = [];
|
||||||
const PIPES = [TranslatePipe];
|
const PIPES = [TranslatePipe];
|
||||||
const DIRECTIVES = [];
|
const DIRECTIVES = [ProfileListNodeDirective];
|
||||||
const SERVICES = [TranslateService];
|
const SERVICES = [TranslateService];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -46,8 +61,11 @@ export class OrganizationUiRootModule {}
|
||||||
|
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
MatCheckboxModule,
|
MatCheckboxModule,
|
||||||
|
MatFormFieldModule,
|
||||||
MatIconModule,
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
MatRippleModule,
|
MatRippleModule,
|
||||||
|
MatSelectModule,
|
||||||
MatTreeModule,
|
MatTreeModule,
|
||||||
|
|
||||||
PerfectScrollbarModule,
|
PerfectScrollbarModule,
|
||||||
|
|
|
@ -113,7 +113,7 @@ export class TranslateService {
|
||||||
|
|
||||||
if (target instanceof Array) {
|
if (target instanceof Array) {
|
||||||
const result: string[] = [];
|
const result: string[] = [];
|
||||||
target.forEach(t => {
|
target.forEach((t) => {
|
||||||
result.push(this.get(t, key, separator));
|
result.push(this.get(t, key, separator));
|
||||||
});
|
});
|
||||||
return result.join(!!separator ? separator : '');
|
return result.join(!!separator ? separator : '');
|
||||||
|
@ -127,7 +127,12 @@ export class TranslateService {
|
||||||
const langKey = `${key}${lang}`;
|
const langKey = `${key}${lang}`;
|
||||||
|
|
||||||
if (-1 !== keys.indexOf(langKey)) {
|
if (-1 !== keys.indexOf(langKey)) {
|
||||||
return target[langKey];
|
const rtnValue = target[langKey];
|
||||||
|
if (!!rtnValue && rtnValue.trim().length > 0) {
|
||||||
|
return rtnValue;
|
||||||
|
} else {
|
||||||
|
return target[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
|
|
4
projects/ui-organization/src/lib/types/call.type.ts
Normal file
4
projects/ui-organization/src/lib/types/call.type.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export enum CallType {
|
||||||
|
Office = 'OFFICE',
|
||||||
|
Mobile = 'MOBILE'
|
||||||
|
}
|
|
@ -4,11 +4,14 @@
|
||||||
|
|
||||||
export * from './lib/config/module-config';
|
export * from './lib/config/module-config';
|
||||||
|
|
||||||
export * from './lib/components/profile-list-item.component';
|
export * from './lib/components/profile-01.component';
|
||||||
|
export * from './lib/components/profile-list-item-01.component';
|
||||||
export * from './lib/components/tree.component';
|
export * from './lib/components/tree.component';
|
||||||
|
|
||||||
export * from './lib/pipes/translate.pipe';
|
export * from './lib/pipes/translate.pipe';
|
||||||
|
|
||||||
export * from './lib/services/translate.service';
|
export * from './lib/services/translate.service';
|
||||||
|
|
||||||
|
export * from './lib/types/call.type';
|
||||||
|
|
||||||
export * from './lib/organization-ui.module';
|
export * from './lib/organization-ui.module';
|
||||||
|
|
|
@ -4,5 +4,7 @@
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
"peerDependencies": {}
|
"peerDependencies": {
|
||||||
|
"@ucap/uc-scss": "~0.0.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-ui",
|
"name": "@ucap/ng-ui",
|
||||||
"version": "0.0.5",
|
"version": "0.0.11",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
"@ucap/core": "~0.0.1",
|
"@ucap/core": "~0.0.1",
|
||||||
"@ucap/api": "~0.0.1",
|
"@ucap/api": "~0.0.1",
|
||||||
"@ucap/protocol-event": "~0.0.1",
|
"@ucap/protocol-event": "~0.0.1",
|
||||||
|
"@ucap/uc-scss": "~0.0.1",
|
||||||
"@ucap/ng-core": "~0.0.1",
|
"@ucap/ng-core": "~0.0.1",
|
||||||
"@ucap/ng-api-common": "~0.0.1",
|
"@ucap/ng-api-common": "~0.0.1",
|
||||||
"autolinker": "^3.13.0",
|
"autolinker": "^3.13.0",
|
||||||
|
|
|
@ -1,74 +1,44 @@
|
||||||
import { storiesOf, moduleMetadata } from '@storybook/angular';
|
import { storiesOf, moduleMetadata } from '@storybook/angular';
|
||||||
import { withKnobs, text, object } from '@storybook/addon-knobs';
|
import { withKnobs, text, object } from '@storybook/addon-knobs';
|
||||||
|
|
||||||
import { action } from '@storybook/addon-actions';
|
import { action } from '@storybook/addon-actions';
|
||||||
import { linkTo } from '@storybook/addon-links';
|
import { linkTo } from '@storybook/addon-links';
|
||||||
|
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
import { UiModule } from '../ui.module';
|
import { UiModule } from '../ui.module';
|
||||||
|
|
||||||
import { FloatActionButtonComponent } from './float-action-button.component';
|
import { FloatActionButtonComponent } from './float-action-button.component';
|
||||||
import { ucapAnimations } from '../animations';
|
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
|
||||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
||||||
import { MatCardModule } from '@angular/material/card';
|
|
||||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
|
||||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
||||||
import { MatSliderModule } from '@angular/material/slider';
|
|
||||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
|
||||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
|
||||||
import { I18nModule } from '@ucap/ng-i18n';
|
|
||||||
|
|
||||||
storiesOf('ui/FloatActionButtonComponent', module)
|
export default {
|
||||||
.addDecorator(withKnobs)
|
title: 'FloatActionButtonComponent',
|
||||||
.addDecorator(
|
component: FloatActionButtonComponent,
|
||||||
|
decorators: [
|
||||||
moduleMetadata({
|
moduleMetadata({
|
||||||
imports: [BrowserModule, BrowserAnimationsModule, UiModule],
|
imports: [BrowserModule, BrowserAnimationsModule, UiModule],
|
||||||
providers: []
|
providers: []
|
||||||
})
|
})
|
||||||
)
|
]
|
||||||
.add(
|
};
|
||||||
'Default',
|
|
||||||
() => {
|
export const Text = () => ({
|
||||||
const props: { [K in keyof FloatActionButtonComponent]?: any } = {
|
props: {
|
||||||
buttons: object('Buttons', [
|
buttons: object('Buttons', [
|
||||||
{
|
{
|
||||||
icon: 'chat',
|
icon: 'chat',
|
||||||
tooltip: 'tooltip_chat',
|
tooltip: 'tooltip_chat',
|
||||||
divisionType: 'div_chat'
|
divisionType: 'div_chat'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'group',
|
icon: 'group',
|
||||||
tooltip: 'tooltip_group',
|
tooltip: 'tooltip_group',
|
||||||
divisionType: 'div_group'
|
divisionType: 'div_group'
|
||||||
}
|
}
|
||||||
]),
|
]),
|
||||||
buttonClick: (e) => {
|
buttonClick: (e) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
action('buttonClick')(e);
|
action('buttonClick')(e);
|
||||||
}
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
component: FloatActionButtonComponent,
|
|
||||||
props
|
|
||||||
};
|
|
||||||
},
|
|
||||||
{
|
|
||||||
note: 'Default component.'
|
|
||||||
}
|
}
|
||||||
);
|
}
|
||||||
|
});
|
||||||
// export default {
|
|
||||||
// title: 'FloatActionButtonComponent',
|
|
||||||
// component: FloatActionButtonComponent
|
|
||||||
// };
|
|
||||||
|
|
||||||
// export const Text = () => ({
|
|
||||||
// component: FloatActionButtonComponent,
|
|
||||||
// props: {
|
|
||||||
// text: 'Hello FloatActionButtonComponent'
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
|
@ -6,9 +6,13 @@ import { PhoneNumberUtil } from '../utils/phone-number.util';
|
||||||
export class PhoneNumberPipe implements PipeTransform {
|
export class PhoneNumberPipe implements PipeTransform {
|
||||||
public transform(
|
public transform(
|
||||||
value: string,
|
value: string,
|
||||||
country: CountryCode,
|
country?: CountryCode,
|
||||||
mask: boolean = false
|
mask: boolean = false
|
||||||
): string {
|
): string {
|
||||||
return PhoneNumberUtil.format(country, value, mask);
|
if (!!value && value.trim().length > 0) {
|
||||||
|
return PhoneNumberUtil.format(value, country, mask);
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -265,4 +265,41 @@ export class DateService {
|
||||||
return m.format('MM.DD');
|
return m.format('MM.DD');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checking today
|
||||||
|
* @returns boolean true is today.
|
||||||
|
*/
|
||||||
|
public isToday(date: any, options?: DateOptions): boolean {
|
||||||
|
if (!date || 0 === date.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let d = moment.tz(date, 'Asia/Seoul').utc();
|
||||||
|
|
||||||
|
d = d.tz(this.currentTimezone);
|
||||||
|
|
||||||
|
if (!!options) {
|
||||||
|
if (!!options.manipulate) {
|
||||||
|
if (!!options.manipulate.amount && !!options.manipulate.unit) {
|
||||||
|
d = d.add(options.manipulate.amount, options.manipulate.unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!options.manipulate.startOf) {
|
||||||
|
d = d.startOf(options.manipulate.startOf);
|
||||||
|
}
|
||||||
|
if (!!options.manipulate.endOf) {
|
||||||
|
d = d.startOf(options.manipulate.endOf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const today = moment();
|
||||||
|
|
||||||
|
if (d.isSame(today, 'day')) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
52
projects/ui/src/lib/services/dialog.service.ts
Normal file
52
projects/ui/src/lib/services/dialog.service.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { TemplateRef, Injectable } from '@angular/core';
|
||||||
|
import { ComponentType } from '@angular/cdk/portal';
|
||||||
|
import {
|
||||||
|
MatDialog,
|
||||||
|
MatDialogConfig,
|
||||||
|
MatDialogRef
|
||||||
|
} from '@angular/material/dialog';
|
||||||
|
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { take, map, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DialogService {
|
||||||
|
constructor(private matDialog: MatDialog) {}
|
||||||
|
|
||||||
|
public open<T, D = any, R = any>(
|
||||||
|
componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
|
||||||
|
config?: MatDialogConfig<D>
|
||||||
|
): Promise<R> {
|
||||||
|
return new Promise<R>((resolve, reject) => {
|
||||||
|
config = { ...config, autoFocus: false };
|
||||||
|
|
||||||
|
const dialogRef = this.matDialog.open<T, D, R>(
|
||||||
|
componentOrTemplateRef,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
|
||||||
|
dialogRef
|
||||||
|
.afterClosed()
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
map(result => {
|
||||||
|
return resolve(result);
|
||||||
|
}),
|
||||||
|
catchError(err => {
|
||||||
|
return of(reject(err));
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getDialogById(id: string): MatDialogRef<any, any> {
|
||||||
|
return this.matDialog.getDialogById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
closeAll() {
|
||||||
|
this.matDialog.closeAll();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,19 @@
|
||||||
import { parsePhoneNumberFromString, CountryCode } from 'libphonenumber-js';
|
import { parsePhoneNumberFromString, CountryCode } from 'libphonenumber-js';
|
||||||
|
|
||||||
export class PhoneNumberUtil {
|
export class PhoneNumberUtil {
|
||||||
static format(country: CountryCode, value: string, mask: boolean = false) {
|
static format(
|
||||||
|
value: string,
|
||||||
|
country: CountryCode = 'KR',
|
||||||
|
mask: boolean = false
|
||||||
|
) {
|
||||||
const phoneNumber = parsePhoneNumberFromString(value, country);
|
const phoneNumber = parsePhoneNumberFromString(value, country);
|
||||||
|
|
||||||
const s = phoneNumber.formatNational();
|
let s = value;
|
||||||
|
if (!!phoneNumber) {
|
||||||
|
if (!!phoneNumber.isValid()) {
|
||||||
|
s = phoneNumber.formatNational();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!mask) {
|
if (!mask) {
|
||||||
return s;
|
return s;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user