0517 sync
This commit is contained in:
parent
aee13b8a4d
commit
b4ef37624c
|
@ -1,3 +1,5 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
stories: ['../projects/**/*.stories.ts'],
|
||||
addons: [
|
||||
|
@ -5,5 +7,19 @@ module.exports = {
|
|||
'@storybook/addon-links',
|
||||
'@storybook/addon-notes',
|
||||
'@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": {
|
||||
"projectType": "library",
|
||||
"schematics": {
|
||||
|
|
1925
package-lock.json
generated
1925
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-group": "node ./scripts/build.js store-group",
|
||||
"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-organization": "node ./scripts/build.js ui-organization",
|
||||
"build:ui-authentication": "node ./scripts/build.js ui-authentication",
|
||||
"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-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",
|
||||
|
@ -91,11 +92,12 @@
|
|||
"publish:store-chat": "cd ./dist/store-chat && npm publish",
|
||||
"publish:store-group": "cd ./dist/store-group && 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-organization": "cd ./dist/ui-organization && npm publish",
|
||||
"publish:ui-authentication": "cd ./dist/ui-authentication && 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-default": "cd ./dist/ui-skin-default && npm publish",
|
||||
"test": "ng test",
|
||||
|
@ -179,13 +181,14 @@
|
|||
"@ucap/ng-protocol-sync": "file:pack/ucap-ng-protocol-sync-0.0.3.tgz",
|
||||
"@ucap/ng-protocol-umg": "file:pack/ucap-ng-protocol-umg-0.0.3.tgz",
|
||||
"@ucap/ng-store-authentication": "file:pack/ucap-ng-store-authentication-0.0.10.tgz",
|
||||
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.5.tgz",
|
||||
"@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.6.tgz",
|
||||
"@ucap/ng-store-organization": "file:pack/ucap-ng-store-organization-0.0.4.tgz",
|
||||
"@ucap/ng-ui": "file:pack/ucap-ng-ui-0.0.5.tgz",
|
||||
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.19.tgz",
|
||||
"@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.27.tgz",
|
||||
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.12.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.9.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.11.tgz",
|
||||
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.20.tgz",
|
||||
"@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.3.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-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",
|
||||
|
@ -206,6 +209,7 @@
|
|||
"@ucap/protocol-status": "~0.0.5",
|
||||
"@ucap/protocol-sync": "~0.0.4",
|
||||
"@ucap/protocol-umg": "~0.0.5",
|
||||
"@ucap/ui-scss": "~0.0.3",
|
||||
"@ucap/web-socket": "~0.0.5",
|
||||
"@ucap/web-storage": "~0.0.5",
|
||||
"autolinker": "^3.13.0",
|
||||
|
@ -236,6 +240,7 @@
|
|||
"queueing-subject": "^0.3.4",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "~6.5.4",
|
||||
"sass-resources-loader": "^2.0.3",
|
||||
"scss-bundle": "^3.1.1",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslib": "^1.11.1",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-store-chat",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.8",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
|
|
@ -13,6 +13,8 @@ import {
|
|||
Open3Response as CreateTimerResponse,
|
||||
ExitRequest as DeleteRequest,
|
||||
ExitResponse as DeleteResponse,
|
||||
ExitAllRequest as DeleteMultiRequest,
|
||||
ExitAllResponse as DeleteMultiResponse,
|
||||
UpdateRequest,
|
||||
UpdateResponse,
|
||||
InviteRequest,
|
||||
|
@ -20,7 +22,8 @@ import {
|
|||
ExitForcingRequest,
|
||||
ExitForcingResponse,
|
||||
UpdateTimerSetRequest,
|
||||
UpdateTimerSetResponse
|
||||
UpdateTimerSetResponse,
|
||||
UserInfoShort
|
||||
} from '@ucap/protocol-room';
|
||||
|
||||
/**
|
||||
|
@ -32,6 +35,7 @@ export const rooms = createAction(
|
|||
);
|
||||
/**
|
||||
* Success of rooms request
|
||||
* RoomUserData is detail type.
|
||||
*/
|
||||
export const roomsSuccess = createAction(
|
||||
'[ucap::chat::room] rooms Success',
|
||||
|
@ -51,6 +55,31 @@ export const roomsFailure = createAction(
|
|||
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
|
||||
*/
|
||||
|
@ -142,6 +171,28 @@ export const delFailure = createAction(
|
|||
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
|
||||
*/
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
OpenResponse as CreateResponse,
|
||||
Open3Response as CreateTimerResponse,
|
||||
ExitResponse as DeleteResponse,
|
||||
ExitAllResponse as DeleteMultiResponse,
|
||||
UpdateResponse,
|
||||
InviteResponse,
|
||||
ExitForcingResponse,
|
||||
|
@ -67,7 +68,12 @@ import {
|
|||
expelFailure,
|
||||
updateTimeRoomInterval,
|
||||
updateTimeRoomIntervalSuccess,
|
||||
updateTimeRoomIntervalFailure
|
||||
updateTimeRoomIntervalFailure,
|
||||
rooms2Success,
|
||||
rooms2Failure,
|
||||
delMulti,
|
||||
delMultiSuccess,
|
||||
delMultiFailure
|
||||
} from './actions';
|
||||
|
||||
@Injectable()
|
||||
|
@ -75,7 +81,7 @@ export class Effects {
|
|||
sessionCreatedForRooms$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
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),
|
||||
withLatestFrom(this.store.pipe(select(RoomSelector.roomsSyncDate))),
|
||||
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
|
||||
.room({ syncDate, localeCode: action.localeCode })
|
||||
.room2({ syncDate, localeCode: action.localeCode })
|
||||
.pipe(
|
||||
map(res =>
|
||||
roomsSuccess({
|
||||
map((res) => {
|
||||
return rooms2Success({
|
||||
roomList: res.roomList,
|
||||
roomUserInfoMap: res.roomUserInfoMap,
|
||||
syncDate: res.res.syncDate
|
||||
})
|
||||
),
|
||||
catchError(error => of(roomsFailure({ error })))
|
||||
});
|
||||
}),
|
||||
catchError((error) => of(rooms2Failure({ error })))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
@ -103,16 +124,16 @@ export class Effects {
|
|||
room$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(room),
|
||||
map(action => action.req),
|
||||
switchMap(req => {
|
||||
map((action) => action.req),
|
||||
switchMap((req) => {
|
||||
return this.roomProtocolService.info(req).pipe(
|
||||
map(res =>
|
||||
map((res) =>
|
||||
roomSuccess({
|
||||
roomInfo: res.roomInfo,
|
||||
userInfoList: res.userInfoList
|
||||
})
|
||||
),
|
||||
catchError(error => of(roomFailure({ error })))
|
||||
catchError((error) => of(roomFailure({ error })))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
@ -121,13 +142,13 @@ export class Effects {
|
|||
create$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(create),
|
||||
map(action => action.req),
|
||||
exhaustMap(req => {
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) => {
|
||||
return this.roomProtocolService.open(req).pipe(
|
||||
map((res: CreateResponse) => {
|
||||
return createSuccess({ res });
|
||||
}),
|
||||
catchError(error => of(createFailure({ error })))
|
||||
catchError((error) => of(createFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
|
@ -136,13 +157,13 @@ export class Effects {
|
|||
createTimer$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(createTimer),
|
||||
map(action => action.req),
|
||||
exhaustMap(req => {
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) => {
|
||||
return this.roomProtocolService.open3(req).pipe(
|
||||
map((res: CreateTimerResponse) => {
|
||||
return createTimerSuccess({ res });
|
||||
}),
|
||||
catchError(error => of(createTimerFailure({ error })))
|
||||
catchError((error) => of(createTimerFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
|
@ -151,14 +172,30 @@ export class Effects {
|
|||
del$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(del),
|
||||
map(action => action.req),
|
||||
exhaustMap(req => {
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) => {
|
||||
return this.roomProtocolService.exit(req).pipe(
|
||||
switchMap((res: DeleteResponse) => [
|
||||
close({ roomIds: [res.roomId] }),
|
||||
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(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(update),
|
||||
map(action => action.req),
|
||||
exhaustMap(req => {
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) => {
|
||||
return this.roomProtocolService.update(req).pipe(
|
||||
map((res: UpdateResponse) => {
|
||||
return updateSuccess({ res });
|
||||
}),
|
||||
catchError(error => of(updateFailure({ error })))
|
||||
catchError((error) => of(updateFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
|
@ -182,7 +219,7 @@ export class Effects {
|
|||
excludeUser$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(excludeUser),
|
||||
map(action =>
|
||||
map((action) =>
|
||||
excludeUserSuccess({
|
||||
roomId: action.roomId,
|
||||
userSeqs: action.userSeqs
|
||||
|
@ -194,21 +231,21 @@ export class Effects {
|
|||
open$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(open),
|
||||
map(action => openSuccess({ roomIds: [...action.roomIds] }))
|
||||
map((action) => openSuccess({ roomIds: [...action.roomIds] }))
|
||||
);
|
||||
});
|
||||
|
||||
close$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(close),
|
||||
map(action => closeSuccess({ roomIds: [...action.roomIds] }))
|
||||
map((action) => closeSuccess({ roomIds: [...action.roomIds] }))
|
||||
);
|
||||
});
|
||||
|
||||
inviteOrCreate$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(inviteOrCreate),
|
||||
map(action => {
|
||||
map((action) => {
|
||||
const roomInfo = action.roomInfo;
|
||||
const localeCode = action.localeCode;
|
||||
|
||||
|
@ -235,7 +272,7 @@ export class Effects {
|
|||
invite$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(invite),
|
||||
exhaustMap(action => {
|
||||
exhaustMap((action) => {
|
||||
const req = action.req;
|
||||
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(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(expel),
|
||||
map(action => action.req),
|
||||
exhaustMap(req => {
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) => {
|
||||
return this.roomProtocolService.exitForcing(req).pipe(
|
||||
map((res: ExitForcingResponse) => {
|
||||
return expelSuccess({ res });
|
||||
}),
|
||||
catchError(error => of(expelFailure({ error })))
|
||||
catchError((error) => of(expelFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
|
@ -276,13 +313,13 @@ export class Effects {
|
|||
updateTimeRoomInterval$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(updateTimeRoomInterval),
|
||||
map(action => action.req),
|
||||
exhaustMap(req => {
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) => {
|
||||
return this.roomProtocolService.updateTimerSet(req).pipe(
|
||||
map((res: UpdateTimerSetResponse) => {
|
||||
return updateTimeRoomIntervalSuccess({ res });
|
||||
}),
|
||||
catchError(error => of(updateTimeRoomIntervalFailure({ error })))
|
||||
catchError((error) => of(updateTimeRoomIntervalFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
|
@ -291,7 +328,7 @@ export class Effects {
|
|||
inviteNotification$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(inviteNotification),
|
||||
map(action =>
|
||||
map((action) =>
|
||||
room({
|
||||
req: {
|
||||
roomId: action.noti.roomId,
|
||||
|
@ -306,7 +343,7 @@ export class Effects {
|
|||
exitNotification$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(exitNotification),
|
||||
switchMap(action => {
|
||||
switchMap((action) => {
|
||||
if (action.userSeq === action.senderSeq) {
|
||||
return [
|
||||
close({ roomIds: [action.roomId] }),
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
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';
|
||||
|
||||
|
@ -8,14 +12,19 @@ import {
|
|||
initialState,
|
||||
adapterRoom,
|
||||
adapterRoomUser,
|
||||
RoomUserMap
|
||||
RoomUserMap,
|
||||
RoomUserShortMap,
|
||||
adapterRoomUserShort
|
||||
} from './state';
|
||||
import {
|
||||
roomsSuccess,
|
||||
roomSuccess,
|
||||
excludeUser,
|
||||
excludeUserSuccess,
|
||||
delSuccess
|
||||
delSuccess,
|
||||
rooms2Success,
|
||||
delMultiSuccess,
|
||||
updateSuccess
|
||||
} from './actions';
|
||||
|
||||
export const reducer = createReducer(
|
||||
|
@ -40,7 +49,7 @@ export const reducer = createReducer(
|
|||
const value = state.rooms.entities[key];
|
||||
unreadTotal += isNaN(value.noReadCnt) ? 0 : value.noReadCnt;
|
||||
}
|
||||
action.roomList.map(item => (unreadTotal += item.noReadCnt));
|
||||
action.roomList.map((item) => (unreadTotal += item.noReadCnt));
|
||||
|
||||
return {
|
||||
...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) => {
|
||||
const roomUserMapList: RoomUserMap[] = [];
|
||||
|
||||
|
@ -82,17 +138,27 @@ export const reducer = createReducer(
|
|||
on(excludeUserSuccess, (state, action) => {
|
||||
const roomId = action.roomId;
|
||||
const roomUserMap = state.roomUsers.entities[roomId];
|
||||
const roomUserMapShort = state.roomUsersShort.entities[roomId];
|
||||
|
||||
const userInfos: RoomUserInfo[] = [...roomUserMap.userInfos];
|
||||
const userInfosShort: RoomUserInfoShort[] = [...roomUserMapShort.userInfos];
|
||||
|
||||
action.userSeqs.forEach(userSeq => {
|
||||
action.userSeqs.forEach((userSeq) => {
|
||||
const userInfo: RoomUserInfo = userInfos.find(
|
||||
u => userSeq === String(u.seq)
|
||||
(u) => userSeq === String(u.seq)
|
||||
);
|
||||
|
||||
if (!!userInfo && !!userInfo.seq) {
|
||||
userInfo.isJoinRoom = false;
|
||||
}
|
||||
|
||||
const userInfoShort: RoomUserInfoShort = userInfosShort.find(
|
||||
(u) => userSeq === String(u.seq)
|
||||
);
|
||||
|
||||
if (!!userInfoShort && !!userInfoShort.seq) {
|
||||
userInfoShort.isJoinRoom = false;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
|
@ -108,10 +174,35 @@ export const reducer = createReducer(
|
|||
{
|
||||
...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) => {
|
||||
const roomId = action.res.roomId;
|
||||
const room = state.rooms.entities[roomId];
|
||||
|
@ -123,7 +214,27 @@ export const reducer = createReducer(
|
|||
return {
|
||||
...state,
|
||||
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 { EntityState, createEntityAdapter, Dictionary } from '@ngrx/entity';
|
||||
|
||||
import { RoomUserDetailData } from '@ucap/protocol-sync';
|
||||
import { RoomInfo, UserInfo as RoomUserInfo } from '@ucap/protocol-room';
|
||||
import { RoomUserDetailData, RoomUserData } from '@ucap/protocol-sync';
|
||||
import {
|
||||
RoomInfo,
|
||||
UserInfo as RoomUserInfo,
|
||||
UserInfoShort as RoomUserInfoShort
|
||||
} from '@ucap/protocol-room';
|
||||
|
||||
export interface RoomState extends EntityState<RoomInfo> {
|
||||
syncDate: string;
|
||||
}
|
||||
export const adapterRoom = createEntityAdapter<RoomInfo>({
|
||||
selectId: roomInfo => roomInfo.roomId,
|
||||
selectId: (roomInfo) => roomInfo.roomId,
|
||||
sortComparer: (a, b) => {
|
||||
return (
|
||||
moment(b.finalEventDate)
|
||||
.toDate()
|
||||
.getTime() -
|
||||
moment(a.finalEventDate)
|
||||
.toDate()
|
||||
.getTime()
|
||||
moment(b.finalEventDate).toDate().getTime() -
|
||||
moment(a.finalEventDate).toDate().getTime()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -28,24 +28,39 @@ export interface RoomUserMap {
|
|||
userInfos: RoomUserInfo[];
|
||||
}
|
||||
|
||||
export interface RoomUserShortMap {
|
||||
roomId: string;
|
||||
userInfos: RoomUserInfoShort[];
|
||||
}
|
||||
|
||||
export interface RoomUserState extends EntityState<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 {
|
||||
rooms: RoomState;
|
||||
roomUsers: RoomUserState;
|
||||
roomUsersShort: RoomUserShortState;
|
||||
}
|
||||
|
||||
const roomInitialState: RoomState = adapterRoom.getInitialState({
|
||||
syncDate: ''
|
||||
});
|
||||
const roomUserInitialState: RoomUserState = adapterRoomUser.getInitialState({});
|
||||
const roomUserShortInitialState: RoomUserShortState = adapterRoomUserShort.getInitialState(
|
||||
{}
|
||||
);
|
||||
|
||||
export const initialState: State = {
|
||||
rooms: roomInitialState,
|
||||
roomUsers: roomUserInitialState
|
||||
roomUsers: roomUserInitialState,
|
||||
roomUsersShort: roomUserShortInitialState
|
||||
};
|
||||
|
||||
const {
|
||||
|
@ -62,6 +77,13 @@ const {
|
|||
selectTotal: selectTotalForRoomUser
|
||||
} = adapterRoomUser.getSelectors();
|
||||
|
||||
const {
|
||||
selectAll: selectAllForRoomUserShort,
|
||||
selectEntities: selectEntitiesForRoomUserShort,
|
||||
selectIds: selectIdsForRoomUserShort,
|
||||
selectTotal: selectTotalForRoomUserShort
|
||||
} = adapterRoomUserShort.getSelectors();
|
||||
|
||||
export function selectors<S>(selector: Selector<any, State>) {
|
||||
const selectRooms = createSelector(selector, (state: State) => state.rooms);
|
||||
|
||||
|
@ -70,6 +92,11 @@ export function selectors<S>(selector: Selector<any, State>) {
|
|||
(state: State) => state.roomUsers
|
||||
);
|
||||
|
||||
const selectRoomUsersShort = createSelector(
|
||||
selector,
|
||||
(state: State) => state.roomUsersShort
|
||||
);
|
||||
|
||||
return {
|
||||
rooms: createSelector(selectRooms, selectAllForRoom),
|
||||
room: createSelector(
|
||||
|
@ -78,7 +105,10 @@ export function selectors<S>(selector: Selector<any, State>) {
|
|||
(roomState: RoomState, entities: Dictionary<RoomInfo>, roomId: string) =>
|
||||
entities && entities[roomId]
|
||||
),
|
||||
roomsSyncDate: createSelector(selectRooms, roomState => roomState.syncDate),
|
||||
roomsSyncDate: createSelector(
|
||||
selectRooms,
|
||||
(roomState) => roomState.syncDate
|
||||
),
|
||||
roomUsers: createSelector(selectRoomUsers, selectAllForRoomUser),
|
||||
roomUser: createSelector(
|
||||
selectRoomUsers,
|
||||
|
@ -89,6 +119,19 @@ export function selectors<S>(selector: Selector<any, State>) {
|
|||
roomId: string
|
||||
) => entities && entities[roomId]
|
||||
),
|
||||
roomUsersShort: createSelector(
|
||||
selectRoomUsersShort,
|
||||
selectAllForRoomUserShort
|
||||
),
|
||||
roomUserShort: createSelector(
|
||||
selectRoomUsersShort,
|
||||
selectEntitiesForRoomUserShort,
|
||||
(
|
||||
roomUserShortState: RoomUserShortState,
|
||||
entities: Dictionary<RoomUserData>,
|
||||
roomId: string
|
||||
) => entities && entities[roomId]
|
||||
),
|
||||
unreadTotal: createSelector(
|
||||
selectRooms,
|
||||
selectAllForRoom,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-store-group",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.9",
|
||||
"publishConfig": {
|
||||
"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 { DepartmentSelector } from '@ucap/ng-store-organization';
|
||||
import {
|
||||
DepartmentSelector,
|
||||
PresenceActions
|
||||
} from '@ucap/ng-store-organization';
|
||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
||||
|
||||
import * as groupActions from '../group/actions';
|
||||
|
@ -53,7 +56,7 @@ export class Effects {
|
|||
sessionCreatedForBuddy2$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(LoginActions.sessionCreated),
|
||||
map(action => buddy2())
|
||||
map((action) => buddy2())
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -63,13 +66,21 @@ export class Effects {
|
|||
withLatestFrom(this.store.pipe(select(BuddySelector.buddySyncDate))),
|
||||
switchMap(([action, syncDate]) => {
|
||||
return this.syncProtocolService.buddy2({ syncDate }).pipe(
|
||||
map(res =>
|
||||
buddy2Success({
|
||||
map((res) => {
|
||||
// Buddy Presence
|
||||
this.store.dispatch(
|
||||
PresenceActions.bulkInfo({
|
||||
divCd: 'buddyBulk',
|
||||
userSeqs: res.buddyInfos.map((userInfo) => userInfo.seq + '')
|
||||
})
|
||||
);
|
||||
|
||||
return buddy2Success({
|
||||
buddyList: res.buddyInfos,
|
||||
syncDate: res.res.syncDate
|
||||
})
|
||||
),
|
||||
catchError(error => of(buddy2Failure({ error })))
|
||||
});
|
||||
}),
|
||||
catchError((error) => of(buddy2Failure({ error })))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
@ -78,13 +89,13 @@ export class Effects {
|
|||
add$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(add),
|
||||
map(action => action.req),
|
||||
exhaustMap(req =>
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) =>
|
||||
this.buddyProtocolService.add(req).pipe(
|
||||
map((res: BuddyAddResponse) => {
|
||||
return buddy2();
|
||||
}),
|
||||
catchError(error => of(addFailure({ error })))
|
||||
catchError((error) => of(addFailure({ error })))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -93,13 +104,13 @@ export class Effects {
|
|||
del$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(del),
|
||||
map(action => action.req),
|
||||
exhaustMap(req =>
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) =>
|
||||
this.buddyProtocolService.del(req).pipe(
|
||||
map((res: BuddyDelResponse) => {
|
||||
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.fixedGroupSeqs &&
|
||||
this.moduleConfig.fixedGroupSeqs.filter(
|
||||
fixedGroupSeq => fixedGroupSeq === group.seq
|
||||
(fixedGroupSeq) => fixedGroupSeq === group.seq
|
||||
).length > 0
|
||||
) {
|
||||
// skip;;
|
||||
|
@ -134,7 +145,7 @@ export class Effects {
|
|||
groupSeq: group.seq,
|
||||
groupName: group.name,
|
||||
userSeqs: group.userSeqs.filter(
|
||||
userSeq => userSeq !== String(req.seq)
|
||||
(userSeq) => userSeq !== String(req.seq)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -147,8 +158,9 @@ export class Effects {
|
|||
if (
|
||||
!!this.moduleConfig.useMyDeptGroup &&
|
||||
this.moduleConfig.useMyDeptGroup &&
|
||||
myDeptUserList.filter(deptUser => deptUser.seq === String(req.seq))
|
||||
.length > 0
|
||||
myDeptUserList.filter(
|
||||
(deptUser) => deptUser.seq === String(req.seq)
|
||||
).length > 0
|
||||
) {
|
||||
// skip;;
|
||||
} else {
|
||||
|
@ -174,13 +186,13 @@ export class Effects {
|
|||
update$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(update),
|
||||
map(action => action.req),
|
||||
exhaustMap(req =>
|
||||
map((action) => action.req),
|
||||
exhaustMap((req) =>
|
||||
this.buddyProtocolService.update(req).pipe(
|
||||
map((res: BuddyUpdateResponse) => {
|
||||
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) {
|
||||
const addBuddyList: string[] = [];
|
||||
targetUserSeqs.forEach(userSeq => {
|
||||
targetUserSeqs.forEach((userSeq) => {
|
||||
if (!buddyList) {
|
||||
addBuddyList.push(userSeq);
|
||||
return;
|
||||
}
|
||||
|
||||
const index = buddyList.findIndex(b => String(b.seq) === userSeq);
|
||||
const index = buddyList.findIndex(
|
||||
(b) => String(b.seq) === userSeq
|
||||
);
|
||||
if (-1 < index) {
|
||||
addBuddyList.push(userSeq);
|
||||
return;
|
||||
|
@ -230,13 +244,15 @@ export class Effects {
|
|||
|
||||
if (!!targetUserSeqs && 0 < targetUserSeqs.length) {
|
||||
const addBuddyList: string[] = [];
|
||||
targetUserSeqs.forEach(userSeq => {
|
||||
targetUserSeqs.forEach((userSeq) => {
|
||||
if (!buddyList) {
|
||||
addBuddyList.push(userSeq);
|
||||
return;
|
||||
}
|
||||
|
||||
const index = buddyList.findIndex(b => String(b.seq) === userSeq);
|
||||
const index = buddyList.findIndex(
|
||||
(b) => String(b.seq) === userSeq
|
||||
);
|
||||
if (-1 < index) {
|
||||
addBuddyList.push(userSeq);
|
||||
return;
|
||||
|
@ -249,7 +265,7 @@ export class Effects {
|
|||
}
|
||||
|
||||
if (!!userSeqsForDelete && 0 < userSeqsForDelete.length) {
|
||||
userSeqsForDelete.forEach(userSeq => {
|
||||
userSeqsForDelete.forEach((userSeq) => {
|
||||
this.store.dispatch(
|
||||
update({ req: { seq: Number(userSeq), isFavorit: false } })
|
||||
);
|
||||
|
|
|
@ -31,7 +31,7 @@ import { _MODULE_CONFIG } from '../../config/token';
|
|||
|
||||
import * as buddyActions from '../buddy/actions';
|
||||
|
||||
import { GroupSelector } from '../state';
|
||||
import { GroupSelector, BuddySelector } from '../state';
|
||||
|
||||
import {
|
||||
groups,
|
||||
|
@ -57,7 +57,7 @@ export class Effects {
|
|||
sessionCreatedForGroups$ = createEffect(() => {
|
||||
return this.actions$.pipe(
|
||||
ofType(LoginActions.sessionCreated),
|
||||
map(action => groups())
|
||||
map((action) => groups())
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -67,13 +67,13 @@ export class Effects {
|
|||
withLatestFrom(this.store.pipe(select(GroupSelector.groupSyncDate))),
|
||||
switchMap(([action, syncDate]) => {
|
||||
return this.syncProtocolService.group2({ syncDate }).pipe(
|
||||
map(res =>
|
||||
map((res) =>
|
||||
groupsSuccess({
|
||||
groupList: res.groupInfos,
|
||||
syncDate: res.res.syncDate
|
||||
})
|
||||
),
|
||||
catchError(error => of(groupsFailure({ error })))
|
||||
catchError((error) => of(groupsFailure({ error })))
|
||||
);
|
||||
})
|
||||
);
|
||||
|
@ -82,7 +82,8 @@ export class Effects {
|
|||
create$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(create),
|
||||
exhaustMap(action => {
|
||||
withLatestFrom(this.store.pipe(select(BuddySelector.buddies))),
|
||||
exhaustMap(([action, buddies]) => {
|
||||
return this.groupProtocolService
|
||||
.add({
|
||||
groupName: action.groupName
|
||||
|
@ -92,6 +93,19 @@ export class Effects {
|
|||
const actions: any[] = [];
|
||||
|
||||
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(
|
||||
update({
|
||||
req: {
|
||||
|
@ -108,7 +122,7 @@ export class Effects {
|
|||
|
||||
return actions;
|
||||
}),
|
||||
catchError(error => of(createFailure({ error })))
|
||||
catchError((error) => of(createFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
|
@ -128,7 +142,7 @@ export class Effects {
|
|||
|
||||
// Del Buddy
|
||||
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
|
||||
) {
|
||||
userSeqsForDelete = userSeqsForDelete.filter(
|
||||
delbuddy =>
|
||||
myDeptUserList.filter(deptUser => deptUser.seq === delbuddy)
|
||||
(delbuddy) =>
|
||||
myDeptUserList.filter((deptUser) => deptUser.seq === delbuddy)
|
||||
.length === 0
|
||||
);
|
||||
}
|
||||
userSeqsForDelete = userSeqsForDelete.filter(delbuddy => {
|
||||
userSeqsForDelete = userSeqsForDelete.filter((delbuddy) => {
|
||||
let exist = false;
|
||||
|
||||
for (const group of groupList) {
|
||||
if (
|
||||
group.seq !== targetGroup.seq &&
|
||||
group.userSeqs.filter(v => v === delbuddy).length > 0
|
||||
group.userSeqs.filter((v) => v === delbuddy).length > 0
|
||||
) {
|
||||
exist = true;
|
||||
break;
|
||||
|
@ -184,10 +198,10 @@ export class Effects {
|
|||
withLatestFrom(this.store.pipe(select(GroupSelector.groups))),
|
||||
exhaustMap(([action, groupList]) => {
|
||||
// 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;
|
||||
action.targetUserSeq.forEach(trgtSeq => {
|
||||
action.targetUserSeq.forEach((trgtSeq) => {
|
||||
if (toTrgtUserSeqs.indexOf(trgtSeq) > -1) {
|
||||
// ignore
|
||||
} else {
|
||||
|
@ -205,7 +219,7 @@ export class Effects {
|
|||
exhaustMap((resTo: GroupUpdateResponse) => {
|
||||
// del from
|
||||
const fromGroup = groupList.find(
|
||||
g => g.seq === action.fromGroup.seq
|
||||
(g) => g.seq === action.fromGroup.seq
|
||||
);
|
||||
|
||||
const fromTrgtUserSeqs = fromGroup.userSeqs;
|
||||
|
@ -215,17 +229,17 @@ export class Effects {
|
|||
groupSeq: action.fromGroup.seq,
|
||||
groupName: action.fromGroup.name,
|
||||
userSeqs: fromTrgtUserSeqs.filter(
|
||||
trgtSeq => action.targetUserSeq.indexOf(trgtSeq) < 0
|
||||
(trgtSeq) => action.targetUserSeq.indexOf(trgtSeq) < 0
|
||||
)
|
||||
})
|
||||
.pipe(
|
||||
map((resFrom: GroupUpdateResponse) => {
|
||||
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 (
|
||||
!!this.moduleConfig.useMyDeptGroup &&
|
||||
this.moduleConfig.useMyDeptGroup
|
||||
this.moduleConfig.useMyDeptGroup &&
|
||||
!!myDeptUserList &&
|
||||
myDeptUserList.length > 0
|
||||
) {
|
||||
userSeqsForDelete = targetUserSeqs.filter(
|
||||
delbuddy =>
|
||||
myDeptUserList.filter(deptUser => deptUser.seq === delbuddy)
|
||||
(delbuddy) =>
|
||||
myDeptUserList.filter((deptUser) => deptUser.seq === delbuddy)
|
||||
.length === 0
|
||||
);
|
||||
}
|
||||
|
||||
userSeqsForDelete = targetUserSeqs.filter(delbuddy => {
|
||||
userSeqsForDelete = targetUserSeqs.filter((delbuddy) => {
|
||||
let exist = false;
|
||||
for (const group of groupList) {
|
||||
if (
|
||||
group.seq !== action.group.seq &&
|
||||
group.userSeqs.filter(v => v === delbuddy).length > 0
|
||||
group.userSeqs.filter((v) => v === delbuddy).length > 0
|
||||
) {
|
||||
exist = true;
|
||||
break;
|
||||
|
@ -271,7 +287,7 @@ export class Effects {
|
|||
|
||||
if (userSeqsForDelete.length > 0) {
|
||||
// 즐겨찾기 해제.
|
||||
userSeqsForDelete.forEach(buddySeq => {
|
||||
userSeqsForDelete.forEach((buddySeq) => {
|
||||
this.store.dispatch(
|
||||
buddyActions.update({
|
||||
req: {
|
||||
|
@ -296,7 +312,7 @@ export class Effects {
|
|||
map((res: GroupDelResponse) => {
|
||||
return delSuccess({ res });
|
||||
}),
|
||||
catchError(error => of(delFailure({ error })))
|
||||
catchError((error) => of(delFailure({ error })))
|
||||
);
|
||||
})
|
||||
)
|
||||
|
@ -305,13 +321,13 @@ export class Effects {
|
|||
update$ = createEffect(() =>
|
||||
this.actions$.pipe(
|
||||
ofType(update),
|
||||
map(action => action.req),
|
||||
concatMap(req =>
|
||||
map((action) => action.req),
|
||||
concatMap((req) =>
|
||||
this.groupProtocolService.update2(req).pipe(
|
||||
map((res: GroupUpdateResponse) => {
|
||||
return groups();
|
||||
}),
|
||||
catchError(error => of(updateFailure({ error })))
|
||||
catchError((error) => of(updateFailure({ error })))
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
|
@ -6,9 +6,13 @@
|
|||
"umdModuleIds": {
|
||||
"@ngrx/store": "@ngrx/store",
|
||||
"@ngrx/effects": "@ngrx/effects",
|
||||
"@ngrx/entity": "@ngrx/entity",
|
||||
|
||||
"@ucap/protocol-query": "@ucap/protocol-query",
|
||||
"@ucap/protocol-status": "@ucap/protocol-status",
|
||||
"@ucap/ng-api-external": "@ucap/ng-api-external",
|
||||
"@ucap/ng-protocol-query": "@ucap/ng-protocol-query",
|
||||
"@ucap/ng-protocol-status": "@ucap/ng-protocol-status",
|
||||
"@ucap/ng-store-authentication": "@ucap/ng-store-authentication"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-store-organization",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.8",
|
||||
"publishConfig": {
|
||||
"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 CompanyEffects } from './company/effects';
|
||||
import { Effects as DepartmentEffects } from './department/effects';
|
||||
import { Effects as PresenceEffects } from './presence/effects';
|
||||
|
||||
export const effects: Type<any>[] = [
|
||||
CommonEffects,
|
||||
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 CompanyReducer } from './company/reducers';
|
||||
import { reducer as DepartmentReducer } from './department/reducers';
|
||||
import { reducer as PresenceReducer } from './presence/reducers';
|
||||
|
||||
export function reducers(state: any | undefined, action: Action) {
|
||||
return combineReducers({
|
||||
common: CommonReducer,
|
||||
company: CompanyReducer,
|
||||
department: DepartmentReducer
|
||||
department: DepartmentReducer,
|
||||
presence: PresenceReducer
|
||||
})(state, action);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { createFeatureSelector, createSelector } from '@ngrx/store';
|
|||
import * as CommonState from './common/state';
|
||||
import * as CompanyState from './company/state';
|
||||
import * as DepartmentState from './department/state';
|
||||
import * as PresenceState from './presence/state';
|
||||
|
||||
export const KEY_FEATURE = 'organization';
|
||||
|
||||
|
@ -10,6 +11,7 @@ export interface State {
|
|||
common: CommonState.State;
|
||||
company: CompanyState.State;
|
||||
department: DepartmentState.State;
|
||||
presence: PresenceState.State;
|
||||
}
|
||||
|
||||
export const Selector = createFeatureSelector<State>(KEY_FEATURE);
|
||||
|
@ -25,3 +27,7 @@ export const CompanySelector = CompanyState.selectors(
|
|||
export const DepartmentSelector = DepartmentState.selectors(
|
||||
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 CompanyActions from './lib/store/company/actions';
|
||||
import * as DepartmentActions from './lib/store/department/actions';
|
||||
import * as PresenceActions from './lib/store/presence/actions';
|
||||
|
||||
export * from './lib/config/module-config';
|
||||
|
||||
export { CommonActions, CompanyActions, DepartmentActions };
|
||||
export { CommonActions, CompanyActions, DepartmentActions, PresenceActions };
|
||||
|
||||
export * from './lib/store/state';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-ui-authentication",
|
||||
"version": "0.0.19",
|
||||
"version": "0.0.20",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
@ -10,6 +10,7 @@
|
|||
"@angular/core": "^9.0.2",
|
||||
"@angular/material": "^9.0.0",
|
||||
"@ucap/core": "~0.0.1",
|
||||
"@ucap/uc-scss": "~0.0.1",
|
||||
"@ucap/ng-i18n": "~0.0.1",
|
||||
"@ucap/ng-ui": "~0.0.1",
|
||||
"tslib": "^1.10.0"
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<div class="ucap-login-container">
|
||||
<div class="ucap-authentication-login-container">
|
||||
<ng-content select="[ucapAuthenticationLogin='header']"></ng-content>
|
||||
|
||||
<form name="loginForm" [formGroup]="loginForm" novalidate>
|
||||
<mat-form-field
|
||||
<div
|
||||
class="ucap-authentication-login-input-field"
|
||||
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
||||
class="login-company"
|
||||
>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'login.fields.company' | ucapI18n }}</mat-label>
|
||||
<mat-select
|
||||
[formControl]="companyCodeFormControl"
|
||||
|
@ -20,10 +21,11 @@
|
|||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="input-lineless">
|
||||
<!--<span class="icon-img"><i class="mid mdi-account-tie-outline"></i></span>-->
|
||||
<span class="icon-img">
|
||||
<div class="ucap-authentication-login-input-field">
|
||||
<!--<span class="input-icon"><i class="mid mdi-account-tie-outline"></i></span>-->
|
||||
<span class="ucap-authentication-login-input-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20.82 22">
|
||||
<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"
|
||||
|
@ -36,15 +38,15 @@
|
|||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<mat-form-field class="login-id">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'login.fields.loginId' | ucapI18n }}</mat-label>
|
||||
<input matInput [formControl]="loginIdFormControl" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="input-lineless">
|
||||
<!--<span class="icon-img"><i class="mid mdi-lock-outline"></i></span>-->
|
||||
<span class="icon-img">
|
||||
<div class="ucap-authentication-login-input-field">
|
||||
<!--<span class="input-icon"><i class="mid mdi-lock-outline"></i></span>-->
|
||||
<span class="ucap-authentication-login-input-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 22.07">
|
||||
<title>login-lock</title>
|
||||
<path
|
||||
|
@ -71,7 +73,7 @@
|
|||
<rect x="9.08" y="12.86" width="1.84" height="3.72" />
|
||||
</svg>
|
||||
</span>
|
||||
<mat-form-field class="login-pw">
|
||||
<mat-form-field>
|
||||
<mat-label>{{ 'login.fields.loginPw' | ucapI18n }}</mat-label>
|
||||
<input
|
||||
matInput
|
||||
|
@ -82,13 +84,14 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="error-container">
|
||||
<div class="ucap-authentication-login-error-container">
|
||||
<mat-error
|
||||
*ngIf="
|
||||
companyCodeFormControl.dirty &&
|
||||
companyCodeFormControl.invalid &&
|
||||
companyCodeFormControl.hasError('required')
|
||||
"
|
||||
class="ucap-authentication-login-error"
|
||||
>
|
||||
{{ 'login.errors.requireCompany' | ucapI18n }}
|
||||
</mat-error>
|
||||
|
@ -98,6 +101,7 @@
|
|||
loginIdFormControl.invalid &&
|
||||
loginIdFormControl.hasError('required')
|
||||
"
|
||||
class="ucap-authentication-login-error"
|
||||
>
|
||||
{{ 'login.errors.requireLoginId' | ucapI18n }}
|
||||
</mat-error>
|
||||
|
@ -107,17 +111,18 @@
|
|||
loginPwFormControl.invalid &&
|
||||
loginPwFormControl.hasError('required')
|
||||
"
|
||||
class="ucap-authentication-login-error"
|
||||
>
|
||||
{{ 'login.errors.requireLoginPw' | ucapI18n }}
|
||||
</mat-error>
|
||||
<mat-error *ngIf="loginFailed">
|
||||
<mat-error *ngIf="loginFailed" class="ucap-authentication-login-error">
|
||||
{{ 'login.errors.failed' | ucapI18n }}
|
||||
</mat-error>
|
||||
</div>
|
||||
|
||||
<button
|
||||
mat-raised-button
|
||||
class="submit-button bg-accent-dark"
|
||||
class="ucap-authentication-login-button-submit"
|
||||
aria-label="LOG IN"
|
||||
[disabled]="
|
||||
loginForm.invalid ||
|
||||
|
|
|
@ -1,67 +1,30 @@
|
|||
$desktop-l-width: 1440px;
|
||||
$tablet-l-width: 1024px;
|
||||
$tablet-s-width: 768px;
|
||||
$mob-l-width: 640px;
|
||||
$login-max-height: 800px;
|
||||
@import '~@ucap/ui-scss/ucap';
|
||||
|
||||
@mixin desktop-m {
|
||||
@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%;
|
||||
.ucap-authentication-login-container {
|
||||
top: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 0px;
|
||||
font-size: 14px;
|
||||
|
||||
.mat-title {
|
||||
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;
|
||||
}
|
||||
text-align: center;
|
||||
|
||||
form {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
|
||||
.input-lineless {
|
||||
.ucap-authentication-login-input-field {
|
||||
background-color: #e0e0e0;
|
||||
padding: 14px 20px;
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
@media screen and (max-width: #{$tablet-s-width}) {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
.icon-img {
|
||||
|
||||
.ucap-authentication-login-input-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 10px;
|
||||
|
@ -74,33 +37,15 @@ $login-max-height: 800px;
|
|||
mat-form-field {
|
||||
width: 100%;
|
||||
color: #333333;
|
||||
&.login-pw {
|
||||
//margin-top: 10px;
|
||||
@media screen and (max-width: #{$tablet-s-width}) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat-checkbox {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.remember-forgot-password {
|
||||
font-size: 13px;
|
||||
|
||||
.remember-me {
|
||||
}
|
||||
|
||||
.auto-login {
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2vh;
|
||||
.ucap-authentication-login-error-container {
|
||||
.ucap-authentication-login-error {
|
||||
}
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
.ucap-authentication-login-button-submit {
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
margin: 20px 0 10px;
|
||||
|
@ -108,115 +53,6 @@ $login-max-height: 800px;
|
|||
border-radius: 0;
|
||||
line-height: 50px;
|
||||
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 { 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 { CommonModule } from '@angular/common';
|
||||
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 { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
|
||||
import { LogService } from '@ucap/logger';
|
||||
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 {
|
||||
title: 'LoginComponent',
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
BrowserAnimationsModule,
|
||||
|
||||
CommonModule,
|
||||
ReactiveFormsModule,
|
||||
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatSelectModule,
|
||||
|
||||
I18nModule
|
||||
],
|
||||
imports: [BrowserModule, BrowserAnimationsModule, AuthenticationUiModule],
|
||||
providers: [
|
||||
AuthenticationUiModule,
|
||||
{ provide: I18nService, useValue: new I18nService(new LogService({})) },
|
||||
{
|
||||
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",
|
||||
"version": "0.0.27",
|
||||
"version": "0.0.30",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
@ -10,6 +10,7 @@
|
|||
"@angular/core": "^9.0.2",
|
||||
"@angular/material": "^9.0.0",
|
||||
"@ucap/core": "~0.0.1",
|
||||
"@ucap/uc-scss": "~0.0.1",
|
||||
"@ucap/ng-ui": "~0.0.1",
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<div class="ucap-group-expansion-container" fxFlexFill>
|
||||
<cdk-virtual-scroll-viewport #cvsvList perfectScrollbar fxFlexFill>
|
||||
<ng-container
|
||||
*cdkVirtualFor="let node of dataSource.expandedData$"
|
||||
*cdkVirtualFor="
|
||||
let node of dataSource.expandedData$;
|
||||
templateCacheSize: 0
|
||||
"
|
||||
></ng-container>
|
||||
|
||||
<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 { 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 {
|
||||
None = 'None',
|
||||
|
@ -129,6 +132,8 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
this.nodeMap.set(NodeType.Default, []);
|
||||
this.nodeMap.set(NodeType.Buddy, []);
|
||||
|
||||
this.groupList = list;
|
||||
|
||||
for (const item of list) {
|
||||
let nodeType = NodeType.Buddy;
|
||||
if (0 === item.group.seq) {
|
||||
|
@ -186,12 +191,27 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
| UserInfoDN
|
||||
)[] = [];
|
||||
|
||||
@Output()
|
||||
clickMoreMenu = new EventEmitter<{
|
||||
event: MouseEvent;
|
||||
node: FlatNode;
|
||||
}>();
|
||||
|
||||
@Output()
|
||||
checkGroup = new EventEmitter<{
|
||||
isChecked: boolean;
|
||||
groupBuddyList: { group: GroupDetailData; buddyList: UserInfo[] };
|
||||
}>();
|
||||
|
||||
@ViewChild('treeList', { static: false })
|
||||
treeList: MatTree<FlatNode>;
|
||||
|
||||
@ViewChild('cvsvList', { static: false })
|
||||
cvsvList: CdkVirtualScrollViewport;
|
||||
|
||||
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||
psDirectiveRef?: PerfectScrollbarDirective;
|
||||
|
||||
@ContentChild(ExpansionNodeDirective, {
|
||||
read: TemplateRef,
|
||||
static: false
|
||||
|
@ -221,8 +241,11 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
dataSource: VirtualScrollTreeFlatDataSource<GroupNode, FlatNode>;
|
||||
|
||||
NodeType = NodeType;
|
||||
groupList: { group: GroupDetailData; buddyList: UserInfo[] }[];
|
||||
|
||||
private nodeMap: Map<NodeType, GroupNode[]> = new Map();
|
||||
// tslint:disable-next-line: variable-name
|
||||
private _ngOnDestroySubject: Subject<void>;
|
||||
|
||||
constructor(private changeDetectorRef: ChangeDetectorRef) {
|
||||
this.treeControl = new FlatTreeControl<FlatNode>(
|
||||
|
@ -251,12 +274,27 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
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 {}
|
||||
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 {
|
||||
const groupDetail = node.node.groupDetail;
|
||||
|
@ -306,7 +344,20 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
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) =>
|
||||
NodeType.Profile !== node.node.nodeType && 0 === node.level;
|
||||
|
@ -324,4 +375,18 @@ export class ExpansionComponent implements OnInit, OnDestroy {
|
|||
this.dataSource.data = rootNode;
|
||||
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",
|
||||
"version": "0.0.12",
|
||||
"version": "0.0.27",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
@ -10,6 +10,7 @@
|
|||
"@angular/core": "^9.0.2",
|
||||
"@angular/material": "^9.0.0",
|
||||
"@ucap/core": "~0.0.1",
|
||||
"@ucap/uc-scss": "~0.0.1",
|
||||
"@ucap/ng-ui": "~0.0.1",
|
||||
"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
|
||||
class="tree-node-body"
|
||||
[ngClass]="
|
||||
currentDeptSeq === node?.data?.deptInfo.seq ? 'current' : ''
|
||||
currentDeptSeq === node?.data?.deptInfo?.seq ? 'current' : ''
|
||||
"
|
||||
>
|
||||
<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,
|
||||
OnDestroy,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ViewChild,
|
||||
ContentChild,
|
||||
TemplateRef,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef,
|
||||
Directive
|
||||
EventEmitter,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
|
||||
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
|
||||
|
@ -24,6 +21,9 @@ import { VirtualScrollTreeFlatDataSource } from '@ucap/ng-ui';
|
|||
import { LogService } from '@ucap/ng-logger';
|
||||
import { LoginResponse } from '@ucap/protocol-authentication';
|
||||
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 {
|
||||
deptInfo: DeptInfo;
|
||||
|
@ -119,16 +119,25 @@ export class TreeComponent implements OnInit, OnDestroy {
|
|||
// tslint:disable-next-line: variable-name
|
||||
_deptInfoList: DeptInfo[];
|
||||
|
||||
@Output()
|
||||
clickNode = new EventEmitter<DeptInfo>();
|
||||
|
||||
@ViewChild('treeList', { static: false })
|
||||
treeList: MatTree<FlatNode>;
|
||||
|
||||
@ViewChild('cvsvList', { static: false })
|
||||
cvsvList: CdkVirtualScrollViewport;
|
||||
|
||||
@ViewChild(PerfectScrollbarDirective, { static: false })
|
||||
psDirectiveRef?: PerfectScrollbarDirective;
|
||||
|
||||
treeControl: FlatTreeControl<FlatNode>;
|
||||
treeFlattener: MatTreeFlattener<OrganizationNode, FlatNode>;
|
||||
dataSource: VirtualScrollTreeFlatDataSource<OrganizationNode, FlatNode>;
|
||||
|
||||
// tslint:disable-next-line: variable-name
|
||||
private _ngOnDestroySubject: Subject<void>;
|
||||
|
||||
constructor(
|
||||
private changeDetectorRef: ChangeDetectorRef,
|
||||
private logService: LogService
|
||||
|
@ -158,12 +167,27 @@ export class TreeComponent implements OnInit, OnDestroy {
|
|||
}
|
||||
|
||||
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 {}
|
||||
ngOnDestroy(): void {
|
||||
if (!!this._ngOnDestroySubject) {
|
||||
this._ngOnDestroySubject.next();
|
||||
this._ngOnDestroySubject.complete();
|
||||
}
|
||||
}
|
||||
|
||||
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 { 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 { MatRippleModule } from '@angular/material/core';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatTreeModule } from '@angular/material/tree';
|
||||
|
||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||
|
@ -18,17 +21,29 @@ import { UiModule } from '@ucap/ng-ui';
|
|||
import { ModuleConfig } from './config/module-config';
|
||||
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 { TranslatePipe } from './pipes/translate.pipe';
|
||||
|
||||
import { TranslateService } from './services/translate.service';
|
||||
|
||||
const COMPONENTS = [TreeComponent, ProfileListItemComponent];
|
||||
const COMPONENTS = [
|
||||
TreeComponent,
|
||||
Profile01Component,
|
||||
ProfileListItem01Component,
|
||||
ProfileListComponent,
|
||||
SearchForTenantComponent
|
||||
];
|
||||
const DIALOGS = [];
|
||||
const PIPES = [TranslatePipe];
|
||||
const DIRECTIVES = [];
|
||||
const DIRECTIVES = [ProfileListNodeDirective];
|
||||
const SERVICES = [TranslateService];
|
||||
|
||||
@NgModule({
|
||||
|
@ -46,8 +61,11 @@ export class OrganizationUiRootModule {}
|
|||
|
||||
MatButtonModule,
|
||||
MatCheckboxModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatRippleModule,
|
||||
MatSelectModule,
|
||||
MatTreeModule,
|
||||
|
||||
PerfectScrollbarModule,
|
||||
|
|
|
@ -113,7 +113,7 @@ export class TranslateService {
|
|||
|
||||
if (target instanceof Array) {
|
||||
const result: string[] = [];
|
||||
target.forEach(t => {
|
||||
target.forEach((t) => {
|
||||
result.push(this.get(t, key, separator));
|
||||
});
|
||||
return result.join(!!separator ? separator : '');
|
||||
|
@ -127,7 +127,12 @@ export class TranslateService {
|
|||
const langKey = `${key}${lang}`;
|
||||
|
||||
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) {}
|
||||
|
||||
|
|
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/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/pipes/translate.pipe';
|
||||
|
||||
export * from './lib/services/translate.service';
|
||||
|
||||
export * from './lib/types/call.type';
|
||||
|
||||
export * from './lib/organization-ui.module';
|
||||
|
|
|
@ -4,5 +4,7 @@
|
|||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
"peerDependencies": {}
|
||||
"peerDependencies": {
|
||||
"@ucap/uc-scss": "~0.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@ucap/ng-ui",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.11",
|
||||
"publishConfig": {
|
||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||
},
|
||||
|
@ -12,6 +12,7 @@
|
|||
"@ucap/core": "~0.0.1",
|
||||
"@ucap/api": "~0.0.1",
|
||||
"@ucap/protocol-event": "~0.0.1",
|
||||
"@ucap/uc-scss": "~0.0.1",
|
||||
"@ucap/ng-core": "~0.0.1",
|
||||
"@ucap/ng-api-common": "~0.0.1",
|
||||
"autolinker": "^3.13.0",
|
||||
|
|
|
@ -1,39 +1,29 @@
|
|||
import { storiesOf, moduleMetadata } from '@storybook/angular';
|
||||
import { withKnobs, text, object } from '@storybook/addon-knobs';
|
||||
|
||||
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 { UiModule } from '../ui.module';
|
||||
|
||||
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)
|
||||
.addDecorator(withKnobs)
|
||||
.addDecorator(
|
||||
export default {
|
||||
title: 'FloatActionButtonComponent',
|
||||
component: FloatActionButtonComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [BrowserModule, BrowserAnimationsModule, UiModule],
|
||||
providers: []
|
||||
})
|
||||
)
|
||||
.add(
|
||||
'Default',
|
||||
() => {
|
||||
const props: { [K in keyof FloatActionButtonComponent]?: any } = {
|
||||
]
|
||||
};
|
||||
|
||||
export const Text = () => ({
|
||||
props: {
|
||||
buttons: object('Buttons', [
|
||||
{
|
||||
icon: 'chat',
|
||||
|
@ -50,25 +40,5 @@ storiesOf('ui/FloatActionButtonComponent', module)
|
|||
console.log(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 {
|
||||
public transform(
|
||||
value: string,
|
||||
country: CountryCode,
|
||||
country?: CountryCode,
|
||||
mask: boolean = false
|
||||
): 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');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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';
|
||||
|
||||
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 s = phoneNumber.formatNational();
|
||||
let s = value;
|
||||
if (!!phoneNumber) {
|
||||
if (!!phoneNumber.isValid()) {
|
||||
s = phoneNumber.formatNational();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mask) {
|
||||
return s;
|
||||
|
|
Loading…
Reference in New Issue
Block a user