0524 sync

This commit is contained in:
Park Byung Eun 2020-05-24 13:11:32 +09:00
parent b4ef37624c
commit 02c2645e01
231 changed files with 5838 additions and 631 deletions

View File

@ -14,7 +14,23 @@ module.exports = {
rule.use.push({ rule.use.push({
loader: 'sass-resources-loader', loader: 'sass-resources-loader',
options: { options: {
resources: [path.join(__dirname, 'styles.scss')] resources: [
path.join(
__dirname,
'..',
'node_modules',
'@angular/material/prebuilt-themes',
'indigo-pink.css'
),
path.join(
__dirname,
'..',
'node_modules',
'perfect-scrollbar/css',
'perfect-scrollbar.css'
),
path.join(__dirname, 'styles.scss')
]
} }
}); });
} }

View File

@ -0,0 +1,6 @@
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<style></style>

View File

@ -1,6 +1,7 @@
{ {
"extends": "../tsconfig.json", "extends": "../tsconfig.json",
"compilerOptions": { "compilerOptions": {
"emitDecoratorMetadata": true,
"types": ["node"] "types": ["node"]
}, },
"exclude": [ "exclude": [

View File

@ -1264,6 +1264,49 @@
} }
} }
}, },
"ui-material": {
"projectType": "library",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "projects/ui-material",
"sourceRoot": "projects/ui-material/src",
"prefix": "lib",
"architect": {
"build": {
"builder": "@angular-devkit/build-ng-packagr:build",
"options": {
"tsConfig": "projects/ui-material/tsconfig.lib.json",
"project": "projects/ui-material/ng-package.json"
},
"configurations": {
"production": {
"tsConfig": "projects/ui-material/tsconfig.lib.prod.json"
}
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "projects/ui-material/src/test.ts",
"tsConfig": "projects/ui-material/tsconfig.spec.json",
"karmaConfig": "projects/ui-material/karma.conf.js"
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"projects/ui-material/tsconfig.lib.json",
"projects/ui-material/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**"]
}
}
}
},
"ui": { "ui": {
"projectType": "library", "projectType": "library",
"schematics": { "schematics": {

52
package-lock.json generated
View File

@ -3503,18 +3503,18 @@
"dev": true "dev": true
}, },
"@ucap/ng-store-authentication": { "@ucap/ng-store-authentication": {
"version": "file:pack/ucap-ng-store-authentication-0.0.10.tgz", "version": "file:pack/ucap-ng-store-authentication-0.0.11.tgz",
"integrity": "sha512-71Jzg9oYSQ89nShT3F64Mfy4edAbmc/vH/0pbPtT0S4zVH/ZMiC11xnocDQTzAKKtP1kXdNGJt/wv1ZbjMNUQQ==", "integrity": "sha512-YwLMW+GIR3Rs7LaP+1xOH9KLI5jlpZx8oS7Zl32m6Wbym4ModIcGh21rRrqFhNXky4s9zl+ziaTRpFNCKJRgng==",
"dev": true "dev": true
}, },
"@ucap/ng-store-chat": { "@ucap/ng-store-chat": {
"version": "file:pack/ucap-ng-store-chat-0.0.8.tgz", "version": "file:pack/ucap-ng-store-chat-0.0.13.tgz",
"integrity": "sha512-f/Smoy/A97JV2Tsd7IIAeQcNRrthxKT+S01PrVyzjNktDvcxOurnIXGacytU9sfkEMP2rOFCuWKv5p21rvVg6g==", "integrity": "sha512-o+BCCSMxneUenRHEW47sSY22+Zt3lyr202Lg4bub9OVRbW5CVohHez8H+JwK+w+Lf8KbqG32V1ZjKLGclTpboA==",
"dev": true "dev": true
}, },
"@ucap/ng-store-group": { "@ucap/ng-store-group": {
"version": "file:pack/ucap-ng-store-group-0.0.9.tgz", "version": "file:pack/ucap-ng-store-group-0.0.14.tgz",
"integrity": "sha512-KJ4E/kZECAkT6zwA1FiDCrhGevCx9so2Knz08kU49broquZQlOtbV70tPj1fA0XBnaY3hL7uUKXqvAfC7OXj0g==", "integrity": "sha512-sUmdHO7TD5B33DMAoEnelvqbLXTsWPnK2HC8XQ0FdlfGyUtf3kGpwS4BxduUi5wiZckR3hfuBdpCShIhf/qmeQ==",
"dev": true "dev": true
}, },
"@ucap/ng-store-organization": { "@ucap/ng-store-organization": {
@ -3523,33 +3523,39 @@
"dev": true "dev": true
}, },
"@ucap/ng-ui": { "@ucap/ng-ui": {
"version": "file:pack/ucap-ng-ui-0.0.11.tgz", "version": "file:pack/ucap-ng-ui-0.0.19.tgz",
"integrity": "sha512-Vrl6LFQyBwju76RALrF0/dH9p+PJ4nX33dy99QSx6RUJHQ80C0qAmO/DFS1Ln0LZaDc4KoYIHTAOLvX4WLHS5w==", "integrity": "sha512-UuZSzWM4tBR+e5Z/1PFdNenHS00Pn4K7dTafIicG29YjHE5sTXXRqjDCVrKNxJQoMSEPWjUj7qnTgYwP2U83Vg==",
"dev": true "dev": true
}, },
"@ucap/ng-ui-authentication": { "@ucap/ng-ui-authentication": {
"version": "file:pack/ucap-ng-ui-authentication-0.0.20.tgz", "version": "file:pack/ucap-ng-ui-authentication-0.0.24.tgz",
"integrity": "sha512-1FhqwODVL51NC39mwi/2MECMQiyM+qXAlVp2/BrvN+piXXRz/qp62Iwyf/jww+N8aVCe3dqbktJl/tuPacm4/Q==", "integrity": "sha512-6QMJ8dieTnbPANsBzg2Ll3HH5q6Bzl2iSM19yHq8Ct7XOmElrYqrEZmxbDyYO+aCXIAwd2t7vu+rTsHfz3XOQg==",
"dev": true "dev": true
}, },
"@ucap/ng-ui-chat": { "@ucap/ng-ui-chat": {
"version": "file:pack/ucap-ng-ui-chat-0.0.3.tgz", "version": "file:pack/ucap-ng-ui-chat-0.0.9.tgz",
"integrity": "sha512-RRw533PNi0vVCxH1QiogOkJEC/dWGsSFtK3hdJ/ZlcPCT7G81FnnXVY3HPIzu1MhxnSb7KfVJLZhx2RFEfEXWA==", "integrity": "sha512-6qvzcTuylkxVjsqajsLW15laOyOskxVMy238/Ju1yYvwCRyHygwS1i67APoG5tv+SWu+l38f9uWIqzfy7WYHkQ==",
"dev": true "dev": true
}, },
"@ucap/ng-ui-group": { "@ucap/ng-ui-group": {
"version": "file:pack/ucap-ng-ui-group-0.0.30.tgz", "version": "file:pack/ucap-ng-ui-group-0.0.33.tgz",
"integrity": "sha512-LJXwWZ4anO/ScpI+c6IUfwjmcW9tocQq86/cHi8m6SaQwaMq3jltlN2x8WLzRefd72KysLyJHZrX5L1zMLcXog==", "integrity": "sha512-c//Jq00drbMGE3Cgwlh19ScXllGERX2eMVWkVjm311Y8HN9oBBT6Aq2uCM23/76P866oNrhecVDfDHgGYzPRjA==",
"dev": true
},
"@ucap/ng-ui-material": {
"version": "file:pack/ucap-ng-ui-material-0.0.4.tgz",
"integrity": "sha512-ySPULAbP+nQ65hBG2VWZ2H5Hr7muuTGGNXs6A+S3lsxLaW452wM3GNyUBhvUopr8LaSsoOPpp4nK1JeC0fG6pA==",
"dev": true "dev": true
}, },
"@ucap/ng-ui-organization": { "@ucap/ng-ui-organization": {
"version": "file:pack/ucap-ng-ui-organization-0.0.27.tgz", "version": "file:pack/ucap-ng-ui-organization-0.0.55.tgz",
"integrity": "sha512-BC0Jxoxia/+Kho+1oAXE+7+K9iXLhVYVWPVCmmzSRGYGrIZu1skNzzKCNZh6WxYOr7LAJ6ZOp+FgYDfKS7iHMg==", "integrity": "sha512-vfpKd3fbd+I0Od8aB2nIFfjuI7wj3Ziu/uiTEmZxKwZy7uZrNYm59BPbctKW3AQsQ4UtnLofhlBbAA7e9pT80Q==",
"dev": true "dev": true
}, },
"@ucap/ng-ui-skin-default": { "@ucap/ng-ui-skin-default": {
"version": "file:pack/ucap-ng-ui-skin-default-0.0.1.tgz", "version": "file:pack/ucap-ng-ui-skin-default-0.0.1.tgz",
"integrity": "sha512-kfbbfHZ2b1mQL5SflvM9xn86d53NFEx3yTCaQSx16VSEetbQAtb6Qp1/VedDxK6NcWHp+s/Msw5cfuypN1gwug==" "integrity": "sha512-+lHYAzbnyyWh6hf+Ui7vP/ibyGJXuDUO++82jOiOsnPMCl17hkCCag8vQcB/aVgl0iHmebiPshnsL3CptJfeAg==",
"dev": true
}, },
"@ucap/ng-web-socket": { "@ucap/ng-web-socket": {
"version": "file:pack/ucap-ng-web-socket-0.0.2.tgz", "version": "file:pack/ucap-ng-web-socket-0.0.2.tgz",
@ -3592,9 +3598,9 @@
"dev": true "dev": true
}, },
"@ucap/protocol-file": { "@ucap/protocol-file": {
"version": "0.0.4", "version": "0.0.5",
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-file/-/protocol-file-0.0.4.tgz", "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-file/-/protocol-file-0.0.5.tgz",
"integrity": "sha512-oyne/wiKsEsLzJY9VJUO85AwLtgfq0Dt7RyFLThoqquz6O7lCuWszdtvggAJTgn+XM+nF4hczw2Qm2jwjjCZgQ==", "integrity": "sha512-3XRwtlpcrm2oZeckoOzzAUcqADPCGbgdEb4psfNnphTbGX9nYaBUTpWuLYwD3tVe7Wg4fytaHemcAu8yVZUANw==",
"dev": true "dev": true
}, },
"@ucap/protocol-group": { "@ucap/protocol-group": {
@ -3604,9 +3610,9 @@
"dev": true "dev": true
}, },
"@ucap/protocol-info": { "@ucap/protocol-info": {
"version": "0.0.5", "version": "0.0.6",
"resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-info/-/protocol-info-0.0.5.tgz", "resolved": "https://nexus.loafle.net/repository/npm-all/@ucap/protocol-info/-/protocol-info-0.0.6.tgz",
"integrity": "sha512-C2+cJrxdUNLVt3E1a54tCpTmEYE16sUGNvdJGUPywbvLAhjWf71ECjGr16bTMcPr/PxmzjZBu9M2Of39sqngOQ==", "integrity": "sha512-qpt0jfmHDyaMGyADzaDMKbbkfD04yEC0u4KDyoMdjnTi0RXA6cilDRGr9TW/bezB9OxS40yNLK6REfh7aSmcUA==",
"dev": true "dev": true
}, },
"@ucap/protocol-inner": { "@ucap/protocol-inner": {

View File

@ -6,7 +6,7 @@
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
"build": "ng build", "build": "ng build",
"build:all": "npm-run-all -s build:logger build:core build:util:all build:api:all build:protocol:all build:native:all build:store:all build:ui:all build:ui-skin:all", "build:all": "npm-run-all -s build:logger build:core build:util:all build:api:all build:protocol:all build:native:all build:store:all build:ui-material:all build:ui:all build:ui-skin:all",
"build:api:all": "npm-run-all -s build:api-common build:api-external build:api-message build:api-prompt build:api-public build:pi", "build:api:all": "npm-run-all -s build:api-common build:api-external build:api-message build:api-prompt build:api-public build:pi",
"build:api-common": "node ./scripts/build.js api-common", "build:api-common": "node ./scripts/build.js api-common",
"build:api-external": "node ./scripts/build.js api-external", "build:api-external": "node ./scripts/build.js api-external",
@ -45,15 +45,17 @@
"build:store-chat": "node ./scripts/build.js store-chat", "build:store-chat": "node ./scripts/build.js store-chat",
"build:store-group": "node ./scripts/build.js store-group", "build:store-group": "node ./scripts/build.js store-group",
"build:store-organization": "node ./scripts/build.js store-organization", "build:store-organization": "node ./scripts/build.js store-organization",
"build:ui-material:all": "npm-run-all -s build:ui-material",
"build:ui-material": "node ./scripts/build.js ui-material useScssBundle",
"build:ui:all": "npm-run-all -s build:ui build:ui-organization build:ui-authentication build:ui-group build:ui-chat", "build:ui:all": "npm-run-all -s build:ui build:ui-organization build:ui-authentication build:ui-group build:ui-chat",
"build:ui": "node ./scripts/build.js ui", "build:ui": "node ./scripts/build.js ui useScssBundle",
"build:ui-organization": "node ./scripts/build.js ui-organization", "build:ui-organization": "node ./scripts/build.js ui-organization useScssBundle",
"build:ui-authentication": "node ./scripts/build.js ui-authentication", "build:ui-authentication": "node ./scripts/build.js ui-authentication useScssBundle",
"build:ui-group": "node ./scripts/build.js ui-group", "build:ui-group": "node ./scripts/build.js ui-group useScssBundle",
"build:ui-chat": "node ./scripts/build.js ui-chat", "build:ui-chat": "node ./scripts/build.js ui-chat useScssBundle",
"build:ui-skin:all": "npm-run-all -s build:ui-skin-default", "build:ui-skin:all": "npm-run-all -s build:ui-skin-default",
"build:ui-skin-default": "node ./scripts/build.js ui-skin-default useScssBundle", "build:ui-skin-default": "node ./scripts/build.js ui-skin-default useScssBundle",
"publish:all": "npm-run-all -s publish:logger publish:core publish:util:all publish:api:all publish:protocol:all publish:native:all publish:store:all publish:ui:all publish:ui-skin:all", "publish:all": "npm-run-all -s publish:logger publish:core publish:util:all publish:api:all publish:protocol:all publish:native:all publish:store:all publish:ui-material:all publish:ui:all publish:ui-skin:all",
"publish:api:all": "npm-run-all -s publish:api-common publish:api-external publish:api-message publish:api-prompt publish:api-public publish:pi", "publish:api:all": "npm-run-all -s publish:api-common publish:api-external publish:api-message publish:api-prompt publish:api-public publish:pi",
"publish:api-common": "cd ./dist/api-common && npm publish", "publish:api-common": "cd ./dist/api-common && npm publish",
"publish:api-external": "cd ./dist/api-external && npm publish", "publish:api-external": "cd ./dist/api-external && npm publish",
@ -92,6 +94,8 @@
"publish:store-chat": "cd ./dist/store-chat && npm publish", "publish:store-chat": "cd ./dist/store-chat && npm publish",
"publish:store-group": "cd ./dist/store-group && npm publish", "publish:store-group": "cd ./dist/store-group && npm publish",
"publish:store-organization": "cd ./dist/store-organization && npm publish", "publish:store-organization": "cd ./dist/store-organization && npm publish",
"publish:ui-material:all": "npm-run-all -s publish:ui-material",
"publish:ui-material": "cd ./dist/ui-material && npm publish",
"publish:ui:all": "npm-run-all -s publish:ui publish:ui-organization publish:ui-authentication publish:ui-group publish:ui-chat", "publish:ui:all": "npm-run-all -s publish:ui publish:ui-organization publish:ui-authentication publish:ui-group publish:ui-chat",
"publish:ui": "cd ./dist/ui && npm publish", "publish:ui": "cd ./dist/ui && npm publish",
"publish:ui-organization": "cd ./dist/ui-organization && npm publish", "publish:ui-organization": "cd ./dist/ui-organization && npm publish",
@ -180,15 +184,16 @@
"@ucap/ng-protocol-status": "file:pack/ucap-ng-protocol-status-0.0.3.tgz", "@ucap/ng-protocol-status": "file:pack/ucap-ng-protocol-status-0.0.3.tgz",
"@ucap/ng-protocol-sync": "file:pack/ucap-ng-protocol-sync-0.0.3.tgz", "@ucap/ng-protocol-sync": "file:pack/ucap-ng-protocol-sync-0.0.3.tgz",
"@ucap/ng-protocol-umg": "file:pack/ucap-ng-protocol-umg-0.0.3.tgz", "@ucap/ng-protocol-umg": "file:pack/ucap-ng-protocol-umg-0.0.3.tgz",
"@ucap/ng-store-authentication": "file:pack/ucap-ng-store-authentication-0.0.10.tgz", "@ucap/ng-store-authentication": "file:pack/ucap-ng-store-authentication-0.0.11.tgz",
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.8.tgz", "@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.13.tgz",
"@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.9.tgz", "@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.14.tgz",
"@ucap/ng-store-organization": "file:pack/ucap-ng-store-organization-0.0.8.tgz", "@ucap/ng-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": "file:pack/ucap-ng-ui-0.0.19.tgz",
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.20.tgz", "@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.24.tgz",
"@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.3.tgz", "@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.9.tgz",
"@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.30.tgz", "@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.33.tgz",
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.27.tgz", "@ucap/ng-ui-material": "file:pack/ucap-ng-ui-material-0.0.4.tgz",
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.55.tgz",
"@ucap/ng-ui-skin-default": "file:pack/ucap-ng-ui-skin-default-0.0.1.tgz", "@ucap/ng-ui-skin-default": "file:pack/ucap-ng-ui-skin-default-0.0.1.tgz",
"@ucap/ng-web-socket": "file:pack/ucap-ng-web-socket-0.0.2.tgz", "@ucap/ng-web-socket": "file:pack/ucap-ng-web-socket-0.0.2.tgz",
"@ucap/ng-web-storage": "file:pack/ucap-ng-web-storage-0.0.3.tgz", "@ucap/ng-web-storage": "file:pack/ucap-ng-web-storage-0.0.3.tgz",
@ -197,9 +202,9 @@
"@ucap/protocol-authentication": "~0.0.5", "@ucap/protocol-authentication": "~0.0.5",
"@ucap/protocol-buddy": "~0.0.5", "@ucap/protocol-buddy": "~0.0.5",
"@ucap/protocol-event": "~0.0.5", "@ucap/protocol-event": "~0.0.5",
"@ucap/protocol-file": "~0.0.4", "@ucap/protocol-file": "~0.0.5",
"@ucap/protocol-group": "~0.0.5", "@ucap/protocol-group": "~0.0.5",
"@ucap/protocol-info": "~0.0.5", "@ucap/protocol-info": "~0.0.6",
"@ucap/protocol-inner": "~0.0.4", "@ucap/protocol-inner": "~0.0.4",
"@ucap/protocol-option": "~0.0.7", "@ucap/protocol-option": "~0.0.7",
"@ucap/protocol-ping": "~0.0.4", "@ucap/protocol-ping": "~0.0.4",

View File

@ -11,10 +11,12 @@
"@ucap/api-external": "@ucap/api-external", "@ucap/api-external": "@ucap/api-external",
"@ucap/pi": "@ucap/pi", "@ucap/pi": "@ucap/pi",
"@ucap/protocol-authentication": "@ucap/protocol-authentication", "@ucap/protocol-authentication": "@ucap/protocol-authentication",
"@ucap/protocol-info": "@ucap/protocol-info",
"@ucap/protocol-option": "@ucap/protocol-option", "@ucap/protocol-option": "@ucap/protocol-option",
"@ucap/protocol-query": "@ucap/protocol-query", "@ucap/protocol-query": "@ucap/protocol-query",
"@ucap/ng-pi": "@ucap/ng-pi", "@ucap/ng-pi": "@ucap/ng-pi",
"@ucap/ng-protocol-authentication": "@ucap/ng-protocol-authentication", "@ucap/ng-protocol-authentication": "@ucap/ng-protocol-authentication",
"@ucap/ng-protocol-info": "@ucap/ng-protocol-info",
"@ucap/ng-protocol-option": "@ucap/ng-protocol-option", "@ucap/ng-protocol-option": "@ucap/ng-protocol-option",
"@ucap/ng-protocol-query": "@ucap/ng-protocol-query" "@ucap/ng-protocol-query": "@ucap/ng-protocol-query"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@ucap/ng-store-authentication", "name": "@ucap/ng-store-authentication",
"version": "0.0.10", "version": "0.0.11",
"publishConfig": { "publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-ucap/" "registry": "https://nexus.loafle.net/repository/npm-ucap/"
}, },
@ -17,10 +17,12 @@
"@ucap/protocol-authentication": "~0.0.1", "@ucap/protocol-authentication": "~0.0.1",
"@ucap/protocol-option": "~0.0.1", "@ucap/protocol-option": "~0.0.1",
"@ucap/protocol-query": "~0.0.1", "@ucap/protocol-query": "~0.0.1",
"@ucap/protocol-info": "~0.0.1",
"@ucap/ng-pi": "~0.0.1", "@ucap/ng-pi": "~0.0.1",
"@ucap/ng-protocol-authentication": "~0.0.1", "@ucap/ng-protocol-authentication": "~0.0.1",
"@ucap/ng-protocol-option": "~0.0.1", "@ucap/ng-protocol-option": "~0.0.1",
"@ucap/ng-protocol-query": "~0.0.1", "@ucap/ng-protocol-query": "~0.0.1",
"@ucap/ng-protocol-info": "~0.0.1",
"rxjs": "~6.5.4", "rxjs": "~6.5.4",
"tslib": "^1.10.0" "tslib": "^1.10.0"
} }

View File

@ -7,6 +7,7 @@ import {
LoginResponse, LoginResponse,
LogoutResponse LogoutResponse
} from '@ucap/protocol-authentication'; } from '@ucap/protocol-authentication';
import { UserResponse, UserRequest } from '@ucap/protocol-info';
/** /**
* request of web login * request of web login
@ -106,3 +107,29 @@ export const sessionDestroyed = createAction(
'[ucap::authentication::login] session Destroyed', '[ucap::authentication::login] session Destroyed',
props<{ error: any }>() props<{ error: any }>()
); );
/**
* info user request
*/
export const infoUser = createAction(
'[ucap::authentication::login] Info User',
props<{ req: UserRequest }>()
);
/**
* Success of info user request
*/
export const infoUserSuccess = createAction(
'[ucap::authentication::login] Info User Success',
props<{
res: UserResponse;
}>()
);
/**
* Failure of info user request
*/
export const infoUserFailure = createAction(
'[ucap::authentication::login] Info User Failure',
props<{ error: any }>()
);

View File

@ -1,5 +1,5 @@
import { of } from 'rxjs'; import { of } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators'; import { catchError, map, switchMap, exhaustMap } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
@ -7,26 +7,52 @@ import { Actions, ofType, createEffect } from '@ngrx/effects';
import { PiService } from '@ucap/ng-pi'; import { PiService } from '@ucap/ng-pi';
import { AuthenticationProtocolService } from '@ucap/ng-protocol-authentication'; import { AuthenticationProtocolService } from '@ucap/ng-protocol-authentication';
import { InfoProtocolService } from '@ucap/ng-protocol-info';
import { logout, logoutSuccess } from './actions'; import {
logout,
logoutSuccess,
infoUser,
infoUserSuccess,
infoUserFailure
} from './actions';
import { UserResponse } from '@ucap/protocol-info';
@Injectable() @Injectable()
export class Effects { export class Effects {
logout$ = createEffect(() => { logout$ = createEffect(() => {
return this.actions$.pipe( return this.actions$.pipe(
ofType(logout), ofType(logout),
switchMap(action => { switchMap((action) => {
return this.authenticationProtocolService.logout({}).pipe( return this.authenticationProtocolService.logout({}).pipe(
map(res => logoutSuccess({ res })), map((res) => logoutSuccess({ res })),
catchError(error => of(error)) catchError((error) => of(error))
); );
}) })
); );
}); });
infoUser$ = createEffect(() =>
this.actions$.pipe(
ofType(infoUser),
map((action) => action.req),
exhaustMap((req) =>
this.infoProtocolService.user(req).pipe(
map((res: UserResponse) => {
return infoUserSuccess({
res
});
}),
catchError((error) => of(infoUserFailure({ error })))
)
)
)
);
constructor( constructor(
private actions$: Actions, private actions$: Actions,
private piService: PiService, private piService: PiService,
private authenticationProtocolService: AuthenticationProtocolService private authenticationProtocolService: AuthenticationProtocolService,
private infoProtocolService: InfoProtocolService
) {} ) {}
} }

View File

@ -1,7 +1,8 @@
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { initialState } from './state'; import { initialState } from './state';
import { loginSuccess, logoutSuccess } from './actions'; import { loginSuccess, logoutSuccess, infoUserSuccess } from './actions';
import { UserInfoUpdateType } from '@ucap/protocol-info';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
@ -16,5 +17,41 @@ export const reducer = createReducer(
return { return {
...initialState ...initialState
}; };
}),
on(infoUserSuccess, (state, action) => {
let loginRes = {
...state.loginRes
};
switch (action.res.type) {
case UserInfoUpdateType.Image:
loginRes = {
...loginRes
};
break;
case UserInfoUpdateType.Intro:
loginRes = {
...loginRes,
userInfo: {
...loginRes.userInfo,
intro: action.res.info
}
};
break;
case UserInfoUpdateType.TelephoneVisible:
loginRes = {
...loginRes
};
break;
default:
break;
}
return {
...state,
loginRes: {
...loginRes
}
};
}) })
); );

View File

@ -1,6 +1,6 @@
{ {
"name": "@ucap/ng-store-chat", "name": "@ucap/ng-store-chat",
"version": "0.0.8", "version": "0.0.13",
"publishConfig": { "publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-ucap/" "registry": "https://nexus.loafle.net/repository/npm-ucap/"
}, },

View File

@ -1,4 +1,7 @@
import { ModuleConfig as CoreModuleConfig } from '@ucap/core'; import { ModuleConfig as CoreModuleConfig } from '@ucap/core';
// tslint:disable-next-line: no-empty-interface // tslint:disable-next-line: no-empty-interface
export interface ModuleConfig extends CoreModuleConfig {} export interface ModuleConfig extends CoreModuleConfig {
eventRequestInitCount?: number;
eventRequestDefaultCount?: number;
}

View File

@ -15,7 +15,8 @@ import {
ReadNotification, ReadNotification,
CancelNotification, CancelNotification,
DelNotification, DelNotification,
EventJson EventJson,
ReadResponse
} from '@ucap/protocol-event'; } from '@ucap/protocol-event';
import { import {
@ -40,7 +41,7 @@ export const eventsSuccess = createAction(
props<{ props<{
eventInfoList: Info<EventJson>[]; eventInfoList: Info<EventJson>[];
res: EventInfoResponse; res: EventInfoResponse;
remainInfo: boolean; remainEvent: boolean;
}>() }>()
); );
/** /**
@ -48,7 +49,7 @@ export const eventsSuccess = createAction(
*/ */
export const eventsFailure = createAction( export const eventsFailure = createAction(
'[ucap::chat::chatting] events Failure', '[ucap::chat::chatting] events Failure',
props<{ error: any }>() props<{ roomId: string; error: any }>()
); );
/** /**
@ -74,7 +75,7 @@ export const fileInfosSuccess = createAction(
*/ */
export const fileInfosFailure = createAction( export const fileInfosFailure = createAction(
'[ucap::chat::chatting] fileInfos Failure', '[ucap::chat::chatting] fileInfos Failure',
props<{ error: any }>() props<{ roomId: string; error: any }>()
); );
/** /**
@ -84,3 +85,23 @@ export const addEvent = createAction(
'[ucap::chat::chatting] addEvent', '[ucap::chat::chatting] addEvent',
props<{ req: FileInfoRequest }>() props<{ req: FileInfoRequest }>()
); );
export const read = createAction(
'[ucap::chat::chatting] read',
props<ReadRequest>()
);
export const readSuccess = createAction(
'[ucap::chat::chatting] read Success',
props<ReadResponse>()
);
// export const readNotification = createAction(
// '[ucap::chat::chatting] Read Notification',
// props<ReadNotification>()
// );
export const readFailure = createAction(
'[ucap::chat::chatting] read Failure',
props<{ error: any }>()
);

View File

@ -5,33 +5,165 @@ import {
map, map,
catchError, catchError,
exhaustMap, exhaustMap,
withLatestFrom debounceTime
} from 'rxjs/operators'; } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable, Inject } from '@angular/core';
import { Store, select } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Actions, createEffect, ofType } from '@ngrx/effects';
import { import { EventProtocolService } from '@ucap/ng-protocol-event';
RoomInfo,
UpdateResponse,
OpenResponse,
ExitResponse,
Open3Response,
RoomType,
InviteResponse,
UpdateTimerSetResponse,
ExitForcingResponse
} from '@ucap/protocol-room';
import { RoomProtocolService } from '@ucap/ng-protocol-room'; import { RoomProtocolService } from '@ucap/ng-protocol-room';
import { FileProtocolService } from '@ucap/ng-protocol-file';
import * as roomActions from '../room/actions';
import {
events,
eventsFailure,
eventsSuccess,
read,
readFailure,
readSuccess,
fileInfos,
fileInfosFailure,
fileInfosSuccess
} from './actions';
import { InfoRequest, ReadResponse, FileType } from '@ucap/protocol-event';
import { ModuleConfig } from '../../config/module-config';
import { _MODULE_CONFIG } from '../../config/token';
@Injectable() @Injectable()
export class Effects { export class Effects {
selectedRoom$ = createEffect(
() => {
return this.actions$.pipe(
ofType(roomActions.selectedRoom),
debounceTime(300),
tap((action) => {
const requestCount = this.moduleConfig?.eventRequestInitCount || 50;
const req: InfoRequest = {
roomId: action.roomId,
baseSeq: 0,
requestCount
};
this.store.dispatch(events({ req }));
})
);
},
{ dispatch: false }
);
events$ = createEffect(
() => {
return this.actions$.pipe(
ofType(events),
map((action) => action.req),
switchMap((req) => {
return this.eventProtocolService.info(req).pipe(
map((res) => {
if (!!res && !!res.res) {
const infoList = res.infoList;
this.store.dispatch(
eventsSuccess({
eventInfoList: infoList,
res: res.res,
remainEvent:
infoList.length === req.requestCount ? true : false
})
);
if (req.baseSeq === 0) {
// 최초 이벤트 목록 조회.
// SSVC_TYPE_EVENT_READ_REQ 수행.
const maxSeq = Math.max.apply(
Math,
infoList.map((v) => v.seq)
);
this.store.dispatch(
read({
roomId: req.roomId,
lastReadSeq: Number(maxSeq)
})
);
// File 정보 수집.
this.store.dispatch(
fileInfos({
req: {
roomId: res.res.roomId,
// { 파일타입 } cf) I : 이미지 V: 동영상 F: 파일 "" 빈값이면 모든 타입을 내려줌
type: FileType.All
}
})
);
} else {
}
}
}),
catchError((error) =>
of(eventsFailure({ roomId: req.roomId, error }))
)
);
})
);
},
{ dispatch: false }
);
read$ = createEffect(
() => {
return this.actions$.pipe(
ofType(read),
exhaustMap((req) =>
this.eventProtocolService.read(req).pipe(
map((res: ReadResponse) => {
this.store.dispatch(readSuccess(res));
}),
catchError((error) => of(readFailure({ error })))
)
)
);
},
{ dispatch: false }
);
fileInfos$ = createEffect(
() => {
return this.actions$.pipe(
ofType(fileInfos),
switchMap((action) => {
return this.fileProtocolService.info(action.req).pipe(
map((res) => {
this.store.dispatch(
fileInfosSuccess({
fileInfoList: res.fileInfos,
fileInfoCheckList: res.fileInfoChecks,
res: res.res
})
);
}),
catchError((error) =>
of(fileInfosFailure({ roomId: action.req.roomId, error }))
)
);
})
);
},
{ dispatch: false }
);
constructor( constructor(
private actions$: Actions, private actions$: Actions,
private store: Store<any>, private store: Store<any>,
private roomProtocolService: RoomProtocolService @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig,
private roomProtocolService: RoomProtocolService,
private eventProtocolService: EventProtocolService,
private fileProtocolService: FileProtocolService
) {} ) {}
} }

View File

@ -1,5 +1,167 @@
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { initialState, adapterChatting } from './state'; import {
initialState,
adapterChatting,
adapterEventList,
Chatting,
adapterFileInfoList,
adapterFileInfoCheckList
} from './state';
export const reducer = createReducer(initialState); import * as roomActions from '../room/actions';
import {
eventsSuccess,
eventsFailure,
readSuccess,
fileInfosSuccess,
fileInfosFailure
} from './actions';
export const reducer = createReducer(
initialState,
on(roomActions.selectedRoom, (state, action) => {
return {
...state,
activeRoomId: action.roomId
};
}),
on(roomActions.clearSelectedRoom, (state, action) => {
if (action.roomId === state.activeRoomId) {
return {
...state,
activeRoomId: null
};
} else {
return state;
}
}),
on(eventsSuccess, (state, action) => {
const roomId = action.res.roomId;
const chatting = state.chattings.entities[roomId] || {};
let trgtChatting: Chatting = {
roomId,
eventListProcessing: false,
eventList: adapterEventList.getInitialState(),
eventStatus: null,
remainEvent: false,
fileInfoListProcessing: false,
fileInfoList: adapterFileInfoList.getInitialState(),
fileInfoCheckList: adapterFileInfoCheckList.getInitialState(),
fileInfoSyncDate: '',
...chatting
};
trgtChatting = {
...trgtChatting,
eventList: adapterEventList.upsertMany(action.eventInfoList, {
...trgtChatting.eventList
}),
eventStatus: action.res,
remainEvent: action.remainEvent,
eventListProcessing: false
};
return {
...state,
chattings: adapterChatting.upsertOne(trgtChatting, {
...state.chattings
})
};
}),
on(eventsFailure, (state, action) => {
const roomId = action.roomId;
const chatting = state.chattings.entities[roomId];
let trgtChatting: Chatting;
if (!!chatting) {
trgtChatting = {
...chatting,
eventListProcessing: false
};
}
return {
...state,
chattings: adapterChatting.upsertOne(trgtChatting, {
...state.chattings
})
};
}),
on(fileInfosSuccess, (state, action) => {
const roomId = action.res?.roomId;
if (!roomId) {
return state;
}
const chatting = state.chattings.entities[roomId] || {};
let trgtChatting: Chatting = {
roomId,
eventListProcessing: false,
eventList: adapterEventList.getInitialState(),
eventStatus: null,
remainEvent: false,
fileInfoListProcessing: false,
fileInfoList: adapterFileInfoList.getInitialState(),
fileInfoCheckList: adapterFileInfoCheckList.getInitialState(),
fileInfoSyncDate: '',
...chatting
};
const fileInfoList = action.fileInfoList;
const fileInfoCheckList = action.fileInfoCheckList;
trgtChatting = {
...trgtChatting,
fileInfoList: !!fileInfoList
? adapterFileInfoList.upsertMany(fileInfoList, {
...trgtChatting.fileInfoList
})
: trgtChatting.fileInfoList,
fileInfoCheckList: !!fileInfoCheckList
? adapterFileInfoCheckList.upsertMany(fileInfoCheckList, {
...trgtChatting.fileInfoCheckList
})
: trgtChatting.fileInfoCheckList,
fileInfoListProcessing: false
};
return {
...state,
chattings: adapterChatting.upsertOne(trgtChatting, {
...state.chattings
})
};
}),
on(fileInfosFailure, (state, action) => {
const roomId = action.roomId;
const chatting = state.chattings.entities[roomId];
let trgtChatting: Chatting;
if (!!chatting) {
trgtChatting = {
...chatting,
fileInfoListProcessing: false
};
}
return {
...state,
chattings: adapterChatting.upsertOne(trgtChatting, {
...state.chattings
})
};
})
);

View File

@ -10,19 +10,19 @@ export interface FileInfoListState extends EntityState<FileInfo> {}
export interface FileInfoCheckListState extends EntityState<FileDownloadInfo> {} export interface FileInfoCheckListState extends EntityState<FileDownloadInfo> {}
export const adapterEventList = createEntityAdapter<Info<EventJson>>({ export const adapterEventList = createEntityAdapter<Info<EventJson>>({
selectId: info => info.seq, selectId: (info) => info.seq,
sortComparer: (a, b) => { sortComparer: (a, b) => {
return a.seq - b.seq; return a.seq - b.seq;
} }
}); });
export const adapterFileInfoList = createEntityAdapter<FileInfo>({ export const adapterFileInfoList = createEntityAdapter<FileInfo>({
selectId: info => info.seq, selectId: (info) => info.seq,
sortComparer: (a, b) => { sortComparer: (a, b) => {
return b.seq - a.seq; return b.seq - a.seq;
} }
}); });
export const adapterFileInfoCheckList = createEntityAdapter<FileDownloadInfo>({ export const adapterFileInfoCheckList = createEntityAdapter<FileDownloadInfo>({
selectId: info => info.seq, selectId: (info) => info.seq,
sortComparer: (a, b) => { sortComparer: (a, b) => {
return b.seq - a.seq; return b.seq - a.seq;
} }
@ -65,7 +65,7 @@ export interface Chatting {
eventListProcessing?: boolean; eventListProcessing?: boolean;
eventList?: EventListState; eventList?: EventListState;
eventStatus?: InfoResponse | null; eventStatus?: InfoResponse | null;
remainEvent?: false; remainEvent?: boolean;
fileInfoListProcessing?: boolean; fileInfoListProcessing?: boolean;
fileInfoList?: FileInfoListState; fileInfoList?: FileInfoListState;
@ -76,9 +76,11 @@ export interface Chatting {
export interface ChattingState extends EntityState<Chatting> {} export interface ChattingState extends EntityState<Chatting> {}
export const adapterChatting = createEntityAdapter<Chatting>({ export const adapterChatting = createEntityAdapter<Chatting>({
selectId: chatting => chatting.roomId selectId: (chatting) => chatting.roomId
}); });
const chattingInitialState: ChattingState = adapterChatting.getInitialState({});
const { const {
selectAll: selectAllForChatting, selectAll: selectAllForChatting,
selectEntities: selectEntitiesForChatting, selectEntities: selectEntitiesForChatting,
@ -86,26 +88,14 @@ const {
selectTotal: selectTotalForChatting selectTotal: selectTotalForChatting
} = adapterChatting.getSelectors(); } = adapterChatting.getSelectors();
const chattingInitialState: ChattingState = adapterChatting.getInitialState({
roomId: undefined,
eventListProcessing: false,
eventList: eventListInitialState,
eventStatus: undefined,
remainEvent: false,
fileInfoListProcessing: false,
fileInfoList: fileInfoListInitialState,
fileInfoCheckList: fileInfoCheckListInitialState,
fileInfoSyncDate: undefined
});
export interface State { export interface State {
chattings: ChattingState; chattings: ChattingState;
activeRoomId: string | null;
} }
export const initialState: State = { export const initialState: State = {
chattings: chattingInitialState chattings: chattingInitialState,
activeRoomId: null
}; };
export function selectors<S>(selector: Selector<any, State>) { export function selectors<S>(selector: Selector<any, State>) {
@ -115,46 +105,55 @@ export function selectors<S>(selector: Selector<any, State>) {
); );
const selectChatting = createSelector( const selectChatting = createSelector(
selector, selectChattings,
selectEntitiesForChatting, (state: ChattingState, roomId: string) => {
(state: State, entities: Dictionary<Chatting>, roomId: string) => return state.entities && state.entities[roomId];
entities && entities[roomId] }
); );
const selectChattingEventListProcessing = createSelector( const selectChattingEventListProcessing = createSelector(
selectChatting, selectChatting,
state => state.eventListProcessing (state) => state.eventListProcessing
); );
const selectChattingEventList = createSelector( const selectChattingEventList = createSelector(
selectChatting, selectChattings,
state => state.eventList (state: ChattingState, roomId: string) => {
const chatting = state.entities && state.entities[roomId];
if (!!chatting) {
return chatting?.eventList;
} else {
return adapterEventList.getInitialState();
}
}
); );
const selectChattingEventStatus = createSelector( const selectChattingEventStatus = createSelector(
selectChatting, selectChatting,
state => state.eventStatus (state) => state.eventStatus
); );
const selectChattingRemainEvent = createSelector( const selectChattingRemainEvent = createSelector(
selectChatting, selectChatting,
state => state.remainEvent (state) => state.remainEvent
); );
const selectChattingFileInfoListProcessing = createSelector( const selectChattingFileInfoListProcessing = createSelector(
selectChatting, selectChatting,
state => state.fileInfoListProcessing (state) => state.fileInfoListProcessing
); );
const selectChattingFileInfoList = createSelector( const selectChattingFileInfoList = createSelector(
selectChatting, selectChatting,
state => state.fileInfoList (state) => state.fileInfoList
); );
const selectChattingFileInfoCheckList = createSelector( const selectChattingFileInfoCheckList = createSelector(
selectChatting, selectChatting,
state => state.fileInfoCheckList (state) => state.fileInfoCheckList
); );
const selectChattingFileInfoSyncDate = createSelector( const selectChattingFileInfoSyncDate = createSelector(
selectChatting, selectChatting,
state => state.fileInfoSyncDate (state) => state.fileInfoSyncDate
); );
return { return {
activeRoomId: createSelector(selector, (state) => state.activeRoomId),
chattings: createSelector(selectChattings, selectAllForChatting), chattings: createSelector(selectChattings, selectAllForChatting),
chatting: selectChatting, chatting: selectChatting,

View File

@ -26,6 +26,21 @@ import {
UserInfoShort UserInfoShort
} from '@ucap/protocol-room'; } from '@ucap/protocol-room';
/**
* retrieve selected Room.
*/
export const selectedRoom = createAction(
'[ucap::chat::selectedRoom] selected room',
props<{ roomId: string; localeCode: LocaleCode }>()
);
/**
* clear selected Room.
*/
export const clearSelectedRoom = createAction(
'[ucap::chat::clearSelectedRoom] clear Selected Room',
props<{ roomId: string }>()
);
/** /**
* retrieve list of room information and list of user information per room * retrieve list of room information and list of user information per room
*/ */

View File

@ -4,7 +4,9 @@ import {
map, map,
switchMap, switchMap,
exhaustMap, exhaustMap,
withLatestFrom withLatestFrom,
tap,
debounceTime
} from 'rxjs/operators'; } from 'rxjs/operators';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
@ -21,7 +23,8 @@ import {
UpdateResponse, UpdateResponse,
InviteResponse, InviteResponse,
ExitForcingResponse, ExitForcingResponse,
UpdateTimerSetResponse UpdateTimerSetResponse,
InfoRequest
} from '@ucap/protocol-room'; } from '@ucap/protocol-room';
import { RoomProtocolService } from '@ucap/ng-protocol-room'; import { RoomProtocolService } from '@ucap/ng-protocol-room';
@ -73,7 +76,8 @@ import {
rooms2Failure, rooms2Failure,
delMulti, delMulti,
delMultiSuccess, delMultiSuccess,
delMultiFailure delMultiFailure,
selectedRoom
} from './actions'; } from './actions';
@Injectable() @Injectable()
@ -85,6 +89,29 @@ export class Effects {
); );
}); });
selectedRoom$ = createEffect(
() => {
return this.actions$.pipe(
ofType(selectedRoom),
debounceTime(300),
tap((action) => {
const req: InfoRequest = {
...action,
isDetail: false
};
console.log(req);
// retrieve room info
this.store.dispatch(room({ req }));
// retrieve event info >> chatting.effect.selectedRoom$
})
);
},
{ dispatch: false }
);
rooms$ = createEffect(() => { rooms$ = createEffect(() => {
return this.actions$.pipe( return this.actions$.pipe(
ofType(rooms), ofType(rooms),

View File

@ -26,6 +26,7 @@ import {
delMultiSuccess, delMultiSuccess,
updateSuccess updateSuccess
} from './actions'; } from './actions';
import { userInfo } from 'os';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
@ -214,7 +215,9 @@ export const reducer = createReducer(
return { return {
...state, ...state,
rooms: adapterRoom.removeOne(roomId, { ...state.rooms }), rooms: adapterRoom.removeOne(roomId, { ...state.rooms }),
roomUsers: adapterRoomUser.removeOne(roomId, { ...state.roomUsers }), roomUsers: adapterRoomUser.removeOne(roomId, {
...state.roomUsers
}),
roomUsersShort: adapterRoomUserShort.removeOne(roomId, { roomUsersShort: adapterRoomUserShort.removeOne(roomId, {
...state.roomUsersShort ...state.roomUsersShort
}) })
@ -231,10 +234,72 @@ export const reducer = createReducer(
return { return {
...state, ...state,
rooms: adapterRoom.removeMany(roomIds, { ...state.rooms }), rooms: adapterRoom.removeMany(roomIds, { ...state.rooms }),
roomUsers: adapterRoomUser.removeMany(roomIds, { ...state.roomUsers }), roomUsers: adapterRoomUser.removeMany(roomIds, {
...state.roomUsers
}),
roomUsersShort: adapterRoomUserShort.removeMany(roomIds, { roomUsersShort: adapterRoomUserShort.removeMany(roomIds, {
...state.roomUsersShort ...state.roomUsersShort
}) })
}; };
}),
/**
* [Chatting Action watching.]
*/
on(chattingActions.readSuccess, (state, action) => {
const roomId = action.roomId;
const trgtUserSeq = action.SENDER_SEQ;
if (!roomId || !trgtUserSeq) {
return state;
}
let roomUser: RoomUserMap = state.roomUsers.entities[roomId];
if (!!roomUser) {
roomUser = {
...roomUser,
userInfos: state.roomUsers.entities[roomId].userInfos.map(
(roomUserInfo) => {
if (roomUserInfo.seq === Number(trgtUserSeq)) {
return {
...roomUserInfo,
lastReadEventSeq: action.lastReadSeq
};
} else {
return roomUserInfo;
}
}
)
};
}
let roomUserShort: RoomUserShortMap = state.roomUsersShort.entities[roomId];
if (!!roomUserShort) {
roomUserShort = {
...roomUserShort,
userInfos: state.roomUsersShort.entities[roomId].userInfos.map(
(roomUserInfo) => {
if (roomUserInfo.seq === Number(trgtUserSeq)) {
return { ...roomUserInfo, lastReadEventSeq: action.lastReadSeq };
} else {
return roomUserInfo;
}
}
)
};
}
return {
...state,
roomUsers: !!roomUser
? adapterRoomUser.upsertOne(roomUser, {
...state.roomUsers
})
: state.roomUsers,
roomUsersShort: !!roomUserShort
? adapterRoomUserShort.upsertOne(roomUserShort, {
...state.roomUsersShort
})
: state.roomUsersShort
};
}) })
); );

View File

@ -101,9 +101,9 @@ export function selectors<S>(selector: Selector<any, State>) {
rooms: createSelector(selectRooms, selectAllForRoom), rooms: createSelector(selectRooms, selectAllForRoom),
room: createSelector( room: createSelector(
selectRooms, selectRooms,
selectEntitiesForRoom, (roomState: RoomState, roomId: string) => {
(roomState: RoomState, entities: Dictionary<RoomInfo>, roomId: string) => return roomState.entities && roomState.entities[roomId];
entities && entities[roomId] }
), ),
roomsSyncDate: createSelector( roomsSyncDate: createSelector(
selectRooms, selectRooms,
@ -112,12 +112,9 @@ export function selectors<S>(selector: Selector<any, State>) {
roomUsers: createSelector(selectRoomUsers, selectAllForRoomUser), roomUsers: createSelector(selectRoomUsers, selectAllForRoomUser),
roomUser: createSelector( roomUser: createSelector(
selectRoomUsers, selectRoomUsers,
selectEntitiesForRoomUser, (roomUserState: RoomUserState, roomId: string) => {
( return roomUserState.entities && roomUserState.entities[roomId];
roomUserState: RoomUserState, }
entities: Dictionary<RoomUserDetailData>,
roomId: string
) => entities && entities[roomId]
), ),
roomUsersShort: createSelector( roomUsersShort: createSelector(
selectRoomUsersShort, selectRoomUsersShort,
@ -125,12 +122,11 @@ export function selectors<S>(selector: Selector<any, State>) {
), ),
roomUserShort: createSelector( roomUserShort: createSelector(
selectRoomUsersShort, selectRoomUsersShort,
selectEntitiesForRoomUserShort, (roomUserShortState: RoomUserShortState, roomId: string) => {
( return (
roomUserShortState: RoomUserShortState, roomUserShortState.entities && roomUserShortState.entities[roomId]
entities: Dictionary<RoomUserData>, );
roomId: string }
) => entities && entities[roomId]
), ),
unreadTotal: createSelector( unreadTotal: createSelector(
selectRooms, selectRooms,

View File

@ -14,9 +14,11 @@
"@ucap/protocol-group": "@ucap/protocol-group", "@ucap/protocol-group": "@ucap/protocol-group",
"@ucap/protocol-buddy": "@ucap/protocol-buddy", "@ucap/protocol-buddy": "@ucap/protocol-buddy",
"@ucap/protocol-query": "@ucap/protocol-query", "@ucap/protocol-query": "@ucap/protocol-query",
"@ucap/protocol-info": "@ucap/protocol-info",
"@ucap/ng-protocol-buddy": "@ucap/ng-protocol-buddy", "@ucap/ng-protocol-buddy": "@ucap/ng-protocol-buddy",
"@ucap/ng-protocol-group": "@ucap/ng-protocol-group", "@ucap/ng-protocol-group": "@ucap/ng-protocol-group",
"@ucap/ng-protocol-sync": "@ucap/ng-protocol-sync", "@ucap/ng-protocol-sync": "@ucap/ng-protocol-sync",
"@ucap/ng-protocol-info": "@ucap/ng-protocol-info",
"@ucap/ng-store-organization": "@ucap/ng-store-organization", "@ucap/ng-store-organization": "@ucap/ng-store-organization",
"@ucap/ng-store-authentication": "@ucap/ng-store-authentication" "@ucap/ng-store-authentication": "@ucap/ng-store-authentication"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@ucap/ng-store-group", "name": "@ucap/ng-store-group",
"version": "0.0.9", "version": "0.0.14",
"publishConfig": { "publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-ucap/" "registry": "https://nexus.loafle.net/repository/npm-ucap/"
}, },
@ -12,11 +12,13 @@
"@ucap/protocol-group": "~0.0.1", "@ucap/protocol-group": "~0.0.1",
"@ucap/protocol-query": "~0.0.1", "@ucap/protocol-query": "~0.0.1",
"@ucap/protocol-sync": "~0.0.1", "@ucap/protocol-sync": "~0.0.1",
"@ucap/protocol-info": "~0.0.1",
"@ucap/ng-protocol-buddy": "~0.0.1", "@ucap/ng-protocol-buddy": "~0.0.1",
"@ucap/ng-protocol-group": "~0.0.1", "@ucap/ng-protocol-group": "~0.0.1",
"@ucap/ng-protocol-sync": "~0.0.1", "@ucap/ng-protocol-sync": "~0.0.1",
"@ucap/ng-store-organization": "~0.0.1", "@ucap/ng-store-organization": "~0.0.1",
"@ucap/ng-store-authentication": "~0.0.1", "@ucap/ng-store-authentication": "~0.0.1",
"@ucap/ng-protocol-info": "~0.0.1",
"tslib": "^1.10.0" "tslib": "^1.10.0"
} }
} }

View File

@ -9,6 +9,10 @@ import {
UpdateRequest as BuddyUpdateRequest, UpdateRequest as BuddyUpdateRequest,
UpdateResponse as BuddyUpdateResponse UpdateResponse as BuddyUpdateResponse
} from '@ucap/protocol-buddy'; } from '@ucap/protocol-buddy';
import {
UserNicknameRequest as NicknameRequest,
UserNicknameResponse as NicknameResponse
} from '@ucap/protocol-info';
/** /**
* retrieve list of buddy * retrieve list of buddy
@ -116,3 +120,25 @@ export const updateFailure = createAction(
'[ucap::group::buddy] update Failure', '[ucap::group::buddy] update Failure',
props<{ error: any }>() props<{ error: any }>()
); );
/**
*
*/
export const nickname = createAction(
'[ucap::group::buddy] user nickname',
props<{ req: NicknameRequest }>()
);
/**
* Success of update request
*/
export const nicknameSuccess = createAction(
'[ucap::group::buddy] user nickname Success',
props<{ res: NicknameResponse }>()
);
/**
* Failure of update request
*/
export const nicknameFailure = createAction(
'[ucap::group::buddy] user nickname Failure',
props<{ error: any }>()
);

View File

@ -19,6 +19,11 @@ import {
UpdateResponse as BuddyUpdateResponse UpdateResponse as BuddyUpdateResponse
} from '@ucap/protocol-buddy'; } from '@ucap/protocol-buddy';
import {
UserNicknameRequest as NicknameRequest,
UserNicknameResponse as NicknameResponse
} from '@ucap/protocol-info';
import { SyncProtocolService } from '@ucap/ng-protocol-sync'; import { SyncProtocolService } from '@ucap/ng-protocol-sync';
import { BuddyProtocolService } from '@ucap/ng-protocol-buddy'; import { BuddyProtocolService } from '@ucap/ng-protocol-buddy';
@ -43,6 +48,9 @@ import {
update, update,
updateSuccess, updateSuccess,
updateFailure, updateFailure,
nickname,
nicknameSuccess,
nicknameFailure,
delAndClear delAndClear
} from './actions'; } from './actions';
@ -50,6 +58,7 @@ import { BuddySelector, GroupSelector } from '../state';
import { ModuleConfig } from '../../config/module-config'; import { ModuleConfig } from '../../config/module-config';
import { _MODULE_CONFIG } from '../../config/token'; import { _MODULE_CONFIG } from '../../config/token';
import { InfoProtocolService } from '@ucap/ng-protocol-info';
@Injectable() @Injectable()
export class Effects { export class Effects {
@ -126,7 +135,7 @@ export class Effects {
), ),
tap(([req, groupList, myDeptUserList]) => { tap(([req, groupList, myDeptUserList]) => {
for (const group of groupList) { for (const group of groupList) {
if (group.userSeqs.indexOf(String(req.seq)) > -1) { if (group.userSeqs.indexOf(req.seq as any) > -1) {
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다. // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
if ( if (
!!this.moduleConfig.useMyDeptGroup && !!this.moduleConfig.useMyDeptGroup &&
@ -145,7 +154,7 @@ export class Effects {
groupSeq: group.seq, groupSeq: group.seq,
groupName: group.name, groupName: group.name,
userSeqs: group.userSeqs.filter( userSeqs: group.userSeqs.filter(
(userSeq) => userSeq !== String(req.seq) (userSeq) => userSeq !== (req.seq as any)
) )
} }
}) })
@ -158,8 +167,9 @@ export class Effects {
if ( if (
!!this.moduleConfig.useMyDeptGroup && !!this.moduleConfig.useMyDeptGroup &&
this.moduleConfig.useMyDeptGroup && this.moduleConfig.useMyDeptGroup &&
!!myDeptUserList &&
myDeptUserList.filter( myDeptUserList.filter(
(deptUser) => deptUser.seq === String(req.seq) (deptUser) => deptUser.seq === (req.seq as any)
).length > 0 ).length > 0
) { ) {
// skip;; // skip;;
@ -198,6 +208,21 @@ export class Effects {
) )
); );
nickname$ = createEffect(() =>
this.actions$.pipe(
ofType(nickname),
map((action) => action.req),
exhaustMap((req) =>
this.infoProtocolService.userNickname(req).pipe(
map((res: NicknameResponse) => {
return nicknameSuccess({ res });
}),
catchError((error) => of(nicknameFailure({ error })))
)
)
)
);
groupCreateSuccess$ = createEffect( groupCreateSuccess$ = createEffect(
() => { () => {
return this.actions$.pipe( return this.actions$.pipe(
@ -215,7 +240,7 @@ export class Effects {
} }
const index = buddyList.findIndex( const index = buddyList.findIndex(
(b) => String(b.seq) === userSeq (b) => b.seq === Number(userSeq)
); );
if (-1 < index) { if (-1 < index) {
addBuddyList.push(userSeq); addBuddyList.push(userSeq);
@ -251,7 +276,7 @@ export class Effects {
} }
const index = buddyList.findIndex( const index = buddyList.findIndex(
(b) => String(b.seq) === userSeq (b) => b.seq === Number(userSeq)
); );
if (-1 < index) { if (-1 < index) {
addBuddyList.push(userSeq); addBuddyList.push(userSeq);
@ -284,6 +309,7 @@ export class Effects {
private store: Store<any>, private store: Store<any>,
@Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig, @Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig,
private syncProtocolService: SyncProtocolService, private syncProtocolService: SyncProtocolService,
private buddyProtocolService: BuddyProtocolService private buddyProtocolService: BuddyProtocolService,
private infoProtocolService: InfoProtocolService
) {} ) {}
} }

View File

@ -3,7 +3,12 @@ import { createReducer, on } from '@ngrx/store';
import { UserInfo } from '@ucap/protocol-sync'; import { UserInfo } from '@ucap/protocol-sync';
import { initialState, adapterBuddy } from './state'; import { initialState, adapterBuddy } from './state';
import { buddy2Success, delSuccess, updateSuccess } from './actions'; import {
buddy2Success,
delSuccess,
updateSuccess,
nicknameSuccess
} from './actions';
export const reducer = createReducer( export const reducer = createReducer(
initialState, initialState,
@ -34,6 +39,19 @@ export const reducer = createReducer(
isFavorit: res.isFavorit isFavorit: res.isFavorit
}; };
return {
...state,
buddies: adapterBuddy.upsertOne(userInfo, { ...state.buddies })
};
}),
on(nicknameSuccess, (state, action) => {
const res = action.res;
const userInfo: UserInfo = {
...state.buddies.entities[res.userSeq],
nickName: res.nickname
};
return { return {
...state, ...state,
buddies: adapterBuddy.upsertOne(userInfo, { ...state.buddies }) buddies: adapterBuddy.upsertOne(userInfo, { ...state.buddies })

View File

@ -128,8 +128,7 @@ export class Effects {
) )
); );
updateMember$ = createEffect( updateMember$ = createEffect(() => {
() => {
return this.actions$.pipe( return this.actions$.pipe(
ofType(updateMember), ofType(updateMember),
withLatestFrom( withLatestFrom(
@ -138,17 +137,19 @@ export class Effects {
), ),
switchMap(([action, groupList, myDeptUserList]) => { switchMap(([action, groupList, myDeptUserList]) => {
const targetGroup = action.targetGroup; const targetGroup = action.targetGroup;
const targetUserSeqs = action.targetUserSeqs; const targetUserSeqs = action.targetUserSeqs as any;
// Del Buddy // Del Buddy
let userSeqsForDelete: string[] = targetGroup.userSeqs.filter( let userSeqsForDelete: string[] = targetGroup.userSeqs.filter(
(v) => targetUserSeqs.indexOf(v) < 0 (v) => targetUserSeqs.indexOf(v + '') < 0
); );
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다. // 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
if ( if (
!!this.moduleConfig.useMyDeptGroup && !!this.moduleConfig.useMyDeptGroup &&
this.moduleConfig.useMyDeptGroup this.moduleConfig.useMyDeptGroup &&
!!myDeptUserList &&
myDeptUserList.length > 0
) { ) {
userSeqsForDelete = userSeqsForDelete.filter( userSeqsForDelete = userSeqsForDelete.filter(
(delbuddy) => (delbuddy) =>
@ -188,9 +189,7 @@ export class Effects {
]; ];
}) })
); );
}, });
{ dispatch: false }
);
moveMember$ = createEffect(() => moveMember$ = createEffect(() =>
this.actions$.pipe( this.actions$.pipe(

View File

@ -3,6 +3,7 @@
"dest": "../../dist/ui-authentication", "dest": "../../dist/ui-authentication",
"lib": { "lib": {
"entryFile": "src/public-api.ts", "entryFile": "src/public-api.ts",
"styleIncludePaths": ["./src/assets/scss"],
"umdModuleIds": { "umdModuleIds": {
"moment": "moment", "moment": "moment",
"@ucap/pi": "@ucap/pi", "@ucap/pi": "@ucap/pi",

View File

@ -1,6 +1,6 @@
{ {
"name": "@ucap/ng-ui-authentication", "name": "@ucap/ng-ui-authentication",
"version": "0.0.20", "version": "0.0.24",
"publishConfig": { "publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-ucap/" "registry": "https://nexus.loafle.net/repository/npm-ucap/"
}, },
@ -10,9 +10,10 @@
"@angular/core": "^9.0.2", "@angular/core": "^9.0.2",
"@angular/material": "^9.0.0", "@angular/material": "^9.0.0",
"@ucap/core": "~0.0.1", "@ucap/core": "~0.0.1",
"@ucap/uc-scss": "~0.0.1", "@ucap/ui-scss": "~0.0.1",
"@ucap/ng-i18n": "~0.0.1", "@ucap/ng-i18n": "~0.0.1",
"@ucap/ng-ui": "~0.0.1", "@ucap/ng-ui": "~0.0.1",
"@ucap/ng-ui-material": "~0.0.1",
"tslib": "^1.10.0" "tslib": "^1.10.0"
} }
} }

View File

@ -0,0 +1,11 @@
{
"bundlerOptions": {
"entryFile": "./projects/ui-authentication/src/assets/scss/_theme.scss",
"rootDir": "./projects/ui-authentication/src/assets/scss/",
"outFile": "./dist/ui-authentication/_theme.scss",
"dedupeGlobs": [],
"includePaths": [],
"ignoreImports": ["~@ucap/.*", "~@angular/.*"],
"logLevel": "silent"
}
}

View File

@ -0,0 +1,2 @@
@import '../../lib/components/change-password.component.theme.scss';
@import '../../lib/components/login.component.theme.scss';

View File

@ -30,8 +30,11 @@
} }
.separator { .separator {
font-size: 15px; font: {
font-weight: 600; size: 15px;
weight: 600;
}
margin: 24px auto; margin: 24px auto;
position: relative; position: relative;
overflow: hidden; overflow: hidden;

View File

@ -2,58 +2,17 @@ import { moduleMetadata } from '@storybook/angular';
import { action } from '@storybook/addon-actions'; import { action } from '@storybook/addon-actions';
import { linkTo } from '@storybook/addon-links'; import { linkTo } from '@storybook/addon-links';
import { ChangePasswordComponent } from './change-password.component';
import { AuthenticationUiModule } from '../authentication-ui.module';
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
import { ChangeDetectorRef } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { MatSelectModule } from '@angular/material/select';
import { I18nService, UCAP_I18N_NAMESPACE, I18nModule } from '@ucap/ng-i18n';
import { LogService } from '@ucap/logger';
import { Company } from '@ucap/api-external';
export default { import { FormBuilder } from '@angular/forms';
title: 'ChangePasswordComponent',
decorators: [
moduleMetadata({
imports: [
BrowserModule,
BrowserAnimationsModule,
CommonModule, import { LogService } from '@ucap/ng-logger';
ReactiveFormsModule, import { I18nService, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
MatButtonModule, import { AuthenticationUiModule } from '../authentication-ui.module';
MatCheckboxModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatProgressSpinnerModule,
MatSelectModule,
I18nModule import { ChangePasswordComponent } from './change-password.component';
],
providers: [
AuthenticationUiModule,
{ provide: FormBuilder, useValue: new FormBuilder() },
{ provide: I18nService, useValue: new I18nService(new LogService({})) },
{
provide: UCAP_I18N_NAMESPACE,
useValue: 'authentication'
}
]
})
],
excludeStories: /.*Data$/
};
export const actionsData = { export const actionsData = {
changePassword: action('changePassword') changePassword: action('changePassword')
@ -61,6 +20,25 @@ export const actionsData = {
export const inputData = {}; export const inputData = {};
export default {
title: 'ui-authentication::ChangePasswordComponent',
component: ChangePasswordComponent,
decorators: [
moduleMetadata({
imports: [BrowserModule, BrowserAnimationsModule, AuthenticationUiModule],
providers: [
{ provide: FormBuilder, useClass: FormBuilder },
{ provide: LogService, useValue: new LogService({}) },
{ provide: I18nService, useClass: I18nService },
{
provide: UCAP_I18N_NAMESPACE,
useValue: 'authentication'
}
]
})
]
};
export const Default = () => ({ export const Default = () => ({
component: ChangePasswordComponent, component: ChangePasswordComponent,
props: { props: {

View File

@ -0,0 +1,20 @@
@import '~@ucap/ng-ui-material/material';
@mixin ucap-authentication-change-password-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
.ucap-authentication-change-password-container {
border-color: mat-color($foreground, secondary-text);
}
}
@mixin ucap-authentication-change-password-typography($config) {
.ucap-authentication-change-password-container {
font-family: mat-font-family($config);
}
}

View File

@ -1,4 +1,4 @@
@import '~@ucap/ui-scss/ucap'; @import '~@ucap/ng-ui-material/material';
.ucap-authentication-login-container { .ucap-authentication-login-container {
top: 0; top: 0;
@ -9,7 +9,9 @@
background-color: rgba(255, 255, 255, 1); background-color: rgba(255, 255, 255, 1);
border-radius: 0px; border-radius: 0px;
font-size: 14px; font: {
size: 14px;
}
text-align: center; text-align: center;
@ -52,7 +54,9 @@
display: block; display: block;
border-radius: 0; border-radius: 0;
line-height: 50px; line-height: 50px;
font-size: 1.1em; font: {
size: 1.1em;
}
} }
} }
} }

View File

@ -5,40 +5,42 @@ import { linkTo } from '@storybook/addon-links';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { LogService } from '@ucap/logger';
import { Company } from '@ucap/api-external'; import { Company } from '@ucap/api-external';
import { LogService } from '@ucap/ng-logger';
import { I18nService, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n'; import { I18nService, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
import { AuthenticationUiModule } from '../authentication-ui.module'; import { AuthenticationUiModule } from '../authentication-ui.module';
import { LoginComponent } from './login.component'; import { LoginComponent } from './login.component';
const actionsData = {
login: action('login')
};
const inputData = {
companyList: [
{ companyName: 'LG CNS', companyCode: 'GUC100' },
{ companyName: 'LG UCAP', companyCode: 'GUC101' }
] as Company[]
};
export default { export default {
title: 'LoginComponent', title: 'ui-authentication::LoginComponent',
component: LoginComponent,
decorators: [ decorators: [
moduleMetadata({ moduleMetadata({
imports: [BrowserModule, BrowserAnimationsModule, AuthenticationUiModule], imports: [BrowserModule, BrowserAnimationsModule, AuthenticationUiModule],
providers: [ providers: [
{ provide: I18nService, useValue: new I18nService(new LogService({})) }, { provide: LogService, useValue: new LogService({}) },
{ provide: I18nService, useClass: I18nService },
{ {
provide: UCAP_I18N_NAMESPACE, provide: UCAP_I18N_NAMESPACE,
useValue: 'authentication' useValue: 'authentication'
} }
] ]
}) })
], ]
excludeStories: /.*Data$/
};
export const actionsData = {
login: action('login')
};
export const inputData = {
companyList: [
{ companyName: 'LG CNS', companyCode: 'GUC100' },
{ companyName: 'LG UCAP', companyCode: 'GUC101' }
] as Company[]
}; };
export const Default = () => ({ export const Default = () => ({
@ -55,7 +57,6 @@ export const InputContents = () => ({
companyList: inputData.companyList, companyList: inputData.companyList,
companyCode: 'GUC100', companyCode: 'GUC100',
loginId: 'test-loginid', loginId: 'test-loginid',
// loginPw: '111111',
login: actionsData.login login: actionsData.login
} }
}); });

View File

@ -0,0 +1,20 @@
@import '~@ucap/ng-ui-material/material';
@mixin ucap-authentication-login-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);
$background: map-get($theme, background);
$foreground: map-get($theme, foreground);
.ucap-authentication-login-container {
border-color: mat-color($foreground, secondary-text);
}
}
@mixin ucap-authentication-login-typography($config) {
.ucap-authentication-login-container {
font-family: mat-font-family($config);
}
}

View File

@ -3,11 +3,13 @@
"dest": "../../dist/ui-chat", "dest": "../../dist/ui-chat",
"lib": { "lib": {
"entryFile": "src/public-api.ts", "entryFile": "src/public-api.ts",
"styleIncludePaths": ["./src/assets/scss"],
"umdModuleIds": { "umdModuleIds": {
"ngx-perfect-scrollbar": "ngx-perfect-scrollbar", "ngx-perfect-scrollbar": "ngx-perfect-scrollbar",
"@ucap/core": "@ucap/core", "@ucap/core": "@ucap/core",
"@ucap/protocol-room": "@ucap/protocol-room", "@ucap/protocol-room": "@ucap/protocol-room",
"@ucap/ng-logger": "@ucap/ng-logger", "@ucap/ng-logger": "@ucap/ng-logger",
"@ucap/ng-i18n": "@ucap/ng-i18n",
"@ucap/ng-ui": "@ucap/ng-ui" "@ucap/ng-ui": "@ucap/ng-ui"
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@ucap/ng-ui-chat", "name": "@ucap/ng-ui-chat",
"version": "0.0.3", "version": "0.0.9",
"publishConfig": { "publishConfig": {
"registry": "https://nexus.loafle.net/repository/npm-ucap/" "registry": "https://nexus.loafle.net/repository/npm-ucap/"
}, },
@ -11,8 +11,10 @@
"@angular/material": "^9.0.0", "@angular/material": "^9.0.0",
"@ucap/core": "~0.0.1", "@ucap/core": "~0.0.1",
"@ucap/protocol-room": "~0.0.1", "@ucap/protocol-room": "~0.0.1",
"@ucap/uc-scss": "~0.0.1", "@ucap/ui-scss": "~0.0.1",
"@ucap/ng-ui": "~0.0.1", "@ucap/ng-ui": "~0.0.1",
"@ucap/ng-ui-material": "~0.0.1",
"@ucap/ng-ui-organization": "~0.0.1",
"tslib": "^1.10.0" "tslib": "^1.10.0"
} }
} }

View File

@ -0,0 +1,11 @@
{
"bundlerOptions": {
"entryFile": "./projects/ui-chat/src/assets/scss/_theme.scss",
"rootDir": "./projects/ui-chat/src/assets/scss/",
"outFile": "./dist/ui-chat/_theme.scss",
"dedupeGlobs": [],
"includePaths": [],
"ignoreImports": ["~@ucap/.*", "~@angular/.*"],
"logLevel": "silent"
}
}

View File

@ -0,0 +1,2 @@
@import '../../lib/components/room-expansion.component.theme.scss';
@import '../../lib/components/room-list-item-01.component.theme.scss';

View File

@ -28,8 +28,48 @@ import {
RoomExpansionNodeDirective RoomExpansionNodeDirective
} from './components/room-expansion.component'; } from './components/room-expansion.component';
import { RoomListItem01Component } from './components/room-list-item-01.component'; import { RoomListItem01Component } from './components/room-list-item-01.component';
import { InformationComponent } from './components/message-box/information.component';
import { TextComponent } from './components/message-box/text.component';
import { FileComponent } from './components/message-box/file.component';
import { AttachFileComponent } from './components/message-box/attach-file.component';
import { ImageComponent } from './components/message-box/image.component';
import { VideoComponent } from './components/message-box/video.component';
import { StickerComponent } from './components/message-box/sticker.component';
import { MassComponent } from './components/message-box/mass.component';
import { ScheduleComponent } from './components/message-box/schedule.component';
import { VideoConferenceComponent } from './components/message-box/video-conference.component';
import { TranslationComponent } from './components/message-box/translation.component';
import { MassTranslationComponent } from './components/message-box/mass-translation.component';
import { RecallComponent } from './components/message-box/recall.component';
import { ReadHereComponent } from './components/message-box/read-here.component';
import { DateSplitterComponent } from './components/message-box/date-splitter.component';
import { SmsComponent } from './components/message-box/sms.component';
import { ReplyComponent } from './components/message-box/reply.component';
const COMPONENTS = [RoomExpansionComponent, RoomListItem01Component]; const COMPONENTS = [
RoomExpansionComponent,
RoomListItem01Component,
InformationComponent,
TextComponent,
FileComponent,
AttachFileComponent,
ImageComponent,
VideoComponent,
StickerComponent,
MassComponent,
ScheduleComponent,
VideoConferenceComponent,
TranslationComponent,
MassTranslationComponent,
RecallComponent,
ReadHereComponent,
DateSplitterComponent,
SmsComponent,
ReplyComponent
];
const DIALOGS = []; const DIALOGS = [];
const PIPES = []; const PIPES = [];
const DIRECTIVES = [RoomExpansionHeaderDirective, RoomExpansionNodeDirective]; const DIRECTIVES = [RoomExpansionHeaderDirective, RoomExpansionNodeDirective];

View File

@ -0,0 +1,37 @@
<div class="ucap-chat-message-box-attach-file" (click)="onClickOpenViewer()">
<!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가-->
<!-- <div class="file-img" [ngClass]="fileInfo.FileExt"></div> -->
<div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.fileExt]">
<div class="ico"></div>
</div>
<ul class="file-info">
<li class="file-name">
{{ fileInfo.fileName }}
</li>
<li class="date">
{{ fileInfo.attachmentRegDate | ucapDate }}
</li>
</ul>
</div>
<div class="btn-box over">
<ul *ngIf="!expired">
<li>
<button mat-button (click)="onClickSave()">
저장
</button>
</li>
<li>
<button mat-button (click)="onClickSave()">
폴더열기
</button>
</li>
<li>
<button mat-button (click)="onClickSave()">
???
</button>
</li>
</ul>
</div>
<div class="progress over">
프로그레스 영역
</div>

View File

@ -0,0 +1,126 @@
// $tablet-s-width: 768px;
// .bubble-main {
// display: flex;
// flex-direction: row;
// padding: 14px;
// min-width: 300px;
// .file-img {
// display: inline-flex;
// width: 50px;
// height: 50px;
// float: left;
// margin-right: 14px;
// background-repeat: no-repeat;
// &.doc {
// background-image: url(/assets/images/file/icon_talk_doc.png);
// &.disable {
// background-image: url(/assets/images/file/icon_talk_doc_d.png);
// }
// }
// &.exe {
// background-image: url(/assets/images/file/icon_talk_exe.png);
// &.disable {
// background-image: url(/assets/images/file/icon_talk_exe_d.png);
// }
// }
// &.file {
// background-image: url(/assets/images/file/icon_talk_file.png);
// &.disable {
// background-image: url(/assets/images/file/icon_talk_file_d.png);
// }
// }
// &.hwp {
// background-image: url(/assets/images/file/icon_talk_hwp.png);
// &.disable {
// background-image: url(/assets/images/file/icon_talk_hwp_d.png);
// }
// }
// &.ppt,
// &.pptx {
// background-image: url(/assets/images/file/icon_talk_ppt.png);
// &.disable {
// background-image: url(/assets/images/file/icon_talk_ppt_d.png);
// }
// }
// &.xls,
// &.xlsm,
// &.xlsx {
// background-image: url(/assets/images/file/icon_talk_xls.png);
// &.disable {
// background-image: url(/assets/images/file/icon_talk_xls_d.png);
// }
// }
// &.zip {
// background-image: url(/assets/images/file/icon_talk_zip.png);
// &.disable {
// background-image: url(/assets/images/file/icon_talk_doc_d.png);
// }
// }
// }
// .file-info {
// display: inline-flex;
// flex-direction: column;
// text-align: left;
// float: left;
// line-height: 1.6em;
// .file-name {
// font-size: 1em;
// font-weight: bold;
// display: inline-flex;
// }
// .file-size {
// display: inline-flex;
// font-size: 11px;
// color: #666666;
// }
// .file-ext {
// font-size: 12px;
// color: #848d95;
// }
// }
// }
// .btn-box {
// width: 100%;
// height: 40px;
// border-top: 1px solid #dddddd;
// display: flex;
// width: 100%;
// text-align: center;
// font-size: 1em;
// ul {
// width: 100%;
// li {
// width: 50%;
// height: 100%;
// display: inline-block;
// text-align: center;
// align-items: center;
// border-right: 1px solid #dddddd;
// @media screen and (max-width: #{$tablet-s-width}) {
// width: 30%;
// }
// &:last-child {
// border-right: none;
// @media screen and (max-width: #{$tablet-s-width}) {
// width: 70%;
// }
// }
// .mat-button {
// width: 100%;
// display: block;
// height: 100%;
// font-size: 1em;
// }
// }
// &.expired {
// li {
// width: 100%;
// white-space: nowrap;
// color: #999999;
// align-items: center;
// line-height: 40px;
// }
// }
// }
// }

View File

@ -0,0 +1,28 @@
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { AttachFileComponent } from './attach-file.component';
describe('AttachFileComponent', () => {
let component: AttachFileComponent;
let fixture: ComponentFixture<AttachFileComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AttachFileComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AttachFileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,35 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FileEventJson } from '@ucap/protocol-event';
@Component({
selector: 'ucap-chat-message-box-attach-file',
templateUrl: './attach-file.component.html',
styleUrls: ['./attach-file.component.scss']
})
export class AttachFileComponent implements OnInit {
@Input()
fileInfo: FileEventJson;
@Input()
expired = false;
@Output()
save = new EventEmitter<string>();
@Output()
openViewer = new EventEmitter();
constructor() {}
ngOnInit() {}
onClickSave() {
this.save.emit('save');
}
onClickSaveAs() {
this.save.emit('saveAs');
}
onClickOpenViewer() {
this.openViewer.emit();
}
}

View File

@ -0,0 +1,5 @@
<div class="ucap-chat-message-box-date-splitter">
<span class="date bg-warn-chat">{{
this.message.sendDate | ucapDate: 'L dddd'
}}</span>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DateSplitterComponent } from './date-splitter.component';
describe('Chat::MessageBox::DateSplitterComponent', () => {
let component: DateSplitterComponent;
let fixture: ComponentFixture<DateSplitterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DateSplitterComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DateSplitterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,16 @@
import { Component, OnInit, Input } from '@angular/core';
import { Info, EventJson } from '@ucap/protocol-event';
@Component({
selector: 'ucap-chat-message-box-date-splitter',
templateUrl: './date-splitter.component.html',
styleUrls: ['./date-splitter.component.scss']
})
export class DateSplitterComponent implements OnInit {
@Input()
message: Info<EventJson>;
constructor() {}
ngOnInit() {}
}

View File

@ -0,0 +1,41 @@
<ng-container
*ngIf="fileInfo && fileInfo.fileType"
[ngSwitch]="fileInfo.fileType"
class="ucap-chat-message-box-file"
>
<ucap-chat-message-box-attach-file
*ngSwitchCase="FileType.File"
[expired]="getExpiredFile()"
(openViewer)="onClickFileViewer(fileInfo)"
(save)="onSave($event)"
>
</ucap-chat-message-box-attach-file>
<ucap-chat-message-box-attach-file
*ngSwitchCase="FileType.Sound"
[expired]="getExpiredFile()"
(openViewer)="onClickFileViewer(fileInfo)"
(save)="onSave($event)"
>
</ucap-chat-message-box-attach-file>
<ucap-chat-message-box-image
*ngSwitchCase="FileType.Image"
[expired]="getExpiredFile()"
(openViewer)="onClickFileViewer(fileInfo)"
(save)="onSave($event)"
></ucap-chat-message-box-image>
<ucap-chat-message-box-video
*ngSwitchCase="FileType.Video"
[expired]="getExpiredFile()"
(openViewer)="onClickFileViewer(fileInfo)"
(save)="onSave($event)"
></ucap-chat-message-box-video>
<!--
<ucap-chat-message-box-text
*ngSwitchDefault
[message]="message"
></ucap-chat-message-box-text> -->
</ng-container>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FileComponent } from './file.component';
describe('Chat::MessageBox::FileComponent', () => {
let component: FileComponent;
let fixture: ComponentFixture<FileComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FileComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FileComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,71 @@
import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
import { FileType, Info, FileEventJson } from '@ucap/protocol-event';
import { RoomInfo } from '@ucap/protocol-room';
import { FileDownloadItem, StatusCode } from '@ucap/api';
@Component({
selector: 'ucap-chat-message-box-file',
templateUrl: './file.component.html',
styleUrls: ['./file.component.scss']
})
export class FileComponent implements OnInit {
@Input()
message: Info<FileEventJson>;
@Input()
roomInfo: RoomInfo;
@Output()
save = new EventEmitter<{
fileInfo: FileEventJson;
fileDownloadItem: FileDownloadItem;
type: string;
}>();
@Output()
fileViewer = new EventEmitter<FileEventJson>();
fileInfo?: FileEventJson;
fileDownloadItem: FileDownloadItem;
errorMessage?: string;
FileType = FileType;
constructor() {}
ngOnInit() {
if (StatusCode.Success === this.message.sentMessageJson.statusCode) {
this.fileInfo = this.message.sentMessageJson;
} else {
this.errorMessage =
this.message.sentMessageJson.errorMessage || '[Error] System Error!!';
}
this.fileDownloadItem = new FileDownloadItem();
}
getExpiredFile() {
if (
!!this.roomInfo &&
this.roomInfo.expiredFileStdSeq <= this.message.seq
) {
return false;
} else {
return true;
}
}
onClickFileViewer(fileInfo: FileEventJson) {
if (!this.getExpiredFile()) {
this.fileViewer.emit(fileInfo);
}
}
onSave(value: string) {
if (!this.getExpiredFile()) {
this.save.emit({
fileInfo: this.fileInfo,
fileDownloadItem: this.fileDownloadItem,
type: value
});
}
}
}

View File

@ -0,0 +1,10 @@
<div
class="ucap-chat-message-box-image"
(mouseenter)="mouseEnter($event)"
(mouseleave)="mouseLeave($event)"
>
<div *ngIf="showExpired" class="expired-text">
<span>만기된 파일입니다.</span>
</div>
<img [src]="fileInfo.thumbUrl" />
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ImageComponent } from './image.component';
describe('Chat::MessageBox::ImageComponent', () => {
let component: ImageComponent;
let fixture: ComponentFixture<ImageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ImageComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ImageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,35 @@
import { Component, OnInit, Input } from '@angular/core';
import { FileEventJson } from '@ucap/protocol-event';
@Component({
selector: 'ucap-chat-message-box-image',
templateUrl: './image.component.html',
styleUrls: ['./image.component.scss']
})
export class ImageComponent implements OnInit {
@Input()
fileInfo: FileEventJson;
@Input()
expired = false;
showExpired = false;
constructor() {}
ngOnInit() {}
mouseEnter(event: MouseEvent): void {
if (this.expired) {
this.showExpired = true;
}
event.stopPropagation();
event.preventDefault();
}
mouseLeave(event: MouseEvent): void {
if (this.expired) {
this.showExpired = false;
}
event.stopPropagation();
event.preventDefault();
}
}

View File

@ -0,0 +1,39 @@
import { InformationComponent } from './information.component';
import { TextComponent } from './text.component';
import { FileComponent } from './file.component';
import { AttachFileComponent } from './attach-file.component';
import { ImageComponent } from './image.component';
import { VideoComponent } from './video.component';
import { StickerComponent } from './sticker.component';
import { MassComponent } from './mass.component';
import { ScheduleComponent } from './schedule.component';
import { VideoConferenceComponent } from './video-conference.component';
import { TranslationComponent } from './translation.component';
import { MassTranslationComponent } from './mass-translation.component';
import { RecallComponent } from './recall.component';
import { SmsComponent } from './sms.component';
import { ReplyComponent } from './reply.component';
import { ReadHereComponent } from './read-here.component';
import { DateSplitterComponent } from './date-splitter.component';
export const COMPONENTS = [
InformationComponent,
TextComponent,
FileComponent,
AttachFileComponent,
ImageComponent,
VideoComponent,
StickerComponent,
MassComponent,
ScheduleComponent,
VideoConferenceComponent,
TranslationComponent,
MassTranslationComponent,
RecallComponent,
ReadHereComponent,
DateSplitterComponent,
SmsComponent,
ReplyComponent
];

View File

@ -0,0 +1,88 @@
<div class="ucap-chat-message-box-information">
<ng-container [ngSwitch]="message.type">
<ng-container *ngSwitchCase="EventType.Join">
{{ getI18nForSir(getOwnerForJoinEventJson()) }}이
{{ getI18nForSir(getInviterForJoinEventJson()) }}을 초대했습니다.
<!-- {{
'chat.event.inviteToRoomWith'
| translate
: {
owner: getI18nForSir(message.sentMessageJson.owner),
inviter: getI18nForSir(message.sentMessageJson.inviter)
}
}} -->
</ng-container>
<ng-container *ngSwitchCase="EventType.Exit">
{{ message.sentMessage }}님이 퇴장하셨습니다.
<!-- {{
'chat.event.exitFromRoomWith'
| translate: { exitor: message.sentMessage }
}} -->
</ng-container>
<ng-container *ngSwitchCase="EventType.ForcedExit">
{{ message.exitForcingRequestUserName }}님이 {{ message.sentMessage }}님을
퇴장 시키셨습니다.
<!-- {{
'chat.event.ejectedFromRoomWith'
| translate
: {
requester: message.exitForcingRequestUserName,
ejected: message.sentMessage
}
}} -->
</ng-container>
<ng-container *ngSwitchCase="EventType.RenameRoom">
{{ getRequesterForRenameRoom() }}님이 대화방명을 '{{
getRoomNameForRenameRoom()
}}'으로 변경하셨습니다.
<!-- {{
'chat.event.renamedRoomWith'
| translate
: {
requester: message.sentMessageJson.requester,
roomName: message.sentMessageJson.roomName
}
}} -->
</ng-container>
<ng-container *ngSwitchCase="EventType.NotificationForTimerRoom">
{{ message.sentMessage }}
</ng-container>
<ng-container *ngSwitchCase="EventType.GuideForRoomTimerChanged">
{{ senderName }}님이 타이머를 설정하였습니다. ({{
getCalcTimerForGuideForRoomTimerChanged()
}})
<!-- {{
'chat.event.setTimerWith'
| translate
: {
requester: senderName,
timer: getCalcTimer(message.sentMessageJson.time * 1000)
}
}} -->
</ng-container>
<ng-container *ngSwitchCase="EventType.NotificationForiOSCapture">
{{ message.sentMessage }}님이 대화내용을 캡쳐하였습니다.
<!-- {{
'chat.event.iosCapture'
| translate
: {
requester: message.sentMessage
}
}} -->
</ng-container>
</ng-container>
</div>
<!-- <div class="ucap-chat-info-event">
<div class="info bg-primary-chat">
<strong class="text-warn-chat"
>김나영, 김준혁,강은정,나정미,박미영,박정은, 박훈, 문영준, 이지은, 이진현,
이현준, 임영찬, 임찬우, 정민우,정현욱, 최남진, 최영은, 최진우, 한고은,
한정후, 한지민</strong
>님이 초대되었습니다.
</div>
<div class="chat-event others bg-accent-chat">
자동 삭제 타이머가 10분으로 변경되었습니다.
</div>
</div> -->

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InformationComponent } from './information.component';
describe('Chat::MessageBox::InformationComponent', () => {
let component: InformationComponent;
let fixture: ComponentFixture<InformationComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [InformationComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(InformationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,134 @@
import { Component, OnInit, Input } from '@angular/core';
import {
EventType,
Info,
EventJson,
RenameRoomEventJson,
GuideForRoomTimerChangedEventJson,
JoinEventJson
} from '@ucap/protocol-event';
import { TranslateService } from '@ucap/ng-ui-organization';
@Component({
selector: 'ucap-chat-message-box-information',
templateUrl: './information.component.html',
styleUrls: ['./information.component.scss']
})
export class InformationComponent implements OnInit {
@Input()
message: Info<EventJson>;
@Input()
senderName?: string;
EventType = EventType;
constructor(private translateService: TranslateService) {}
ngOnInit() {}
getI18nForSir(names: string | string[]) {
if (Array.isArray(names)) {
const inviter: string[] = [];
// names.forEach((userName, idx) => {
// inviter.push(
// this.translateService.instant('common.messages.sirWith', {
// sir: userName
// })
// );
// });
names.forEach((userName, idx) => {
inviter.push(userName);
});
return inviter.join(',');
} else {
// return this.translateService.instant('common.messages.sirWith', {
// sir: names
// });
return names;
}
}
getOwnerForJoinEventJson() {
return (this.message.sentMessageJson as JoinEventJson).owner;
}
getInviterForJoinEventJson() {
return (this.message.sentMessageJson as JoinEventJson).inviter;
}
getCalcTimerForGuideForRoomTimerChanged() {
const millisec =
Number(
(this.message.sentMessageJson as GuideForRoomTimerChangedEventJson).time
) * 1000;
// const langs = this.translateService.instant([
// 'common.units.hourFrom',
// 'common.units.minute',
// 'common.units.second'
// ]);
// switch (millisec) {
// case 5000:
// return `5 ${langs['common.units.second']}`;
// case 10000:
// return `10 ${langs['common.units.second']}`;
// case 30000:
// return `30 ${langs['common.units.second']}`;
// case 60000:
// return `1 ${langs['common.units.minute']}`;
// case 300000:
// return `5 ${langs['common.units.minute']}`;
// case 600000:
// return `10 ${langs['common.units.minute']}`;
// case 1800000:
// return `30 ${langs['common.units.minute']}`;
// case 3600000:
// return `1 ${langs['common.units.hourFrom']}`;
// case 21600000:
// return `6 ${langs['common.units.hourFrom']}`;
// case 43200000:
// return `12 ${langs['common.units.hourFrom']}`;
// case 86400000:
// return `24 ${langs['common.units.hourFrom']}`;
// default:
// return `0 ${langs['common.units.second']}`;
// }
const langs = ['초', '분', '시간'];
switch (millisec) {
case 5000:
return `5 ${langs[0]}`;
case 10000:
return `10 ${langs[0]}`;
case 30000:
return `30 ${langs[0]}`;
case 60000:
return `1 ${langs[1]}`;
case 300000:
return `5 ${langs[1]}`;
case 600000:
return `10 ${langs[1]}`;
case 1800000:
return `30 ${langs[1]}`;
case 3600000:
return `1 ${langs[2]}`;
case 21600000:
return `6 ${langs[2]}`;
case 43200000:
return `12 ${langs[2]}`;
case 86400000:
return `24 ${langs[2]}`;
default:
return `0 ${langs[0]}`;
}
}
getRequesterForRenameRoom() {
return (this.message.sentMessageJson as RenameRoomEventJson).requester;
}
getRoomNameForRenameRoom() {
return (this.message.sentMessageJson as RenameRoomEventJson).roomName;
}
}

View File

@ -0,0 +1,44 @@
<div class="ucap-chat-message-box-mass-translation">
<div
*ngIf="!translationSimpleview || (!!translationSimpleview && !!isMe)"
class="contents original"
[innerHTML]="
message.sentMessageJson.original
| ucapSafeHtml
| ucapLinefeedToHtml
| ucapLinky
"
(contextmenu)="onContextMenuMessage($event, 'original')"
></div>
<div
*ngIf="!translationSimpleview || (!!translationSimpleview && !isMe)"
class="contents translation"
(contextmenu)="onContextMenuMessage($event, 'translation')"
>
<span class="language">{{ message.sentMessageJson.destLocale }}</span>
<span
[innerHTML]="
message.sentMessageJson.translation
| ucapSafeHtml
| ucapLinefeedToHtml
| ucapLinky
"
>
</span>
</div>
</div>
<div class="btn-box">
<ul>
<li>
<button mat-button (click)="onClickMassDetail('O')">
전체보기
</button>
</li>
<li>
<button mat-button (click)="onClickMassDetail('T')">
<span class="language">{{ message.sentMessageJson.destLocale }}</span>
번역보기
</button>
</li>
</ul>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MassTranslationComponent } from './mass-translation.component';
describe('Chat::MessageBox::MassTranslationComponent', () => {
let component: MassTranslationComponent;
let fixture: ComponentFixture<MassTranslationComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MassTranslationComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MassTranslationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,73 @@
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
AfterViewInit,
ElementRef,
Inject
} from '@angular/core';
import { Info, MassTranslationEventJson } from '@ucap/protocol-event';
@Component({
selector: 'ucap-chat-message-box-mass-translation',
templateUrl: './mass-translation.component.html',
styleUrls: ['./mass-translation.component.scss']
})
export class MassTranslationComponent implements OnInit, AfterViewInit {
@Input()
message: Info<MassTranslationEventJson>;
@Input()
translationSimpleview: boolean;
@Input()
isMe: boolean;
@Output()
massTranslationDetail = new EventEmitter<{
message: Info<MassTranslationEventJson>;
contentsType: string;
}>();
@Output()
contextMenu = new EventEmitter<{
event: MouseEvent;
type: string;
}>();
@Output()
openLink = new EventEmitter<string>();
constructor(private elementRef: ElementRef) {}
ngOnInit() {}
ngAfterViewInit(): void {
if (
!!this.elementRef.nativeElement &&
!!this.elementRef.nativeElement.querySelector('a')
) {
const elements = this.elementRef.nativeElement.querySelectorAll('a');
elements.forEach((element) => {
element.addEventListener('click', this.onClickEvent.bind(this));
});
}
}
onClickEvent(event: MouseEvent) {
this.openLink.emit((event.target as HTMLAnchorElement).text);
}
onClickMassDetail(contentsType: string) {
this.massTranslationDetail.emit({
message: this.message,
contentsType
});
}
onContextMenuMessage(event: MouseEvent, type: string) {
this.contextMenu.emit({ event, type });
}
}

View File

@ -0,0 +1,12 @@
<div class="ucap-chat-message-box-mass">
<span
class="content"
[innerHTML]="content | ucapSafeHtml | ucapLinefeedToHtml | ucapLinky"
>
</span>
</div>
<div *ngIf="detailButteonShow" class="btn-box">
<button mat-button (click)="onClickDetailView()">
전체보기
</button>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MassComponent } from './mass.component';
describe('Chat::MessageBox::MassComponent', () => {
let component: MassComponent;
let fixture: ComponentFixture<MassComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [MassComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MassComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,74 @@
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ElementRef,
AfterViewInit,
Inject
} from '@angular/core';
import { Info, MassTextEventJson } from '@ucap/protocol-event';
import { StatusCode } from '@ucap/api';
@Component({
selector: 'ucap-chat-message-box-mass',
templateUrl: './mass.component.html',
styleUrls: ['./mass.component.scss']
})
export class MassComponent implements OnInit, AfterViewInit {
@Input()
message: Info<MassTextEventJson>;
@Output()
massDetail = new EventEmitter<number>();
@Output()
openLink = new EventEmitter<string>();
content: string;
eventMassSeq: number;
detailButteonShow = true;
constructor(private elementRef: ElementRef) {}
ngOnInit() {
try {
if (StatusCode.Success === this.message.sentMessageJson.statusCode) {
this.content = this.message.sentMessageJson.content;
} else {
this.content =
this.message.sentMessageJson.errorMessage || '[Error] System Error!!';
this.detailButteonShow = false;
}
if (!!this.message.sentMessageJson.massSeq) {
this.eventMassSeq = this.message.sentMessageJson.massSeq;
} else {
this.detailButteonShow = false;
}
} catch (e) {
this.detailButteonShow = false;
}
}
ngAfterViewInit(): void {
if (
!!this.elementRef.nativeElement &&
!!this.elementRef.nativeElement.querySelector('a')
) {
const elements = this.elementRef.nativeElement.querySelectorAll('a');
elements.forEach((element) => {
element.addEventListener('click', this.onClickEvent.bind(this));
});
}
}
onClickEvent(event: MouseEvent) {
this.openLink.emit((event.target as HTMLAnchorElement).text);
}
onClickDetailView() {
this.massDetail.emit(this.eventMassSeq);
}
}

View File

@ -0,0 +1,3 @@
<div class="ucap-chat-message-box-read-here">
<span>여기까지 읽었습니다</span>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReadHereComponent } from './read-here.component';
describe('Chat::MessageBox::ReadHereComponent', () => {
let component: ReadHereComponent;
let fixture: ComponentFixture<ReadHereComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ReadHereComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ReadHereComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'ucap-chat-message-box-read-here',
templateUrl: './read-here.component.html',
styleUrls: ['./read-here.component.scss']
})
export class ReadHereComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

@ -0,0 +1,4 @@
<div class="ucap-chat-message-box-recall">
<span class="icon-recall"><i class="material-icons">redo</i></span>
<span class="recall-msg">회수된 메시지</span>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { RecallComponent } from './recall.component';
describe('Chat::MessageBox::RecallComponent', () => {
let component: RecallComponent;
let fixture: ComponentFixture<RecallComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [RecallComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(RecallComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'ucap-chat-message-box-recall',
templateUrl: './recall.component.html',
styleUrls: ['./recall.component.scss']
})
export class RecallComponent implements OnInit {
constructor() {}
ngOnInit() {}
}

View File

@ -0,0 +1,14 @@
<div class="ucap-chat-message-box-reply">
<div class="event-header" style="color: black;">
<ul>
<li class="sub-title">나에게 답장</li>
<li class="parent-contents">
올려 놓았습니다. 확인해 보시기 바랍니다.
</li>
</ul>
</div>
<div class="contents">
<span class="language"></span>
네 감사합니다. 수고가 많으시네요. 오늘 중으로 확인해 보겠습니다.
</div>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TextComponent } from './text.component';
describe('Chat::MessageBox::TextComponent', () => {
let component: TextComponent;
let fixture: ComponentFixture<TextComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TextComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TextComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,21 @@
import {
Component,
OnInit,
Input,
ElementRef,
AfterViewInit,
Inject
} from '@angular/core';
@Component({
selector: 'ucap-chat-message-box-reply',
templateUrl: './reply.component.html',
styleUrls: ['./reply.component.scss']
})
export class ReplyComponent implements OnInit, AfterViewInit {
constructor(private elementRef: ElementRef) {}
ngOnInit() {}
ngAfterViewInit(): void {}
}

View File

@ -0,0 +1,38 @@
<div class="ucap-chat-message-box-schedule">
<div class="event-header" style="color: black;">
<ng-container
*ngIf="!!message.sentMessageJson && !!message.sentMessageJson.contents"
>
<ng-container [ngSwitch]="message.sentMessageJson.contents">
<ng-container *ngSwitchCase="PlanContentType.New">
{{ 'chat.event.scheduleTypeNew' | ucapI18n }}
</ng-container>
<ng-container *ngSwitchCase="PlanContentType.Update">
{{ 'chat.event.scheduleTypeUpdate' | ucapI18n }}
</ng-container>
<ng-container *ngSwitchCase="PlanContentType.Delete">
{{ 'chat.event.scheduleTypeDelete' | ucapI18n }}
</ng-container>
<ng-container *ngSwitchDefault>
<!-- {{ 'chat.event.scheduleTypeDefault' | translate }} -->
{{ getAlertLeftTime() }}
</ng-container>
</ng-container>
</ng-container>
</div>
<ul class="event-info">
<li class="place">
마곡 E14동 906호
</li>
<li class="date">
2020.05.23 9:30 ~ 2020.05.23 11:30
</li>
<li class="event-content">
{{ message.sentMessageJson.title }}
{{ message.sentMessageJson.contents }}
</li>
</ul>
</div>
<div class="btn-box">
<button mat-button>상세보기</button>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ScheduleComponent } from './schedule.component';
describe('Chat::MessageBox::ScheduleComponent', () => {
let component: ScheduleComponent;
let fixture: ComponentFixture<ScheduleComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ScheduleComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ScheduleComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,64 @@
import { Component, OnInit, Input } from '@angular/core';
import { Info, PlanEventJson, PlanContentType } from '@ucap/protocol-event';
import { TranslateService } from '@ucap/ng-ui-organization';
import moment from 'moment';
@Component({
selector: 'ucap-chat-message-box-schedule',
templateUrl: './schedule.component.html',
styleUrls: ['./schedule.component.scss']
})
export class ScheduleComponent implements OnInit {
@Input()
message: Info<PlanEventJson>;
PlanContentType = PlanContentType;
date: any;
endDate: any;
constructor(private translateService: TranslateService) {}
ngOnInit() {
if (!!this.message && !!this.message.sentMessageJson) {
if (!!this.message.sentMessageJson.date) {
let strDate = this.message.sentMessageJson.date
.replace(/ /g, '')
.replace(/\n/g, '')
.replace(/(\([월,화,수,목,금,토,일]\))/g, '');
if (strDate.indexOf('오전') > -1) {
strDate = strDate.replace('오전', ' ');
} else if (strDate.indexOf('오후') > -1) {
strDate = strDate.replace('오후', ' ');
const arr = strDate.split(' ');
const h = Number(arr[1].split(':')[0]) + 12;
strDate = arr[0] + ' ' + h + ':' + arr[1].split(':')[1];
}
this.date = moment(strDate).toDate();
if (this.date === 'Invalid Date') {
this.date = this.message.sentMessageJson.date.replace(/\n/g, '');
}
}
}
}
onClickSave(): void {}
getAlertLeftTime(): string {
let content = this.message.sentMessageJson.contents;
if (content.indexOf('PLAN_CONTENTS_MINUTE') > -1) {
content = content.replace('PLAN_CONTENTS_MINUTE', '분');
} else if (content.indexOf('PLAN_CONTENTS_HOUR') > -1) {
content = content.replace('PLAN_CONTENTS_HOUR', '시간');
}
if (content.indexOf('PLAN_CONTENTS_AFTER') > -1) {
content = content.replace('PLAN_CONTENTS_AFTER', ' 전 알림');
}
content = '[이벤트] ' + content;
return content;
}
}

View File

@ -0,0 +1,9 @@
<div class="ucap-chat-message-box-sms">
<div class="event-header" style="color: black;">
SMS
</div>
<div class="contents">
SMS 내용 공간입니다. 내용을 보여주세요 SMS 내용 공간입니다. 내용을
보여주세요 SMS 내용 공간입니다. 내용을 보여주세요
</div>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TextComponent } from './text.component';
describe('Chat::MessageBox::TextComponent', () => {
let component: TextComponent;
let fixture: ComponentFixture<TextComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TextComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TextComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,21 @@
import {
Component,
OnInit,
Input,
ElementRef,
AfterViewInit,
Inject
} from '@angular/core';
@Component({
selector: 'ucap-chat-message-box-sms',
templateUrl: './sms.component.html',
styleUrls: ['./sms.component.scss']
})
export class SmsComponent implements OnInit, AfterViewInit {
constructor(private elementRef: ElementRef) {}
ngOnInit() {}
ngAfterViewInit(): void {}
}

View File

@ -0,0 +1,11 @@
<div class="ucap-chat-message-box-sticker">
<ul>
<li *ngIf="stickerUrl">
<img [src]="stickerUrl" />
</li>
<li
*ngIf="contents"
[innerHTML]="contents | ucapSafeHtml | ucapLinefeedToHtml | ucapLinky"
></li>
</ul>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { StickerComponent } from './sticker.component';
describe('Chat::MessageBox::StickerComponent', () => {
let component: StickerComponent;
let fixture: ComponentFixture<StickerComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [StickerComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(StickerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,54 @@
import {
Component,
OnInit,
Input,
ElementRef,
AfterViewInit,
EventEmitter,
Output
} from '@angular/core';
import { Info, StickerEventJson } from '@ucap/protocol-event';
@Component({
selector: 'ucap-chat-message-box-sticker',
templateUrl: './sticker.component.html',
styleUrls: ['./sticker.component.scss']
})
export class StickerComponent implements OnInit, AfterViewInit {
@Input()
message: Info<StickerEventJson>;
@Input()
stickerUrl?: string;
@Input()
stickerDefaultImagePath: string;
@Output()
openLink = new EventEmitter<string>();
contents: string;
constructor(private elementRef: ElementRef) {}
ngOnInit() {
if (!!this.message.sentMessageJson?.chat) {
this.contents = this.message.sentMessageJson.chat;
}
}
ngAfterViewInit(): void {
if (
!!this.elementRef.nativeElement &&
!!this.elementRef.nativeElement.querySelector('a')
) {
const elements = this.elementRef.nativeElement.querySelectorAll('a');
elements.forEach((element) => {
element.addEventListener('click', this.onClickEvent.bind(this));
});
}
}
onClickEvent(event: MouseEvent) {
this.openLink.emit((event.target as HTMLAnchorElement).text);
}
}

View File

@ -0,0 +1,3 @@
<div class="ucap-chat-message-box-text">
<span [innerHTML]="message.sentMessage | ucapSafeHtml | ucapLinky"></span>
</div>

View File

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TextComponent } from './text.component';
describe('Chat::MessageBox::TextComponent', () => {
let component: TextComponent;
let fixture: ComponentFixture<TextComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [TextComponent]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(TextComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,43 @@
import {
Component,
OnInit,
Input,
ElementRef,
AfterViewInit,
Output,
EventEmitter
} from '@angular/core';
import { Info, EventJson } from '@ucap/protocol-event';
@Component({
selector: 'ucap-chat-message-box-text',
templateUrl: './text.component.html',
styleUrls: ['./text.component.scss']
})
export class TextComponent implements OnInit, AfterViewInit {
@Input()
message: Info<EventJson>;
@Output()
openLink = new EventEmitter<string>();
constructor(private elementRef: ElementRef) {}
ngOnInit() {}
ngAfterViewInit(): void {
if (
!!this.elementRef.nativeElement &&
!!this.elementRef.nativeElement.querySelector('a')
) {
const elements = this.elementRef.nativeElement.querySelectorAll('a');
elements.forEach((element) => {
element.addEventListener('click', this.onClickEvent.bind(this));
});
}
}
onClickEvent(event: MouseEvent) {
this.openLink.emit((event.target as HTMLAnchorElement).text);
}
}

View File

@ -0,0 +1,20 @@
<div class="ucap-chat-message-box-translation">
<div class="sticker" *ngIf="!!stickerUrl && stickerUrl.trim().length > 0">
<img [src]="stickerUrl" />
</div>
<div
*ngIf="!translationSimpleview || (!!translationSimpleview && !!isMe)"
class="original"
[innerHTML]="message.sentMessageJson.original | ucapLinky"
(contextmenu)="onContextMenuMessage($event, 'original')"
></div>
<div
*ngIf="!translationSimpleview || (!!translationSimpleview && !isMe)"
class="translation"
(contextmenu)="onContextMenuMessage($event, 'translation')"
>
<span class="language">{{ message.sentMessageJson.locale }}</span>
<span [innerHTML]="message.sentMessageJson.translation | ucapLinky"> </span>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More