0710 sync
This commit is contained in:
parent
cbe1ddbd29
commit
bf63e11021
38
angular.json
38
angular.json
|
@ -1074,44 +1074,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"native-browser": {
|
|
||||||
"projectType": "library",
|
|
||||||
"root": "projects/native-browser",
|
|
||||||
"sourceRoot": "projects/native-browser/src",
|
|
||||||
"prefix": "lib",
|
|
||||||
"architect": {
|
|
||||||
"build": {
|
|
||||||
"builder": "@angular-devkit/build-ng-packagr:build",
|
|
||||||
"options": {
|
|
||||||
"tsConfig": "projects/native-browser/tsconfig.lib.json",
|
|
||||||
"project": "projects/native-browser/ng-package.json"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"tsConfig": "projects/native-browser/tsconfig.lib.prod.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"builder": "@angular-devkit/build-angular:karma",
|
|
||||||
"options": {
|
|
||||||
"main": "projects/native-browser/src/test.ts",
|
|
||||||
"tsConfig": "projects/native-browser/tsconfig.spec.json",
|
|
||||||
"karmaConfig": "projects/native-browser/karma.conf.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"builder": "@angular-devkit/build-angular:tslint",
|
|
||||||
"options": {
|
|
||||||
"tsConfig": [
|
|
||||||
"projects/native-browser/tsconfig.lib.json",
|
|
||||||
"projects/native-browser/tsconfig.spec.json"
|
|
||||||
],
|
|
||||||
"exclude": ["**/node_modules/**"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"store-authentication": {
|
"store-authentication": {
|
||||||
"projectType": "library",
|
"projectType": "library",
|
||||||
"root": "projects/store-authentication",
|
"root": "projects/store-authentication",
|
||||||
|
|
760
package-lock.json
generated
760
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
94
package.json
94
package.json
|
@ -16,9 +16,8 @@
|
||||||
"build:pi": "node ./scripts/build.js pi",
|
"build:pi": "node ./scripts/build.js pi",
|
||||||
"build:core": "node ./scripts/build.js core",
|
"build:core": "node ./scripts/build.js core",
|
||||||
"build:logger": "node ./scripts/build.js logger",
|
"build:logger": "node ./scripts/build.js logger",
|
||||||
"build:native:all": "npm-run-all -s build:native build:native-browser",
|
"build:native:all": "npm-run-all -s build:native",
|
||||||
"build:native": "node ./scripts/build.js native",
|
"build:native": "node ./scripts/build.js native",
|
||||||
"build:native-browser": "node ./scripts/build.js native-browser",
|
|
||||||
"build:protocol:all": "npm-run-all -s build:protocol build:protocol-authentication build:protocol-buddy build:protocol-event build:protocol-file build:protocol-group build:protocol-status build:protocol-info build:protocol-inner build:protocol-option build:protocol-ping build:protocol-query build:protocol-room build:protocol-service build:protocol-sync build:protocol-umg",
|
"build:protocol:all": "npm-run-all -s build:protocol build:protocol-authentication build:protocol-buddy build:protocol-event build:protocol-file build:protocol-group build:protocol-status build:protocol-info build:protocol-inner build:protocol-option build:protocol-ping build:protocol-query build:protocol-room build:protocol-service build:protocol-sync build:protocol-umg",
|
||||||
"build:protocol": "node ./scripts/build.js protocol",
|
"build:protocol": "node ./scripts/build.js protocol",
|
||||||
"build:protocol-authentication": "node ./scripts/build.js protocol-authentication",
|
"build:protocol-authentication": "node ./scripts/build.js protocol-authentication",
|
||||||
|
@ -65,9 +64,8 @@
|
||||||
"publish:pi": "cd ./dist/pi && npm publish",
|
"publish:pi": "cd ./dist/pi && npm publish",
|
||||||
"publish:core": "cd ./dist/core && npm publish",
|
"publish:core": "cd ./dist/core && npm publish",
|
||||||
"publish:logger": "cd ./dist/logger && npm publish",
|
"publish:logger": "cd ./dist/logger && npm publish",
|
||||||
"publish:native:all": "npm-run-all -s publish:native publish:native-browser",
|
"publish:native:all": "npm-run-all -s publish:native",
|
||||||
"publish:native": "cd ./dist/native && npm publish",
|
"publish:native": "cd ./dist/native && npm publish",
|
||||||
"publish:native-browser": "cd ./dist/native-browser && npm publish",
|
|
||||||
"publish:protocol:all": "npm-run-all -s publish:protocol publish:protocol-authentication publish:protocol-buddy publish:protocol-event publish:protocol-file publish:protocol-group publish:protocol-status publish:protocol-info publish:protocol-inner publish:protocol-option publish:protocol-ping publish:protocol-query publish:protocol-room publish:protocol-service publish:protocol-sync publish:protocol-umg",
|
"publish:protocol:all": "npm-run-all -s publish:protocol publish:protocol-authentication publish:protocol-buddy publish:protocol-event publish:protocol-file publish:protocol-group publish:protocol-status publish:protocol-info publish:protocol-inner publish:protocol-option publish:protocol-ping publish:protocol-query publish:protocol-room publish:protocol-service publish:protocol-sync publish:protocol-umg",
|
||||||
"publish:protocol": "cd ./dist/protocol && npm publish",
|
"publish:protocol": "cd ./dist/protocol && npm publish",
|
||||||
"publish:protocol-authentication": "cd ./dist/protocol-authentication && npm publish",
|
"publish:protocol-authentication": "cd ./dist/protocol-authentication && npm publish",
|
||||||
|
@ -110,35 +108,33 @@
|
||||||
"storybook": "start-storybook -p 6006",
|
"storybook": "start-storybook -p 6006",
|
||||||
"build-storybook": "build-storybook"
|
"build-storybook": "build-storybook"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {},
|
||||||
"@storybook/addon-knobs": "^5.3.18",
|
|
||||||
"tslib": "^1.10.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "^0.900.6",
|
"@angular-devkit/build-angular": "^0.900.6",
|
||||||
"@angular-devkit/build-ng-packagr": "^0.900.6",
|
"@angular-devkit/build-ng-packagr": "^0.900.6",
|
||||||
"@angular/animations": "^9.0.6",
|
"@angular/animations": "^9.1.11",
|
||||||
"@angular/cdk": "^9.1.2",
|
"@angular/cdk": "^9.2.4",
|
||||||
"@angular/cli": "^9.0.6",
|
"@angular/cli": "^9.1.9",
|
||||||
"@angular/common": "^9.0.6",
|
"@angular/common": "^9.1.11",
|
||||||
"@angular/compiler": "^9.0.6",
|
"@angular/compiler": "^9.1.11",
|
||||||
"@angular/compiler-cli": "^9.0.6",
|
"@angular/compiler-cli": "^9.1.11",
|
||||||
"@angular/core": "^9.0.6",
|
"@angular/core": "^9.1.11",
|
||||||
"@angular/flex-layout": "^9.0.0-beta.29",
|
"@angular/flex-layout": "^9.0.0-beta.31",
|
||||||
"@angular/forms": "^9.0.6",
|
"@angular/forms": "^9.1.11",
|
||||||
"@angular/language-service": "^9.0.6",
|
"@angular/language-service": "^9.1.11",
|
||||||
"@angular/material": "^9.1.2",
|
"@angular/material": "^9.2.4",
|
||||||
"@angular/material-moment-adapter": "^9.1.2",
|
"@angular/material-moment-adapter": "^9.2.4",
|
||||||
"@angular/platform-browser": "^9.0.6",
|
"@angular/platform-browser": "^9.1.11",
|
||||||
"@angular/platform-browser-dynamic": "^9.0.6",
|
"@angular/platform-browser-dynamic": "^9.1.11",
|
||||||
"@angular/router": "^9.0.6",
|
"@angular/router": "^9.1.11",
|
||||||
"@babel/core": "^7.9.0",
|
"@babel/core": "^7.9.0",
|
||||||
"@ngrx/effects": "^9.0.0",
|
"@ngrx/effects": "^9.2.0",
|
||||||
"@ngrx/entity": "^9.0.0",
|
"@ngrx/entity": "^9.2.0",
|
||||||
"@ngrx/router-store": "^9.0.0",
|
"@ngrx/router-store": "^9.2.0",
|
||||||
"@ngrx/store": "^9.0.0",
|
"@ngrx/store": "^9.2.0",
|
||||||
"@ngrx/store-devtools": "^9.0.0",
|
"@ngrx/store-devtools": "^9.2.0",
|
||||||
"@storybook/addon-actions": "^5.3.18",
|
"@storybook/addon-actions": "^5.3.18",
|
||||||
|
"@storybook/addon-knobs": "^5.3.18",
|
||||||
"@storybook/addon-links": "^5.3.18",
|
"@storybook/addon-links": "^5.3.18",
|
||||||
"@storybook/addon-notes": "^5.3.18",
|
"@storybook/addon-notes": "^5.3.18",
|
||||||
"@storybook/addons": "^5.3.18",
|
"@storybook/addons": "^5.3.18",
|
||||||
|
@ -148,15 +144,14 @@
|
||||||
"@types/moment-timezone": "^0.5.12",
|
"@types/moment-timezone": "^0.5.12",
|
||||||
"@types/node": "^12.12.30",
|
"@types/node": "^12.12.30",
|
||||||
"@ucap/api": "~0.0.1",
|
"@ucap/api": "~0.0.1",
|
||||||
"@ucap/api-common": "~0.0.5",
|
"@ucap/api-common": "~0.0.7",
|
||||||
"@ucap/api-external": "~0.0.2",
|
"@ucap/api-external": "~0.0.2",
|
||||||
"@ucap/api-message": "~0.0.1",
|
"@ucap/api-message": "~0.0.1",
|
||||||
"@ucap/api-prompt": "~0.0.1",
|
"@ucap/api-prompt": "~0.0.1",
|
||||||
"@ucap/api-public": "~0.0.1",
|
"@ucap/api-public": "~0.0.1",
|
||||||
"@ucap/core": "~0.0.6",
|
"@ucap/core": "~0.0.14",
|
||||||
"@ucap/logger": "~0.0.12",
|
"@ucap/logger": "~0.0.12",
|
||||||
"@ucap/native": "~0.0.1",
|
"@ucap/native": "~0.0.19",
|
||||||
"@ucap/native-browser": "~0.0.1",
|
|
||||||
"@ucap/ng-api-common": "file:pack/ucap-ng-api-common-0.0.1.tgz",
|
"@ucap/ng-api-common": "file:pack/ucap-ng-api-common-0.0.1.tgz",
|
||||||
"@ucap/ng-api-external": "file:pack/ucap-ng-api-external-0.0.1.tgz",
|
"@ucap/ng-api-external": "file:pack/ucap-ng-api-external-0.0.1.tgz",
|
||||||
"@ucap/ng-api-message": "file:pack/ucap-ng-api-message-0.0.1.tgz",
|
"@ucap/ng-api-message": "file:pack/ucap-ng-api-message-0.0.1.tgz",
|
||||||
|
@ -165,8 +160,7 @@
|
||||||
"@ucap/ng-core": "file:pack/ucap-ng-core-0.0.7.tgz",
|
"@ucap/ng-core": "file:pack/ucap-ng-core-0.0.7.tgz",
|
||||||
"@ucap/ng-i18n": "file:pack/ucap-ng-i18n-0.0.6.tgz",
|
"@ucap/ng-i18n": "file:pack/ucap-ng-i18n-0.0.6.tgz",
|
||||||
"@ucap/ng-logger": "file:pack/ucap-ng-logger-0.0.2.tgz",
|
"@ucap/ng-logger": "file:pack/ucap-ng-logger-0.0.2.tgz",
|
||||||
"@ucap/ng-native": "file:pack/ucap-ng-native-0.0.1.tgz",
|
"@ucap/ng-native": "file:pack/ucap-ng-native-0.0.5.tgz",
|
||||||
"@ucap/ng-native-browser": "file:pack/ucap-ng-native-browser-0.0.1.tgz",
|
|
||||||
"@ucap/ng-pi": "file:pack/ucap-ng-pi-0.0.1.tgz",
|
"@ucap/ng-pi": "file:pack/ucap-ng-pi-0.0.1.tgz",
|
||||||
"@ucap/ng-protocol": "file:pack/ucap-ng-protocol-0.0.3.tgz",
|
"@ucap/ng-protocol": "file:pack/ucap-ng-protocol-0.0.3.tgz",
|
||||||
"@ucap/ng-protocol-authentication": "file:pack/ucap-ng-protocol-authentication-0.0.3.tgz",
|
"@ucap/ng-protocol-authentication": "file:pack/ucap-ng-protocol-authentication-0.0.3.tgz",
|
||||||
|
@ -184,37 +178,37 @@
|
||||||
"@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.11.tgz",
|
"@ucap/ng-store-authentication": "file:pack/ucap-ng-store-authentication-0.0.14.tgz",
|
||||||
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.19.tgz",
|
"@ucap/ng-store-chat": "file:pack/ucap-ng-store-chat-0.0.66.tgz",
|
||||||
"@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.14.tgz",
|
"@ucap/ng-store-group": "file:pack/ucap-ng-store-group-0.0.22.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.20.tgz",
|
||||||
"@ucap/ng-ui": "file:pack/ucap-ng-ui-0.0.24.tgz",
|
"@ucap/ng-ui": "file:pack/ucap-ng-ui-0.0.97.tgz",
|
||||||
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.25.tgz",
|
"@ucap/ng-ui-authentication": "file:pack/ucap-ng-ui-authentication-0.0.29.tgz",
|
||||||
"@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.13.tgz",
|
"@ucap/ng-ui-chat": "file:pack/ucap-ng-ui-chat-0.0.72.tgz",
|
||||||
"@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.33.tgz",
|
"@ucap/ng-ui-group": "file:pack/ucap-ng-ui-group-0.0.78.tgz",
|
||||||
"@ucap/ng-ui-material": "file:pack/ucap-ng-ui-material-0.0.4.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.84.tgz",
|
"@ucap/ng-ui-organization": "file:pack/ucap-ng-ui-organization-0.0.202.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",
|
||||||
"@ucap/pi": "~0.0.5",
|
"@ucap/pi": "~0.0.8",
|
||||||
"@ucap/protocol": "~0.0.11",
|
"@ucap/protocol": "~0.0.11",
|
||||||
"@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.6",
|
||||||
"@ucap/protocol-file": "~0.0.5",
|
"@ucap/protocol-file": "~0.0.6",
|
||||||
"@ucap/protocol-group": "~0.0.5",
|
"@ucap/protocol-group": "~0.0.5",
|
||||||
"@ucap/protocol-info": "~0.0.6",
|
"@ucap/protocol-info": "~0.0.9",
|
||||||
"@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",
|
||||||
"@ucap/protocol-query": "~0.0.5",
|
"@ucap/protocol-query": "~0.0.5",
|
||||||
"@ucap/protocol-room": "~0.0.6",
|
"@ucap/protocol-room": "~0.0.7",
|
||||||
"@ucap/protocol-service": "~0.0.4",
|
"@ucap/protocol-service": "~0.0.4",
|
||||||
"@ucap/protocol-status": "~0.0.5",
|
"@ucap/protocol-status": "~0.0.5",
|
||||||
"@ucap/protocol-sync": "~0.0.4",
|
"@ucap/protocol-sync": "~0.0.6",
|
||||||
"@ucap/protocol-umg": "~0.0.5",
|
"@ucap/protocol-umg": "~0.0.5",
|
||||||
"@ucap/ui-scss": "~0.0.3",
|
"@ucap/ui-scss": "~0.0.5",
|
||||||
"@ucap/web-socket": "~0.0.5",
|
"@ucap/web-socket": "~0.0.5",
|
||||||
"@ucap/web-storage": "~0.0.5",
|
"@ucap/web-storage": "~0.0.5",
|
||||||
"autolinker": "^3.13.0",
|
"autolinker": "^3.13.0",
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
# NativeBrowser
|
|
||||||
|
|
||||||
This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.0.2.
|
|
||||||
|
|
||||||
## Code scaffolding
|
|
||||||
|
|
||||||
Run `ng generate component component-name --project native-browser` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project native-browser`.
|
|
||||||
> Note: Don't forget to add `--project native-browser` or else it will be added to the default project in your `angular.json` file.
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
Run `ng build native-browser` to build the project. The build artifacts will be stored in the `dist/` directory.
|
|
||||||
|
|
||||||
## Publishing
|
|
||||||
|
|
||||||
After building your library with `ng build native-browser`, go to the dist folder `cd dist/native-browser` and run `npm publish`.
|
|
||||||
|
|
||||||
## Running unit tests
|
|
||||||
|
|
||||||
Run `ng test native-browser` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
|
||||||
|
|
||||||
## Further help
|
|
||||||
|
|
||||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
|
|
@ -1,32 +0,0 @@
|
||||||
// Karma configuration file, see link for more information
|
|
||||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
|
||||||
|
|
||||||
module.exports = function (config) {
|
|
||||||
config.set({
|
|
||||||
basePath: '',
|
|
||||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
|
||||||
plugins: [
|
|
||||||
require('karma-jasmine'),
|
|
||||||
require('karma-chrome-launcher'),
|
|
||||||
require('karma-jasmine-html-reporter'),
|
|
||||||
require('karma-coverage-istanbul-reporter'),
|
|
||||||
require('@angular-devkit/build-angular/plugins/karma')
|
|
||||||
],
|
|
||||||
client: {
|
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
|
||||||
},
|
|
||||||
coverageIstanbulReporter: {
|
|
||||||
dir: require('path').join(__dirname, '../../coverage/native-browser'),
|
|
||||||
reports: ['html', 'lcovonly', 'text-summary'],
|
|
||||||
fixWebpackSourcePaths: true
|
|
||||||
},
|
|
||||||
reporters: ['progress', 'kjhtml'],
|
|
||||||
port: 9876,
|
|
||||||
colors: true,
|
|
||||||
logLevel: config.LOG_INFO,
|
|
||||||
autoWatch: true,
|
|
||||||
browsers: ['Chrome'],
|
|
||||||
singleRun: false,
|
|
||||||
restartOnFileChange: true
|
|
||||||
});
|
|
||||||
};
|
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
|
||||||
"dest": "../../dist/native-browser",
|
|
||||||
"lib": {
|
|
||||||
"entryFile": "src/public-api.ts",
|
|
||||||
"umdModuleIds": {
|
|
||||||
"@ucap/native-browser": "@ucap/native-browser",
|
|
||||||
"@ucap/ng-core": "@ucap/ng-core"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
{
|
|
||||||
"name": "@ucap/ng-native-browser",
|
|
||||||
"version": "0.0.1",
|
|
||||||
"publishConfig": {
|
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@angular/common": "^9.0.2",
|
|
||||||
"@angular/core": "^9.0.2",
|
|
||||||
"@ucap/native-browser": "~0.0.1",
|
|
||||||
"@ucap/ng-core": "~0.0.1",
|
|
||||||
"axios": "^0.19.2",
|
|
||||||
"rxjs": "~6.5.4",
|
|
||||||
"tslib": "^1.10.0"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
import { Injectable, Inject } from '@angular/core';
|
|
||||||
|
|
||||||
import { AxiosInstance } from 'axios';
|
|
||||||
|
|
||||||
import { BrowserNativeService as UcapBrowserNativeService } from '@ucap/native-browser';
|
|
||||||
|
|
||||||
import { AXIOS_INSTANCE } from '@ucap/ng-core';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class BrowserNativeService extends UcapBrowserNativeService {
|
|
||||||
constructor(@Inject(AXIOS_INSTANCE) axios: AxiosInstance) {
|
|
||||||
super(axios);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
/*
|
|
||||||
* Public API Surface of native-browser
|
|
||||||
*/
|
|
||||||
|
|
||||||
export * from './lib/services/browser-native.service';
|
|
|
@ -1,26 +0,0 @@
|
||||||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
|
||||||
|
|
||||||
import 'zone.js/dist/zone';
|
|
||||||
import 'zone.js/dist/zone-testing';
|
|
||||||
import { getTestBed } from '@angular/core/testing';
|
|
||||||
import {
|
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting
|
|
||||||
} from '@angular/platform-browser-dynamic/testing';
|
|
||||||
|
|
||||||
declare const require: {
|
|
||||||
context(path: string, deep?: boolean, filter?: RegExp): {
|
|
||||||
keys(): string[];
|
|
||||||
<T>(id: string): T;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// First, initialize the Angular testing environment.
|
|
||||||
getTestBed().initTestEnvironment(
|
|
||||||
BrowserDynamicTestingModule,
|
|
||||||
platformBrowserDynamicTesting()
|
|
||||||
);
|
|
||||||
// Then we find all the tests.
|
|
||||||
const context = require.context('./', true, /\.spec\.ts$/);
|
|
||||||
// And load the modules.
|
|
||||||
context.keys().map(context);
|
|
|
@ -1,23 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../out-tsc/lib",
|
|
||||||
"target": "es2015",
|
|
||||||
"declaration": true,
|
|
||||||
"inlineSources": true,
|
|
||||||
"types": [],
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"es2018"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"skipTemplateCodegen": true,
|
|
||||||
"strictMetadataEmit": true,
|
|
||||||
"enableResourceInlining": true
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"src/test.ts",
|
|
||||||
"**/*.spec.ts"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "./tsconfig.lib.json",
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"enableIvy": false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../out-tsc/spec",
|
|
||||||
"types": [
|
|
||||||
"jasmine",
|
|
||||||
"node"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"src/test.ts"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"**/*.spec.ts",
|
|
||||||
"**/*.d.ts"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "../../tslint.json",
|
|
||||||
"rules": {
|
|
||||||
"directive-selector": [
|
|
||||||
true,
|
|
||||||
"attribute",
|
|
||||||
"lib",
|
|
||||||
"camelCase"
|
|
||||||
],
|
|
||||||
"component-selector": [
|
|
||||||
true,
|
|
||||||
"element",
|
|
||||||
"lib",
|
|
||||||
"kebab-case"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,11 @@
|
||||||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
|
||||||
"dest": "../../dist/native",
|
"dest": "../../dist/native",
|
||||||
"lib": {
|
"lib": {
|
||||||
"entryFile": "src/public-api.ts"
|
"entryFile": "src/public-api.ts",
|
||||||
|
"umdModuleIds": {
|
||||||
|
"@ucap/core": "@ucap/core",
|
||||||
|
"@ucap/native": "@ucap/native",
|
||||||
|
"@ucap/ng-core": "@ucap/ng-core"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,13 +1,15 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-native",
|
"name": "@ucap/ng-native",
|
||||||
"version": "0.0.1",
|
"version": "0.0.5",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@angular/common": "^9.0.2",
|
"@angular/common": "^9.0.2",
|
||||||
"@angular/core": "^9.0.2",
|
"@angular/core": "^9.0.2",
|
||||||
|
"@ucap/core": "~0.0.1",
|
||||||
"@ucap/native": "~0.0.1",
|
"@ucap/native": "~0.0.1",
|
||||||
|
"axios": "^0.19.2",
|
||||||
"rxjs": "~6.5.4",
|
"rxjs": "~6.5.4",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
|
|
666
projects/native/src/lib/services/browser-native.service.ts
Normal file
666
projects/native/src/lib/services/browser-native.service.ts
Normal file
|
@ -0,0 +1,666 @@
|
||||||
|
import { Subject, BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
import { AxiosInstance } from 'axios';
|
||||||
|
|
||||||
|
import { StatusCode, FileUtil } from '@ucap/core';
|
||||||
|
import {
|
||||||
|
NativeService,
|
||||||
|
UpdateCheckConfig,
|
||||||
|
UpdateInfo,
|
||||||
|
NativeType,
|
||||||
|
WindowState,
|
||||||
|
WindowIdle,
|
||||||
|
NotificationRequest,
|
||||||
|
NotificationType
|
||||||
|
} from '@ucap/native';
|
||||||
|
|
||||||
|
import { NotificationService } from './notification.service';
|
||||||
|
|
||||||
|
export class BrowserNativeService extends NativeService {
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _notificationService: NotificationService;
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _idle_checker: WindowIdleChecker;
|
||||||
|
|
||||||
|
constructor(private axios: AxiosInstance) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._notificationService = new NotificationService();
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_nativeType(): Promise<NativeType> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(NativeType.Browser);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_networkInfo(): Promise<any> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve([{ ip: 'Browser', mac: 'browser' }]);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_execute(executableName: string): Promise<number> {
|
||||||
|
return new Promise<number>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(-1);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_openDefaultBrowser(
|
||||||
|
url: string,
|
||||||
|
options?: {
|
||||||
|
name?: string;
|
||||||
|
features?: string;
|
||||||
|
replace?: boolean;
|
||||||
|
}
|
||||||
|
): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const name = !!options && !!options.name ? options.name : null;
|
||||||
|
const features =
|
||||||
|
!!options && !!options.features ? options.features : null;
|
||||||
|
window.open(url, name, features);
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
platform_readFromClipboard(): Promise<{
|
||||||
|
text?: string;
|
||||||
|
rtf?: string;
|
||||||
|
html?: string;
|
||||||
|
image?: Buffer;
|
||||||
|
imageDataUrl?: string;
|
||||||
|
}> {
|
||||||
|
return new Promise<{
|
||||||
|
text?: string;
|
||||||
|
rtf?: string;
|
||||||
|
html?: string;
|
||||||
|
image?: Buffer;
|
||||||
|
imageDataUrl?: string;
|
||||||
|
}>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
navigator.permissions
|
||||||
|
.query({ name: 'clipboard-read' as PermissionName })
|
||||||
|
.then((result) => {
|
||||||
|
if ('granted' === result.state || 'prompt' === result.state) {
|
||||||
|
navigator.clipboard
|
||||||
|
.readText()
|
||||||
|
.then((value) => {
|
||||||
|
resolve({ text: value });
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
file_save(
|
||||||
|
buffer: Buffer,
|
||||||
|
fileName: string,
|
||||||
|
mimeType: string,
|
||||||
|
path?: string
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
FileUtil.save(buffer, fileName, mimeType)
|
||||||
|
.then((fn) => {
|
||||||
|
resolve(fn);
|
||||||
|
})
|
||||||
|
.catch((reason) => {
|
||||||
|
reject(reason);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_read(path: string): Promise<Buffer> {
|
||||||
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
this.axios
|
||||||
|
.get(path, { responseType: 'arraybuffer' })
|
||||||
|
.then((res) => {
|
||||||
|
resolve(Buffer.from(res.data));
|
||||||
|
})
|
||||||
|
.catch((reason) => reject(reason));
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_openFolder(folderPath?: string, make?: boolean): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(false);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_openItem(filePath?: string): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(false);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_path(
|
||||||
|
name: 'home' | 'documents' | 'downloads' | 'music' | 'pictures' | 'videos',
|
||||||
|
...appendPaths: string[]
|
||||||
|
): Promise<string> {
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(undefined);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_selectForOpen(option: {
|
||||||
|
title?: string;
|
||||||
|
defaultPath?: string;
|
||||||
|
filters?: {
|
||||||
|
extensions: string[];
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
properties?: Array<
|
||||||
|
| 'openFile'
|
||||||
|
| 'openDirectory'
|
||||||
|
| 'multiSelections'
|
||||||
|
| 'showHiddenFiles'
|
||||||
|
| 'createDirectory'
|
||||||
|
| 'promptToCreate'
|
||||||
|
| 'noResolveAliases'
|
||||||
|
| 'treatPackageAsDirectory'
|
||||||
|
>;
|
||||||
|
message?: string;
|
||||||
|
}): Promise<string> {
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(undefined);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
file_selectForSave(option: {
|
||||||
|
title?: string;
|
||||||
|
defaultPath?: string;
|
||||||
|
filters?: {
|
||||||
|
extensions: string[];
|
||||||
|
name: string;
|
||||||
|
}[];
|
||||||
|
message?: string;
|
||||||
|
}): Promise<{
|
||||||
|
canceled: boolean;
|
||||||
|
filePath: string;
|
||||||
|
}> {
|
||||||
|
return new Promise<{
|
||||||
|
canceled: boolean;
|
||||||
|
filePath: string;
|
||||||
|
}>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(undefined);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window_onState$(): BehaviorSubject<WindowState> {
|
||||||
|
return super.window_onState$();
|
||||||
|
}
|
||||||
|
window_onFocus$(): BehaviorSubject<boolean> {
|
||||||
|
if (!this._window_onFocusSubject) {
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
const __this = this;
|
||||||
|
const onFocus = (event: Event) => {
|
||||||
|
__this._window_onFocusSubject.next(true);
|
||||||
|
};
|
||||||
|
const onBlur = (event: Event) => {
|
||||||
|
__this._window_onFocusSubject.next(false);
|
||||||
|
};
|
||||||
|
window.addEventListener('focus', onFocus);
|
||||||
|
window.addEventListener('blur', onBlur);
|
||||||
|
|
||||||
|
super.window_onFocus$().subscribe(
|
||||||
|
(focus) => {},
|
||||||
|
(error) => {},
|
||||||
|
() => {
|
||||||
|
window.removeEventListener('focus', onFocus);
|
||||||
|
window.removeEventListener('blur', onBlur);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.window_onFocus$();
|
||||||
|
}
|
||||||
|
window_close(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window_minimize(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window_maximize(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
idle_startCheck(limitTime: number): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
if (!!this._idle_onStateSubject) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._idle_checker = new WindowIdleChecker({
|
||||||
|
limitTime,
|
||||||
|
onIdle: () => {
|
||||||
|
if (!!this._idle_onStateSubject) {
|
||||||
|
this._idle_onStateSubject.next(WindowIdle.Idle);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onActive: () => {
|
||||||
|
if (!!this._idle_onStateSubject) {
|
||||||
|
this._idle_onStateSubject.next(WindowIdle.Active);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onHide: () => {
|
||||||
|
if (!!this._idle_onStateSubject) {
|
||||||
|
this._idle_onStateSubject.next(WindowIdle.Idle);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onShow: () => {
|
||||||
|
if (!!this._idle_onStateSubject) {
|
||||||
|
this._idle_onStateSubject.next(WindowIdle.Active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._idle_checker.start();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
idle_onState$(): BehaviorSubject<WindowIdle> {
|
||||||
|
return super.idle_onState$();
|
||||||
|
}
|
||||||
|
idle_stopCheck(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
if (!!this._idle_checker) {
|
||||||
|
this._idle_checker.stop();
|
||||||
|
this._idle_checker = undefined;
|
||||||
|
}
|
||||||
|
if (!!this._idle_onStateSubject) {
|
||||||
|
this._idle_onStateSubject.complete();
|
||||||
|
this._idle_onStateSubject = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
idle_changeLimitTime(limitTime: number): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
if (!this._idle_checker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._idle_checker.changeLimitTime(limitTime);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app_version(): Promise<string> {
|
||||||
|
return new Promise<any>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve('');
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postInit(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postLogin(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
this._notificationService.requestPermission();
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postLogout(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_postDestroy(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_changeAutoLaunch(autoLaunch: boolean): Promise<boolean> {
|
||||||
|
return new Promise<boolean>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(true);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_showNotify(req: NotificationRequest): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
this._notificationService.notify(req, () => {
|
||||||
|
window.focus();
|
||||||
|
|
||||||
|
switch (req.type) {
|
||||||
|
case NotificationType.Event:
|
||||||
|
if (!!this._chat_onOpenSubject) {
|
||||||
|
this._chat_onOpenSubject.next(req.seq);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NotificationType.Message:
|
||||||
|
if (!!this._message_onOpenSubject) {
|
||||||
|
this._message_onOpenSubject.next(req.seq);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_closeAllNotify(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_checkForUpdates(currentVersion: string): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_startCheckForUpdate(config: UpdateCheckConfig): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_onUpdate$(): BehaviorSubject<UpdateInfo> {
|
||||||
|
return super.app_onUpdate$();
|
||||||
|
}
|
||||||
|
app_stopCheckForUpdate(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_applyInstantUpdates(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
app_exit(): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
window.close();
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app_onLogout$(): Subject<void> {
|
||||||
|
return super.app_onLogout$();
|
||||||
|
}
|
||||||
|
app_onStatus$(): BehaviorSubject<StatusCode> {
|
||||||
|
return super.app_onStatus$();
|
||||||
|
}
|
||||||
|
app_onShowSetting$(): Subject<void> {
|
||||||
|
return super.app_onShowSetting$();
|
||||||
|
}
|
||||||
|
|
||||||
|
chat_onOpen$(): BehaviorSubject<string> {
|
||||||
|
return super.chat_onOpen$();
|
||||||
|
}
|
||||||
|
|
||||||
|
message_onOpen$(): BehaviorSubject<string> {
|
||||||
|
return super.message_onOpen$();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WindowIdleCheckerConfig {
|
||||||
|
limitTime?: number;
|
||||||
|
events?: string[];
|
||||||
|
onIdle?: () => void;
|
||||||
|
onActive?: () => void;
|
||||||
|
visibilityEvents?: string[];
|
||||||
|
onHide?: () => void;
|
||||||
|
onShow?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultWindowIdleCheckerConfig: WindowIdleCheckerConfig = {
|
||||||
|
limitTime: 5 * 60 * 1000,
|
||||||
|
events: ['mousemove', 'keydown', 'mousedown', 'touchstart'],
|
||||||
|
onIdle: () => {},
|
||||||
|
onActive: () => {},
|
||||||
|
onHide: () => {},
|
||||||
|
onShow: () => {},
|
||||||
|
visibilityEvents: [
|
||||||
|
'visibilitychange',
|
||||||
|
'webkitvisibilitychange',
|
||||||
|
'mozvisibilitychange',
|
||||||
|
'msvisibilitychange'
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const WEBKIT_HIDDEN = 'webkitHidden';
|
||||||
|
const MOZ_HIDDEN = 'mozHidden';
|
||||||
|
const MS_HIDDEN = 'msHidden';
|
||||||
|
|
||||||
|
class WindowIdleChecker {
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _timerId: any;
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _status: WindowIdle;
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _visible: boolean;
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _started: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
// tslint:disable-next-line: variable-name
|
||||||
|
private _config: WindowIdleCheckerConfig
|
||||||
|
) {
|
||||||
|
this._config = Object.assign({}, defaultWindowIdleCheckerConfig, _config);
|
||||||
|
this._status = WindowIdle.Active;
|
||||||
|
this._visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
start() {
|
||||||
|
if (!!this._timerId) {
|
||||||
|
clearInterval(this._timerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._timerId = setInterval(() => {
|
||||||
|
if (WindowIdle.Active === this._status) {
|
||||||
|
this._status = WindowIdle.Idle;
|
||||||
|
this._config.onIdle();
|
||||||
|
}
|
||||||
|
}, this._config.limitTime);
|
||||||
|
|
||||||
|
if (!this._started) {
|
||||||
|
this._started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._addEventListerner(this._config.events, this._onActive);
|
||||||
|
|
||||||
|
if (!!this._config.onShow || !!this._config.onHide) {
|
||||||
|
this._addEventListerner(
|
||||||
|
this._config.visibilityEvents,
|
||||||
|
this._onVisibility
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (!!this._timerId) {
|
||||||
|
clearInterval(this._timerId);
|
||||||
|
this._timerId = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!!this._started) {
|
||||||
|
this._removeEventListerner(this._config.events, this._onActive);
|
||||||
|
|
||||||
|
if (!!this._config.onShow || !!this._config.onHide) {
|
||||||
|
this._removeEventListerner(
|
||||||
|
this._config.visibilityEvents,
|
||||||
|
this._onVisibility
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changeLimitTime(limitTime: number) {
|
||||||
|
this._config = {
|
||||||
|
...this._config,
|
||||||
|
limitTime
|
||||||
|
};
|
||||||
|
this.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onActive(event: Event) {
|
||||||
|
if (WindowIdle.Idle === this._status) {
|
||||||
|
this._status = WindowIdle.Active;
|
||||||
|
this._config.onActive();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onVisibility(event: Event) {
|
||||||
|
if (
|
||||||
|
!!document.hidden ||
|
||||||
|
!!document[WEBKIT_HIDDEN] ||
|
||||||
|
!!document[MOZ_HIDDEN] ||
|
||||||
|
!!document[MS_HIDDEN]
|
||||||
|
) {
|
||||||
|
if (this._visible) {
|
||||||
|
this._visible = false;
|
||||||
|
this._config.onHide();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!this._visible) {
|
||||||
|
this._visible = true;
|
||||||
|
this._config.onShow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _addEventListerner(
|
||||||
|
events: string[],
|
||||||
|
callback: (event: Event) => void
|
||||||
|
) {
|
||||||
|
events.forEach((e) => {
|
||||||
|
window.addEventListener(e, callback.bind(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeEventListerner(
|
||||||
|
events: string[],
|
||||||
|
callback: (event: Event) => void
|
||||||
|
) {
|
||||||
|
events.forEach((e) => {
|
||||||
|
window.removeEventListener(e, callback.bind(this));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
44
projects/native/src/lib/services/notification.service.ts
Normal file
44
projects/native/src/lib/services/notification.service.ts
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import { NotificationRequest } from '@ucap/native';
|
||||||
|
|
||||||
|
export class NotificationService {
|
||||||
|
notificationPermission: NotificationPermission;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.notificationPermission = this.isSupported() ? 'default' : 'denied';
|
||||||
|
}
|
||||||
|
|
||||||
|
public isSupported(): boolean {
|
||||||
|
return 'Notification' in window;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestPermission(): void {
|
||||||
|
const self = this;
|
||||||
|
if ('Notification' in window) {
|
||||||
|
Notification.requestPermission().then((result) => {
|
||||||
|
self.notificationPermission = result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notify(noti: NotificationRequest, click?: () => void) {
|
||||||
|
if (!this.isSupported()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const notification = new Notification(noti.title, {
|
||||||
|
body: noti.contents,
|
||||||
|
icon: noti.image || 'assets/images/img_nophoto_50.png'
|
||||||
|
});
|
||||||
|
notification.onclick = (e) => {
|
||||||
|
console.log('notification.onclick');
|
||||||
|
if (!!click) {
|
||||||
|
click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
notification.onclose = (e) => {
|
||||||
|
console.log('notification.onclose');
|
||||||
|
};
|
||||||
|
notification.onerror = (e) => {
|
||||||
|
console.log('notification.onerror');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,4 +2,7 @@
|
||||||
* Public API Surface of native
|
* Public API Surface of native
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from './lib/services/notification.service';
|
||||||
|
export * from './lib/services/browser-native.service';
|
||||||
|
|
||||||
export * from './lib/types/token';
|
export * from './lib/types/token';
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
"@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-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",
|
||||||
|
"@ucap/ng-store-organization": "@ucap/ng-store-organization"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-store-authentication",
|
"name": "@ucap/ng-store-authentication",
|
||||||
"version": "0.0.11",
|
"version": "0.0.14",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
@ -23,6 +23,7 @@
|
||||||
"@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",
|
"@ucap/ng-protocol-info": "~0.0.1",
|
||||||
|
"@ucap/ng-store-organization": "~0.0.1",
|
||||||
"rxjs": "~6.5.4",
|
"rxjs": "~6.5.4",
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ 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
|
||||||
|
@ -107,29 +106,3 @@ 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 }>()
|
|
||||||
);
|
|
||||||
|
|
|
@ -1,22 +1,16 @@
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { catchError, map, switchMap, exhaustMap } from 'rxjs/operators';
|
import { catchError, map, switchMap, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Store } from '@ngrx/store';
|
||||||
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
||||||
|
|
||||||
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 {
|
import { UserActions } from '@ucap/ng-store-organization';
|
||||||
logout,
|
|
||||||
logoutSuccess,
|
import { logout, logoutSuccess, loginSuccess } from './actions';
|
||||||
infoUser,
|
|
||||||
infoUserSuccess,
|
|
||||||
infoUserFailure
|
|
||||||
} from './actions';
|
|
||||||
import { UserResponse } from '@ucap/protocol-info';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Effects {
|
export class Effects {
|
||||||
|
@ -32,27 +26,37 @@ export class Effects {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
infoUser$ = createEffect(() =>
|
loginSuccessForOrganizationUserInit$ = createEffect(
|
||||||
this.actions$.pipe(
|
() =>
|
||||||
ofType(infoUser),
|
this.actions$.pipe(
|
||||||
map((action) => action.req),
|
ofType(loginSuccess),
|
||||||
exhaustMap((req) =>
|
map((params) => params.res),
|
||||||
this.infoProtocolService.user(req).pipe(
|
tap((loginRes) => {
|
||||||
map((res: UserResponse) => {
|
this.store.dispatch(
|
||||||
return infoUserSuccess({
|
UserActions.init({
|
||||||
res
|
user: {
|
||||||
});
|
info: loginRes.userInfo,
|
||||||
}),
|
companyCode: loginRes.companyCode,
|
||||||
catchError((error) => of(infoUserFailure({ error })))
|
departmentCode: loginRes.departmentCode,
|
||||||
)
|
statusMessage1: loginRes.statusMessage1,
|
||||||
)
|
statusMessage2: loginRes.statusMessage2,
|
||||||
)
|
statusMessage3: loginRes.statusMessage3,
|
||||||
|
madn: loginRes.madn,
|
||||||
|
hardPhoneSadn: loginRes.hardPhoneSadn,
|
||||||
|
fmcSadn: loginRes.fmcSadn,
|
||||||
|
pbxIndex: loginRes.pbxIndex,
|
||||||
|
talkWithMeBotSeq: loginRes.talkWithMeBotSeq
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
),
|
||||||
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private actions$: Actions,
|
private actions$: Actions,
|
||||||
private piService: PiService,
|
private store: Store<any>,
|
||||||
private authenticationProtocolService: AuthenticationProtocolService,
|
private authenticationProtocolService: AuthenticationProtocolService
|
||||||
private infoProtocolService: InfoProtocolService
|
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { createReducer, on } from '@ngrx/store';
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
|
||||||
import { initialState } from './state';
|
import { initialState } from './state';
|
||||||
import { loginSuccess, logoutSuccess, infoUserSuccess } from './actions';
|
import { loginSuccess, logoutSuccess } from './actions';
|
||||||
import { UserInfoUpdateType } from '@ucap/protocol-info';
|
|
||||||
|
|
||||||
export const reducer = createReducer(
|
export const reducer = createReducer(
|
||||||
initialState,
|
initialState,
|
||||||
|
@ -17,41 +16,5 @@ 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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,13 +8,20 @@
|
||||||
"@ngrx/store": "@ngrx/store",
|
"@ngrx/store": "@ngrx/store",
|
||||||
"@ngrx/entity": "@ngrx/entity",
|
"@ngrx/entity": "@ngrx/entity",
|
||||||
"@ngrx/effects": "@ngrx/effects",
|
"@ngrx/effects": "@ngrx/effects",
|
||||||
|
"@ucap/api": "@ucap/api",
|
||||||
"@ucap/pi": "@ucap/pi",
|
"@ucap/pi": "@ucap/pi",
|
||||||
"@ucap/protocol-event": "@ucap/protocol-event",
|
"@ucap/protocol-event": "@ucap/protocol-event",
|
||||||
"@ucap/protocol-file": "@ucap/protocol-file",
|
"@ucap/protocol-file": "@ucap/protocol-file",
|
||||||
|
"@ucap/protocol-info": "@ucap/protocol-info",
|
||||||
"@ucap/protocol-room": "@ucap/protocol-room",
|
"@ucap/protocol-room": "@ucap/protocol-room",
|
||||||
"@ucap/protocol-sync": "@ucap/protocol-sync",
|
"@ucap/protocol-sync": "@ucap/protocol-sync",
|
||||||
|
"@ucap/ng-i18n": "@ucap/ng-i18n",
|
||||||
|
"@ucap/ng-api-common": "@ucap/ng-api-common",
|
||||||
"@ucap/ng-protocol-room": "@ucap/ng-protocol-room",
|
"@ucap/ng-protocol-room": "@ucap/ng-protocol-room",
|
||||||
|
"@ucap/ng-protocol-event": "@ucap/ng-protocol-event",
|
||||||
|
"@ucap/ng-protocol-file": "@ucap/ng-protocol-file",
|
||||||
"@ucap/ng-protocol-sync": "@ucap/ng-protocol-sync",
|
"@ucap/ng-protocol-sync": "@ucap/ng-protocol-sync",
|
||||||
|
"@ucap/ng-store-organization": "@ucap/ng-store-organization",
|
||||||
"@ucap/ng-store-authentication": "@ucap/ng-store-authentication"
|
"@ucap/ng-store-authentication": "@ucap/ng-store-authentication"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-store-chat",
|
"name": "@ucap/ng-store-chat",
|
||||||
"version": "0.0.19",
|
"version": "0.0.66",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import { createAction, props } from '@ngrx/store';
|
import { createAction, props } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { DeviceType } from '@ucap/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Info,
|
Info,
|
||||||
InfoRequest as EventInfoRequest,
|
InfoRequest as EventInfoRequest,
|
||||||
|
@ -16,14 +18,17 @@ import {
|
||||||
CancelNotification,
|
CancelNotification,
|
||||||
DelNotification,
|
DelNotification,
|
||||||
EventJson,
|
EventJson,
|
||||||
ReadResponse
|
ReadResponse,
|
||||||
|
SendRequest
|
||||||
} from '@ucap/protocol-event';
|
} from '@ucap/protocol-event';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InfoRequest as FileInfoRequest,
|
InfoRequest as FileInfoRequest,
|
||||||
InfoResponse as FileInfoResponse,
|
InfoResponse as FileInfoResponse,
|
||||||
FileDownloadInfo,
|
FileDownloadInfo,
|
||||||
FileInfo
|
FileInfo,
|
||||||
|
DownCheckRequest,
|
||||||
|
DownCheckResponse
|
||||||
} from '@ucap/protocol-file';
|
} from '@ucap/protocol-file';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,6 +56,13 @@ export const eventsFailure = createAction(
|
||||||
'[ucap::chat::chatting] events Failure',
|
'[ucap::chat::chatting] events Failure',
|
||||||
props<{ roomId: string; error: any }>()
|
props<{ roomId: string; error: any }>()
|
||||||
);
|
);
|
||||||
|
/**
|
||||||
|
* retrieve list of event
|
||||||
|
*/
|
||||||
|
export const moreEvents = createAction(
|
||||||
|
'[ucap::chat::chatting] events more',
|
||||||
|
props<{ roomId: string }>()
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieve list of file information
|
* retrieve list of file information
|
||||||
|
@ -78,6 +90,19 @@ export const fileInfosFailure = createAction(
|
||||||
props<{ roomId: string; error: any }>()
|
props<{ roomId: string; error: any }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const fileDownCheck = createAction(
|
||||||
|
'[ucap::chat::chatting] fileDownCheck',
|
||||||
|
props<{ req: DownCheckRequest }>()
|
||||||
|
);
|
||||||
|
export const fileDownCheckSuccess = createAction(
|
||||||
|
'[ucap::chat::chatting] fileDownCheck Success',
|
||||||
|
props<{ res: DownCheckResponse }>()
|
||||||
|
);
|
||||||
|
export const fileDownCheckFailure = createAction(
|
||||||
|
'[ucap::chat::chatting] fileDownCheck Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add new event
|
* add new event
|
||||||
*/
|
*/
|
||||||
|
@ -143,3 +168,109 @@ export const sendNotification = createAction(
|
||||||
'[ucap::chat::chatting] Send Notification',
|
'[ucap::chat::chatting] Send Notification',
|
||||||
props<{ noti: SendNotification }>()
|
props<{ noti: SendNotification }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/** 대화 삭제 */
|
||||||
|
export const del = createAction(
|
||||||
|
'[ucap::chat::chatting] Delete',
|
||||||
|
props<DelRequest>()
|
||||||
|
);
|
||||||
|
export const delFailure = createAction(
|
||||||
|
'[ucap::chat::chatting] Delete Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
export const delNotification = createAction(
|
||||||
|
'[ucap::chat::chatting] Delete Notification || Response',
|
||||||
|
props<{ noti: DelNotification | DelResponse }>()
|
||||||
|
);
|
||||||
|
/** 대화 삭제시 열린 대화방의 대화 내용 갱신 */
|
||||||
|
export const delEventList = createAction(
|
||||||
|
'[ucap::chat::chatting] Delete InfoList',
|
||||||
|
props<{
|
||||||
|
roomId: string;
|
||||||
|
eventSeqs: number[];
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Clear event for TimerRoom */
|
||||||
|
export const intervalClearEvent = createAction(
|
||||||
|
'[ucap::chat::chatting] Clear events interval',
|
||||||
|
props<{
|
||||||
|
roomId: string;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
/** forward */
|
||||||
|
export const forward = createAction(
|
||||||
|
'[ucap::chat::chatting] Forward',
|
||||||
|
props<{
|
||||||
|
senderSeq: string;
|
||||||
|
deviceType: DeviceType;
|
||||||
|
req: SendRequest;
|
||||||
|
trgtUserSeqs?: string[];
|
||||||
|
trgtRoomId?: string;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
/** chat forward failure */
|
||||||
|
export const forwardFailure = createAction(
|
||||||
|
'[ucap::chat::chatting] Forward failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const forwardAfterRoomOpen = createAction(
|
||||||
|
'[ucap::chat::chatting] Forward after room open',
|
||||||
|
props<{
|
||||||
|
senderSeq: string;
|
||||||
|
deviceType: DeviceType;
|
||||||
|
req: SendRequest;
|
||||||
|
trgtUserSeqs?: string[];
|
||||||
|
trgtRoomId?: string;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const roomOpenAfterForward = createAction(
|
||||||
|
'[ucap::chat::chatting] Room open after forward',
|
||||||
|
props<{
|
||||||
|
senderSeq: string;
|
||||||
|
req: SendRequest;
|
||||||
|
trgtUserSeqs?: string[];
|
||||||
|
trgtRoomId?: string;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const forwarForFileEvent = createAction(
|
||||||
|
'[ucap::chat::chatting] Forward for file event',
|
||||||
|
props<{
|
||||||
|
forwardType: string;
|
||||||
|
deviceType: DeviceType;
|
||||||
|
trgtRoomId?: string;
|
||||||
|
sendReq: SendRequest;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
/** 대화 회수 */
|
||||||
|
export const cancel = createAction(
|
||||||
|
'[ucap::chat::chatting] Cancel',
|
||||||
|
props<CancelRequest>()
|
||||||
|
);
|
||||||
|
export const cancelFailure = createAction(
|
||||||
|
'[ucap::chat::chatting] Cancel Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
||||||
|
export const cancelNotification = createAction(
|
||||||
|
'[ucap::chat::chatting] Cancel Notification || Response',
|
||||||
|
props<{ noti: CancelNotification | CancelResponse }>()
|
||||||
|
);
|
||||||
|
/** 대화 회수시 열린 대화방의 대화 내용 갱신 */
|
||||||
|
export const updateEventList = createAction(
|
||||||
|
'[ucap::chat::chatting] Update InfoList',
|
||||||
|
props<{
|
||||||
|
roomId: string;
|
||||||
|
eventSeq: number;
|
||||||
|
sentMessage: string;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
export const clearActiveRoomId = createAction(
|
||||||
|
'[ucap::chat::chatting] Clear activeRoomId',
|
||||||
|
props()
|
||||||
|
);
|
||||||
|
|
|
@ -7,7 +7,9 @@ import {
|
||||||
exhaustMap,
|
exhaustMap,
|
||||||
concatMap,
|
concatMap,
|
||||||
withLatestFrom,
|
withLatestFrom,
|
||||||
debounceTime
|
debounceTime,
|
||||||
|
mergeMap,
|
||||||
|
take
|
||||||
} from 'rxjs/operators';
|
} from 'rxjs/operators';
|
||||||
|
|
||||||
import { Injectable, Inject } from '@angular/core';
|
import { Injectable, Inject } from '@angular/core';
|
||||||
|
@ -16,11 +18,46 @@ import { Store, select } from '@ngrx/store';
|
||||||
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||||
import { Dictionary } from '@ngrx/entity';
|
import { Dictionary } from '@ngrx/entity';
|
||||||
|
|
||||||
|
import {
|
||||||
|
RoomType,
|
||||||
|
OpenResponse as CreateResponse,
|
||||||
|
Open3Response as CreateTimerResponse,
|
||||||
|
ExitResponse as DeleteResponse,
|
||||||
|
ExitAllResponse as DeleteMultiResponse
|
||||||
|
} from '@ucap/protocol-room';
|
||||||
|
import {
|
||||||
|
InfoRequest,
|
||||||
|
ReadResponse,
|
||||||
|
FileType,
|
||||||
|
EventType,
|
||||||
|
DelResponse,
|
||||||
|
SendResponse,
|
||||||
|
CancelResponse,
|
||||||
|
FileEventJson,
|
||||||
|
decodeFileEventJson,
|
||||||
|
Info,
|
||||||
|
EventJson
|
||||||
|
} from '@ucap/protocol-event';
|
||||||
|
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
|
import { LocaleCode } from '@ucap/core';
|
||||||
|
import { StatusCode } from '@ucap/api';
|
||||||
|
import { FileTalkShareRequest, FileTalkShareResponse } from '@ucap/api-common';
|
||||||
|
|
||||||
|
import { I18nService } from '@ucap/ng-i18n';
|
||||||
|
import { CommonApiService } from '@ucap/ng-api-common';
|
||||||
import { EventProtocolService } from '@ucap/ng-protocol-event';
|
import { EventProtocolService } from '@ucap/ng-protocol-event';
|
||||||
import { RoomProtocolService } from '@ucap/ng-protocol-room';
|
import { RoomProtocolService } from '@ucap/ng-protocol-room';
|
||||||
import { FileProtocolService } from '@ucap/ng-protocol-file';
|
import { FileProtocolService } from '@ucap/ng-protocol-file';
|
||||||
|
import { UserSelector } from '@ucap/ng-store-organization';
|
||||||
|
import { LoginSelector } from '@ucap/ng-store-authentication';
|
||||||
|
|
||||||
|
import { ModuleConfig } from '../../config/module-config';
|
||||||
|
import { _MODULE_CONFIG } from '../../config/token';
|
||||||
|
import { ChattingSelector, RoomSelector } from '../state';
|
||||||
import * as RoomActions from '../room/actions';
|
import * as RoomActions from '../room/actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
events,
|
events,
|
||||||
eventsFailure,
|
eventsFailure,
|
||||||
|
@ -35,18 +72,26 @@ import {
|
||||||
sendSuccess,
|
sendSuccess,
|
||||||
sendFailure,
|
sendFailure,
|
||||||
addEvent,
|
addEvent,
|
||||||
addEventSuccess
|
addEventSuccess,
|
||||||
|
del,
|
||||||
|
delNotification,
|
||||||
|
delFailure,
|
||||||
|
delEventList,
|
||||||
|
moreEvents,
|
||||||
|
forward,
|
||||||
|
forwardFailure,
|
||||||
|
forwardAfterRoomOpen,
|
||||||
|
roomOpenAfterForward,
|
||||||
|
forwarForFileEvent,
|
||||||
|
cancel,
|
||||||
|
cancelFailure,
|
||||||
|
cancelNotification,
|
||||||
|
updateEventList,
|
||||||
|
intervalClearEvent,
|
||||||
|
fileDownCheck,
|
||||||
|
fileDownCheckSuccess,
|
||||||
|
fileDownCheckFailure
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
import {
|
|
||||||
InfoRequest,
|
|
||||||
ReadResponse,
|
|
||||||
FileType,
|
|
||||||
EventType
|
|
||||||
} from '@ucap/protocol-event';
|
|
||||||
|
|
||||||
import { ModuleConfig } from '../../config/module-config';
|
|
||||||
import { _MODULE_CONFIG } from '../../config/token';
|
|
||||||
import { Chatting } from './state';
|
import { Chatting } from './state';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -110,6 +155,40 @@ export class Effects {
|
||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
moreEvents$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(moreEvents),
|
||||||
|
mergeMap(
|
||||||
|
(action) =>
|
||||||
|
of(action).pipe(
|
||||||
|
withLatestFrom(
|
||||||
|
this.store.pipe(
|
||||||
|
select(ChattingSelector.eventList, action.roomId)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
(action, latestStoreData) => latestStoreData
|
||||||
|
),
|
||||||
|
tap(([req, eventList]) => {
|
||||||
|
if (!!eventList && eventList.length > 0) {
|
||||||
|
this.store.dispatch(
|
||||||
|
events({
|
||||||
|
req: {
|
||||||
|
roomId: req.roomId,
|
||||||
|
baseSeq: eventList.sort((a, b) => a.seq - b.seq)[0].seq,
|
||||||
|
requestCount:
|
||||||
|
this.moduleConfig?.eventRequestDefaultCount || 50
|
||||||
|
} as InfoRequest
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
read$ = createEffect(
|
read$ = createEffect(
|
||||||
() => {
|
() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
|
@ -117,7 +196,14 @@ export class Effects {
|
||||||
exhaustMap((req) =>
|
exhaustMap((req) =>
|
||||||
this.eventProtocolService.read(req).pipe(
|
this.eventProtocolService.read(req).pipe(
|
||||||
map((res: ReadResponse) => {
|
map((res: ReadResponse) => {
|
||||||
|
// room user lastReadEventSeq reset.
|
||||||
this.store.dispatch(readSuccess(res));
|
this.store.dispatch(readSuccess(res));
|
||||||
|
// room noReadCount reset.
|
||||||
|
this.store.dispatch(
|
||||||
|
RoomActions.updateUnreadCount({
|
||||||
|
roomId: res.roomId
|
||||||
|
})
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
catchError((error) => of(readFailure({ error })))
|
catchError((error) => of(readFailure({ error })))
|
||||||
)
|
)
|
||||||
|
@ -152,6 +238,23 @@ export class Effects {
|
||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
fileDownCheck$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(fileDownCheck),
|
||||||
|
switchMap((action) => {
|
||||||
|
return this.fileProtocolService.downCheck(action.req).pipe(
|
||||||
|
map((res) => {
|
||||||
|
this.store.dispatch(fileDownCheckSuccess({ res }));
|
||||||
|
}),
|
||||||
|
catchError((error) => of(fileDownCheckFailure({ error })))
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
addEvent$ = createEffect(
|
addEvent$ = createEffect(
|
||||||
() => {
|
() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
|
@ -234,6 +337,352 @@ export class Effects {
|
||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
del$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(del),
|
||||||
|
exhaustMap((req) =>
|
||||||
|
this.eventProtocolService.del(req).pipe(
|
||||||
|
map((res: DelResponse) => {
|
||||||
|
return delNotification({ noti: res });
|
||||||
|
}),
|
||||||
|
catchError((error) => of(delFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
delNotification$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(delNotification),
|
||||||
|
map((action) => action.noti),
|
||||||
|
withLatestFrom(this.store.pipe(select(RoomSelector.rooms))),
|
||||||
|
tap(([noti, rooms]) => {
|
||||||
|
const notiRoomId = noti.roomId;
|
||||||
|
|
||||||
|
if (!!rooms && rooms.length > 0) {
|
||||||
|
// 현재 방이 오픈되어 있으면 방내용 갱신
|
||||||
|
const idx = rooms.findIndex(
|
||||||
|
(roomInfo) => roomInfo.roomId === notiRoomId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (idx > -1) {
|
||||||
|
this.store.dispatch(
|
||||||
|
delEventList({ roomId: notiRoomId, eventSeqs: [noti.eventSeq] })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 대화 > 리스트의 항목 갱신
|
||||||
|
this.store.dispatch(
|
||||||
|
RoomActions.room({
|
||||||
|
req: {
|
||||||
|
roomId: notiRoomId,
|
||||||
|
isDetail: false,
|
||||||
|
localeCode: this.i18nService.currentLng.toUpperCase() as LocaleCode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
intervalClearEvent$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(intervalClearEvent),
|
||||||
|
withLatestFrom(
|
||||||
|
this.store.pipe(select(RoomSelector.rooms)),
|
||||||
|
this.store.pipe(select(ChattingSelector.chattings))
|
||||||
|
),
|
||||||
|
tap(([action, rooms, chattings]) => {
|
||||||
|
const roomId = action.roomId;
|
||||||
|
if (
|
||||||
|
!!rooms &&
|
||||||
|
rooms.length > 0 &&
|
||||||
|
!!chattings &&
|
||||||
|
chattings.length > 0
|
||||||
|
) {
|
||||||
|
const roomInfo = rooms.find((item) => item.roomId === roomId);
|
||||||
|
const chatting = chattings.find((item) => item.roomId === roomId);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!!roomInfo &&
|
||||||
|
!!roomInfo.timeRoomInterval &&
|
||||||
|
!!chatting &&
|
||||||
|
!!chatting.eventList
|
||||||
|
) {
|
||||||
|
const eventList = chatting.eventList;
|
||||||
|
const delEventSeq: number[] = [];
|
||||||
|
|
||||||
|
eventList.ids.forEach((id) => {
|
||||||
|
const event: Info<EventJson> = eventList.entities[id];
|
||||||
|
|
||||||
|
if (
|
||||||
|
!event ||
|
||||||
|
event.type === EventType.NotificationForTimerRoom
|
||||||
|
) {
|
||||||
|
//ignore..
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
new Date().getTime() -
|
||||||
|
moment(event.sendDate).toDate().getTime() >=
|
||||||
|
roomInfo.timeRoomInterval * 1000
|
||||||
|
) {
|
||||||
|
delEventSeq.push(event.seq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (delEventSeq.length > 0) {
|
||||||
|
this.store.dispatch(
|
||||||
|
delEventList({
|
||||||
|
roomId,
|
||||||
|
eventSeqs: delEventSeq
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
forward$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(forward),
|
||||||
|
tap((action) => {
|
||||||
|
if (!!action.trgtRoomId) {
|
||||||
|
// 대화전달 후 방오픈. Exist roomSeq.
|
||||||
|
if (action.req.eventType === EventType.File) {
|
||||||
|
// file share request action
|
||||||
|
this.store.dispatch(
|
||||||
|
forwarForFileEvent({
|
||||||
|
forwardType: 'F',
|
||||||
|
deviceType: action.deviceType,
|
||||||
|
sendReq: action.req,
|
||||||
|
trgtRoomId: action.trgtRoomId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.store.dispatch(roomOpenAfterForward(action));
|
||||||
|
}
|
||||||
|
} else if (!!action.trgtUserSeqs && action.trgtUserSeqs.length > 0) {
|
||||||
|
// 방오픈 후 대화전달.
|
||||||
|
this.store.dispatch(forwardAfterRoomOpen(action));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
forwarForFileEvent$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(forwarForFileEvent),
|
||||||
|
withLatestFrom(
|
||||||
|
this.store.pipe(select(UserSelector.user)),
|
||||||
|
this.store.pipe(select(LoginSelector.loginRes))
|
||||||
|
),
|
||||||
|
tap(([actionReq, user, loginRes]) => {
|
||||||
|
const fileEventJson: FileEventJson = decodeFileEventJson(
|
||||||
|
actionReq.sendReq.sentMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
const req: FileTalkShareRequest = {
|
||||||
|
userSeq: String(user.info.seq),
|
||||||
|
deviceType: actionReq.deviceType,
|
||||||
|
token: loginRes.tokenString,
|
||||||
|
attachmentsSeq: fileEventJson.attachmentSeq.toString(),
|
||||||
|
roomId: actionReq.trgtRoomId,
|
||||||
|
synapKey: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.commonApiService
|
||||||
|
.fileTalkShare(req)
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
map((res: FileTalkShareResponse) => {
|
||||||
|
const sedRequest = {
|
||||||
|
senderSeq: String(user.info.seq),
|
||||||
|
req: {
|
||||||
|
roomId: actionReq.trgtRoomId,
|
||||||
|
eventType: actionReq.sendReq.eventType,
|
||||||
|
sentMessage: res.returnJson
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (actionReq.forwardType === 'O') {
|
||||||
|
if (res.statusCode === StatusCode.Success) {
|
||||||
|
this.store.dispatch(send(sedRequest));
|
||||||
|
}
|
||||||
|
} else if (actionReq.forwardType === 'F') {
|
||||||
|
if (res.statusCode === StatusCode.Success) {
|
||||||
|
this.store.dispatch(
|
||||||
|
roomOpenAfterForward({
|
||||||
|
...sedRequest,
|
||||||
|
trgtRoomId: actionReq.trgtRoomId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError((error) => of(forwardFailure({ error })))
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
forwardAfterRoomOpen$ = createEffect(
|
||||||
|
() => {
|
||||||
|
let createRes: CreateResponse;
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(forwardAfterRoomOpen),
|
||||||
|
exhaustMap((actionReq) => {
|
||||||
|
return this.roomProtocolService
|
||||||
|
.open({ divCd: 'forwardOpen', userSeqs: actionReq.trgtUserSeqs })
|
||||||
|
.pipe(
|
||||||
|
map((res: CreateResponse) => {
|
||||||
|
createRes = res;
|
||||||
|
this.store.dispatch(
|
||||||
|
RoomActions.createRoomSuccess({ res, isForward: true })
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
map(() => {
|
||||||
|
if (actionReq.req.eventType === EventType.File) {
|
||||||
|
// file share request action
|
||||||
|
this.store.dispatch(
|
||||||
|
forwarForFileEvent({
|
||||||
|
forwardType: 'O',
|
||||||
|
deviceType: actionReq.deviceType,
|
||||||
|
sendReq: actionReq.req,
|
||||||
|
trgtRoomId: createRes.roomId
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.store.dispatch(
|
||||||
|
send({
|
||||||
|
senderSeq: actionReq.senderSeq,
|
||||||
|
req: {
|
||||||
|
roomId: createRes.roomId,
|
||||||
|
eventType: actionReq.req.eventType,
|
||||||
|
sentMessage: actionReq.req.sentMessage
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
catchError((error) =>
|
||||||
|
of(RoomActions.createRoomFailure({ error }))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
roomOpenAfterForward$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(roomOpenAfterForward),
|
||||||
|
exhaustMap((action) => {
|
||||||
|
return this.eventProtocolService
|
||||||
|
.send({
|
||||||
|
roomId: action.trgtRoomId,
|
||||||
|
eventType: action.req.eventType,
|
||||||
|
sentMessage: action.req.sentMessage
|
||||||
|
})
|
||||||
|
.pipe(
|
||||||
|
map((res: SendResponse) => {
|
||||||
|
this.store.dispatch(
|
||||||
|
addEvent({
|
||||||
|
roomId: res.roomId,
|
||||||
|
info: res.info,
|
||||||
|
SVC_TYPE: res.SVC_TYPE,
|
||||||
|
SSVC_TYPE: res.SSVC_TYPE
|
||||||
|
})
|
||||||
|
);
|
||||||
|
this.store.dispatch(
|
||||||
|
RoomActions.selectedRoom({
|
||||||
|
roomId: res.roomId,
|
||||||
|
localeCode: this.i18nService.currentLng.toUpperCase() as LocaleCode
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
catchError((error) =>
|
||||||
|
of(RoomActions.createRoomFailure({ error }))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
cancel$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(cancel),
|
||||||
|
exhaustMap((req) =>
|
||||||
|
this.eventProtocolService.cancel(req).pipe(
|
||||||
|
map((res: CancelResponse) => {
|
||||||
|
return cancelNotification({ noti: res });
|
||||||
|
}),
|
||||||
|
catchError((error) => of(delFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
cancelNotification$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(cancelNotification),
|
||||||
|
withLatestFrom(this.store.pipe(select(RoomSelector.rooms))),
|
||||||
|
tap(([action, rooms]) => {
|
||||||
|
const notiRoomId = action.noti.roomId;
|
||||||
|
|
||||||
|
if (!!rooms && rooms.length > 0) {
|
||||||
|
// 현재 방이 오픈되어 있으면 방내용 갱신
|
||||||
|
const idx = rooms.findIndex(
|
||||||
|
(roomInfo) => roomInfo.roomId === notiRoomId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (idx > -1) {
|
||||||
|
this.store.dispatch(
|
||||||
|
updateEventList({
|
||||||
|
roomId: notiRoomId,
|
||||||
|
eventSeq: action.noti.eventSeq,
|
||||||
|
sentMessage: this.i18nService.t('event.recalled')
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 대화 > 리스트의 항목 갱신
|
||||||
|
this.store.dispatch(
|
||||||
|
RoomActions.room({
|
||||||
|
req: {
|
||||||
|
roomId: action.noti.roomId,
|
||||||
|
isDetail: false,
|
||||||
|
localeCode: this.i18nService.currentLng.toUpperCase() as LocaleCode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
/*******************************************************************
|
/*******************************************************************
|
||||||
* [Room Action watching.]
|
* [Room Action watching.]
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
|
@ -263,6 +712,10 @@ export class Effects {
|
||||||
@Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig,
|
@Inject(_MODULE_CONFIG) private moduleConfig: ModuleConfig,
|
||||||
private roomProtocolService: RoomProtocolService,
|
private roomProtocolService: RoomProtocolService,
|
||||||
private eventProtocolService: EventProtocolService,
|
private eventProtocolService: EventProtocolService,
|
||||||
private fileProtocolService: FileProtocolService
|
private fileProtocolService: FileProtocolService,
|
||||||
) {}
|
private i18nService: I18nService,
|
||||||
|
private commonApiService: CommonApiService
|
||||||
|
) {
|
||||||
|
this.i18nService.setDefaultNamespace('chat');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
import { createReducer, on } from '@ngrx/store';
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { FileInfo } from '@ucap/protocol-file';
|
||||||
|
import { Info, EventJson, EventType } from '@ucap/protocol-event';
|
||||||
|
|
||||||
|
import * as RoomActions from '../room/actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
initialState,
|
initialState,
|
||||||
adapterChatting,
|
adapterChatting,
|
||||||
adapterEventList,
|
adapterEventList,
|
||||||
Chatting,
|
Chatting,
|
||||||
adapterFileInfoList,
|
adapterFileInfoList
|
||||||
adapterFileInfoCheckList
|
// adapterFileInfoCheckList
|
||||||
} from './state';
|
} from './state';
|
||||||
|
|
||||||
import * as RoomActions from '../room/actions';
|
|
||||||
import {
|
import {
|
||||||
eventsSuccess,
|
eventsSuccess,
|
||||||
eventsFailure,
|
eventsFailure,
|
||||||
fileInfosSuccess,
|
fileInfosSuccess,
|
||||||
fileInfosFailure,
|
fileInfosFailure,
|
||||||
addEvent
|
addEvent,
|
||||||
|
delEventList,
|
||||||
|
updateEventList,
|
||||||
|
clearActiveRoomId
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
export const reducer = createReducer(
|
export const reducer = createReducer(
|
||||||
|
@ -31,22 +38,53 @@ export const reducer = createReducer(
|
||||||
eventListProcessing: false,
|
eventListProcessing: false,
|
||||||
eventList: adapterEventList.getInitialState(),
|
eventList: adapterEventList.getInitialState(),
|
||||||
eventStatus: null,
|
eventStatus: null,
|
||||||
remainEvent: false,
|
remainEvent: null,
|
||||||
|
|
||||||
fileInfoListProcessing: false,
|
fileInfoListProcessing: false,
|
||||||
fileInfoList: adapterFileInfoList.getInitialState(),
|
fileInfoList: adapterFileInfoList.getInitialState(),
|
||||||
fileInfoCheckList: adapterFileInfoCheckList.getInitialState(),
|
// fileInfoCheckList: adapterFileInfoCheckList.getInitialState(),
|
||||||
|
fileInfoCheckList: [],
|
||||||
fileInfoSyncDate: '',
|
fileInfoSyncDate: '',
|
||||||
...chatting
|
...chatting
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// dupliaction event process
|
||||||
|
const trgtEventInfoList: Info<EventJson>[] = [];
|
||||||
|
if (!!chatting && !!chatting.eventList && !!chatting.eventList.entities) {
|
||||||
|
const filteredList = action.eventInfoList.filter((item) => {
|
||||||
|
let notExistOrDiff = true; // added target flag.
|
||||||
|
|
||||||
|
// tslint:disable-next-line: forin
|
||||||
|
for (const key in chatting.eventList.entities) {
|
||||||
|
const event = chatting.eventList.entities[key];
|
||||||
|
if (
|
||||||
|
item.seq === event.seq &&
|
||||||
|
item.type === event.type &&
|
||||||
|
item.sentMessage === event.sentMessage
|
||||||
|
) {
|
||||||
|
notExistOrDiff = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return notExistOrDiff;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!!filteredList && filteredList.length > 0) {
|
||||||
|
trgtEventInfoList.push(...filteredList);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trgtEventInfoList.push(...action.eventInfoList);
|
||||||
|
}
|
||||||
|
|
||||||
trgtChatting = {
|
trgtChatting = {
|
||||||
...trgtChatting,
|
...trgtChatting,
|
||||||
eventList: adapterEventList.upsertMany(action.eventInfoList, {
|
eventList: adapterEventList.upsertMany(trgtEventInfoList, {
|
||||||
...trgtChatting.eventList
|
...trgtChatting.eventList
|
||||||
}),
|
}),
|
||||||
eventStatus: action.res,
|
eventStatus: action.res,
|
||||||
remainEvent: action.remainEvent,
|
remainEvent:
|
||||||
|
trgtChatting.remainEvent === false ? false : action.remainEvent, // 재조회를 위한 처리.
|
||||||
eventListProcessing: false
|
eventListProcessing: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -92,11 +130,12 @@ export const reducer = createReducer(
|
||||||
eventListProcessing: false,
|
eventListProcessing: false,
|
||||||
eventList: adapterEventList.getInitialState(),
|
eventList: adapterEventList.getInitialState(),
|
||||||
eventStatus: null,
|
eventStatus: null,
|
||||||
remainEvent: false,
|
remainEvent: null,
|
||||||
|
|
||||||
fileInfoListProcessing: false,
|
fileInfoListProcessing: false,
|
||||||
fileInfoList: adapterFileInfoList.getInitialState(),
|
fileInfoList: adapterFileInfoList.getInitialState(),
|
||||||
fileInfoCheckList: adapterFileInfoCheckList.getInitialState(),
|
// fileInfoCheckList: adapterFileInfoCheckList.getInitialState(),
|
||||||
|
fileInfoCheckList: [],
|
||||||
fileInfoSyncDate: '',
|
fileInfoSyncDate: '',
|
||||||
...chatting
|
...chatting
|
||||||
};
|
};
|
||||||
|
@ -111,11 +150,12 @@ export const reducer = createReducer(
|
||||||
...trgtChatting.fileInfoList
|
...trgtChatting.fileInfoList
|
||||||
})
|
})
|
||||||
: trgtChatting.fileInfoList,
|
: trgtChatting.fileInfoList,
|
||||||
fileInfoCheckList: !!fileInfoCheckList
|
// fileInfoCheckList: !!fileInfoCheckList
|
||||||
? adapterFileInfoCheckList.upsertMany(fileInfoCheckList, {
|
// ? adapterFileInfoCheckList.upsertMany(fileInfoCheckList, {
|
||||||
...trgtChatting.fileInfoCheckList
|
// ...trgtChatting.fileInfoCheckList
|
||||||
})
|
// })
|
||||||
: trgtChatting.fileInfoCheckList,
|
// : trgtChatting.fileInfoCheckList,
|
||||||
|
fileInfoCheckList,
|
||||||
fileInfoListProcessing: false
|
fileInfoListProcessing: false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -171,6 +211,117 @@ export const reducer = createReducer(
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
on(delEventList, (state, action) => {
|
||||||
|
const roomId = action.roomId;
|
||||||
|
const chatting = state.chattings.entities[roomId];
|
||||||
|
|
||||||
|
// checked Valid array
|
||||||
|
if (
|
||||||
|
!chatting ||
|
||||||
|
!chatting.eventList ||
|
||||||
|
chatting.eventList.ids.length === 0
|
||||||
|
) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileInfoList = chatting.fileInfoList;
|
||||||
|
const trgtDelFileInfoSeqs = [];
|
||||||
|
if (!!fileInfoList) {
|
||||||
|
fileInfoList.ids.forEach((id) => {
|
||||||
|
const fileInfo: FileInfo = fileInfoList.entities[id];
|
||||||
|
if (action.eventSeqs.indexOf(fileInfo.eventSeq) > -1) {
|
||||||
|
trgtDelFileInfoSeqs.push(id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
chattings: adapterChatting.upsertOne(
|
||||||
|
{
|
||||||
|
...chatting,
|
||||||
|
eventList: adapterEventList.removeMany(action.eventSeqs, {
|
||||||
|
...chatting.eventList
|
||||||
|
}),
|
||||||
|
fileInfoList:
|
||||||
|
trgtDelFileInfoSeqs.length > 0
|
||||||
|
? adapterFileInfoList.removeMany(trgtDelFileInfoSeqs, {
|
||||||
|
...chatting.fileInfoList
|
||||||
|
})
|
||||||
|
: chatting.fileInfoList
|
||||||
|
},
|
||||||
|
{ ...state.chattings }
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(updateEventList, (state, action) => {
|
||||||
|
const roomId = action.roomId;
|
||||||
|
const eventSeq = action.eventSeq;
|
||||||
|
const sentMessage = action.sentMessage;
|
||||||
|
const chatting = state.chattings.entities[roomId];
|
||||||
|
let fileInfoSeq;
|
||||||
|
|
||||||
|
// checked Valid array
|
||||||
|
if (
|
||||||
|
!chatting ||
|
||||||
|
!chatting.eventList ||
|
||||||
|
chatting.eventList.ids.length === 0
|
||||||
|
) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusEventInfo: Info<EventJson> = {
|
||||||
|
...chatting.eventList[eventSeq],
|
||||||
|
type: EventType.RecalledMessage,
|
||||||
|
sentMessage
|
||||||
|
};
|
||||||
|
|
||||||
|
// 파일이 회수되었을 경우 fileInfoList 에서도 삭제 한다.
|
||||||
|
if (chatting.eventList.entities[eventSeq].type === EventType.File) {
|
||||||
|
const fileInfoList = chatting.fileInfoList;
|
||||||
|
if (!!chatting && !!fileInfoList) {
|
||||||
|
fileInfoList.ids.forEach((id) => {
|
||||||
|
const fileInfo: FileInfo = fileInfoList.entities[id];
|
||||||
|
if (action.eventSeq === fileInfo.eventSeq) {
|
||||||
|
fileInfoSeq = id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
chattings: adapterChatting.upsertOne(
|
||||||
|
{
|
||||||
|
...chatting,
|
||||||
|
eventList: adapterEventList.updateOne(
|
||||||
|
{
|
||||||
|
id: eventSeq,
|
||||||
|
changes: statusEventInfo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...chatting.eventList
|
||||||
|
}
|
||||||
|
),
|
||||||
|
fileInfoList: !!fileInfoSeq
|
||||||
|
? adapterFileInfoList.removeOne(fileInfoSeq, {
|
||||||
|
...chatting.fileInfoList
|
||||||
|
})
|
||||||
|
: chatting.fileInfoList
|
||||||
|
},
|
||||||
|
{ ...state.chattings }
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(clearActiveRoomId, (state, action) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeRoomId: null
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
/*******************************************************************
|
/*******************************************************************
|
||||||
* [Room Action watching.]
|
* [Room Action watching.]
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
|
@ -197,7 +348,9 @@ export const reducer = createReducer(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
chattings: adapterChatting.removeMany(roomIds, { ...state.chattings })
|
chattings: adapterChatting.removeMany(roomIds, {
|
||||||
|
...state.chattings
|
||||||
|
})
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,12 +2,11 @@ import { Selector, createSelector } from '@ngrx/store';
|
||||||
import { EntityState, createEntityAdapter, Dictionary } from '@ngrx/entity';
|
import { EntityState, createEntityAdapter, Dictionary } from '@ngrx/entity';
|
||||||
|
|
||||||
import { InfoResponse, Info, EventJson } from '@ucap/protocol-event';
|
import { InfoResponse, Info, EventJson } from '@ucap/protocol-event';
|
||||||
|
|
||||||
import { FileInfo, FileDownloadInfo } from '@ucap/protocol-file';
|
import { FileInfo, FileDownloadInfo } from '@ucap/protocol-file';
|
||||||
|
|
||||||
export interface EventListState extends EntityState<Info<EventJson>> {}
|
export interface EventListState extends EntityState<Info<EventJson>> {}
|
||||||
export interface FileInfoListState extends EntityState<FileInfo> {}
|
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,
|
||||||
|
@ -21,12 +20,12 @@ export const adapterFileInfoList = createEntityAdapter<FileInfo>({
|
||||||
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;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
const eventListInitialState: EventListState = adapterEventList.getInitialState(
|
const eventListInitialState: EventListState = adapterEventList.getInitialState(
|
||||||
{}
|
{}
|
||||||
|
@ -34,9 +33,9 @@ const eventListInitialState: EventListState = adapterEventList.getInitialState(
|
||||||
const fileInfoListInitialState: FileInfoListState = adapterFileInfoList.getInitialState(
|
const fileInfoListInitialState: FileInfoListState = adapterFileInfoList.getInitialState(
|
||||||
{}
|
{}
|
||||||
);
|
);
|
||||||
const fileInfoCheckListInitialState: FileInfoCheckListState = adapterFileInfoCheckList.getInitialState(
|
// const fileInfoCheckListInitialState: FileInfoCheckListState = adapterFileInfoCheckList.getInitialState(
|
||||||
{}
|
// {}
|
||||||
);
|
// );
|
||||||
|
|
||||||
const {
|
const {
|
||||||
selectAll: selectAllForEventList,
|
selectAll: selectAllForEventList,
|
||||||
|
@ -52,12 +51,12 @@ const {
|
||||||
selectTotal: selectTotalForFileInfoList
|
selectTotal: selectTotalForFileInfoList
|
||||||
} = adapterFileInfoList.getSelectors();
|
} = adapterFileInfoList.getSelectors();
|
||||||
|
|
||||||
const {
|
// const {
|
||||||
selectAll: selectAllForFileInfoCheckList,
|
// selectAll: selectAllForFileInfoCheckList,
|
||||||
selectEntities: selectEntitiesForFileInfoCheckList,
|
// selectEntities: selectEntitiesForFileInfoCheckList,
|
||||||
selectIds: selectIdsForFileInfoCheckList,
|
// selectIds: selectIdsForFileInfoCheckList,
|
||||||
selectTotal: selectTotalForFileInfoCheckList
|
// selectTotal: selectTotalForFileInfoCheckList
|
||||||
} = adapterFileInfoCheckList.getSelectors();
|
// } = adapterFileInfoCheckList.getSelectors();
|
||||||
|
|
||||||
export interface Chatting {
|
export interface Chatting {
|
||||||
roomId?: string;
|
roomId?: string;
|
||||||
|
@ -65,11 +64,12 @@ export interface Chatting {
|
||||||
eventListProcessing?: boolean;
|
eventListProcessing?: boolean;
|
||||||
eventList?: EventListState;
|
eventList?: EventListState;
|
||||||
eventStatus?: InfoResponse | null;
|
eventStatus?: InfoResponse | null;
|
||||||
remainEvent?: boolean;
|
remainEvent?: boolean | null;
|
||||||
|
|
||||||
fileInfoListProcessing?: boolean;
|
fileInfoListProcessing?: boolean;
|
||||||
fileInfoList?: FileInfoListState;
|
fileInfoList?: FileInfoListState;
|
||||||
fileInfoCheckList?: FileInfoCheckListState;
|
// fileInfoCheckList?: FileInfoCheckListState;
|
||||||
|
fileInfoCheckList?: FileDownloadInfo[];
|
||||||
fileInfoSyncDate?: string;
|
fileInfoSyncDate?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,12 +139,37 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
(state) => state.fileInfoListProcessing
|
(state) => state.fileInfoListProcessing
|
||||||
);
|
);
|
||||||
const selectChattingFileInfoList = createSelector(
|
const selectChattingFileInfoList = createSelector(
|
||||||
selectChatting,
|
selectChattings,
|
||||||
(state) => state.fileInfoList
|
(state: ChattingState, roomId: string) => {
|
||||||
|
const chatting = state.entities && state.entities[roomId];
|
||||||
|
if (!!chatting) {
|
||||||
|
return chatting?.fileInfoList;
|
||||||
|
} else {
|
||||||
|
return adapterFileInfoList.getInitialState();
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
// const selectChattingFileInfoCheckList = createSelector(
|
||||||
|
// selectChattings,
|
||||||
|
// (state: ChattingState, roomId: string) => {
|
||||||
|
// const chatting = state.entities && state.entities[roomId];
|
||||||
|
// if (!!chatting) {
|
||||||
|
// return chatting?.fileInfoCheckList;
|
||||||
|
// } else {
|
||||||
|
// return adapterFileInfoCheckList.getInitialState();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// );
|
||||||
const selectChattingFileInfoCheckList = createSelector(
|
const selectChattingFileInfoCheckList = createSelector(
|
||||||
selectChatting,
|
selectChattings,
|
||||||
(state) => state.fileInfoCheckList
|
(state: ChattingState, roomId: string) => {
|
||||||
|
const chatting = state.entities && state.entities[roomId];
|
||||||
|
if (!!chatting) {
|
||||||
|
return chatting?.fileInfoCheckList;
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
const selectChattingFileInfoSyncDate = createSelector(
|
const selectChattingFileInfoSyncDate = createSelector(
|
||||||
selectChatting,
|
selectChatting,
|
||||||
|
@ -167,10 +192,11 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
selectChattingFileInfoList,
|
selectChattingFileInfoList,
|
||||||
selectAllForFileInfoList
|
selectAllForFileInfoList
|
||||||
),
|
),
|
||||||
fileInfoCheckList: createSelector(
|
// fileInfoCheckList: createSelector(
|
||||||
selectChattingFileInfoCheckList,
|
// selectChattingFileInfoCheckList,
|
||||||
selectAllForFileInfoCheckList
|
// selectAllForFileInfoCheckList
|
||||||
),
|
// ),
|
||||||
|
fileInfoCheckList: selectChattingFileInfoCheckList,
|
||||||
fileInfoSyncDate: selectChattingFileInfoSyncDate
|
fileInfoSyncDate: selectChattingFileInfoSyncDate
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,21 +160,6 @@ export const create = createAction(
|
||||||
'[ucap::chat::room] create',
|
'[ucap::chat::room] create',
|
||||||
props<{ req: CreateRequest }>()
|
props<{ req: CreateRequest }>()
|
||||||
);
|
);
|
||||||
/**
|
|
||||||
* Success of create request
|
|
||||||
*/
|
|
||||||
export const createSuccess = createAction(
|
|
||||||
'[ucap::chat::room] create Success',
|
|
||||||
props<{ res: CreateResponse }>()
|
|
||||||
);
|
|
||||||
/**
|
|
||||||
* Failure of create request
|
|
||||||
*/
|
|
||||||
export const createFailure = createAction(
|
|
||||||
'[ucap::chat::room] create Failure',
|
|
||||||
props<{ error: any }>()
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create timer room
|
* create timer room
|
||||||
*/
|
*/
|
||||||
|
@ -183,17 +168,17 @@ export const createTimer = createAction(
|
||||||
props<{ req: CreateTimerRequest }>()
|
props<{ req: CreateTimerRequest }>()
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
* Success of openTimer request
|
* Success of create room / timer room request
|
||||||
*/
|
*/
|
||||||
export const createTimerSuccess = createAction(
|
export const createRoomSuccess = createAction(
|
||||||
'[ucap::chat::room] createTimer Success',
|
'[ucap::chat::room] create room Success',
|
||||||
props<{ res: CreateTimerResponse }>()
|
props<{ res: CreateResponse; isForward?: boolean }>()
|
||||||
);
|
);
|
||||||
/**
|
/**
|
||||||
* Failure of createTimer request
|
* Failure of create room / timer room request
|
||||||
*/
|
*/
|
||||||
export const createTimerFailure = createAction(
|
export const createRoomFailure = createAction(
|
||||||
'[ucap::chat::room] createTimer Failure',
|
'[ucap::chat::room] create room Failure',
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -264,6 +249,15 @@ export const updateFailure = createAction(
|
||||||
'[ucap::chat::room] update Failure',
|
'[ucap::chat::room] update Failure',
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
/**
|
||||||
|
* Success of update RoomName
|
||||||
|
*/
|
||||||
|
export const updateRoomName = createAction(
|
||||||
|
'[ucap::chat::room] update Roomname for Notification',
|
||||||
|
props<{
|
||||||
|
res: UpdateResponse;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update isJoinRoom of user information from true to false
|
* update isJoinRoom of user information from true to false
|
||||||
|
@ -331,18 +325,6 @@ export const closeFailure = createAction(
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
|
||||||
* Invite conversation partner to room or create room
|
|
||||||
*/
|
|
||||||
export const inviteOrCreate = createAction(
|
|
||||||
'[ucap::chat::room] inviteOrCreate',
|
|
||||||
props<{
|
|
||||||
roomInfo: RoomInfo;
|
|
||||||
localeCode: LocaleCode;
|
|
||||||
req: CreateRequest;
|
|
||||||
}>()
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invite conversation partner to room
|
* Invite conversation partner to room
|
||||||
*/
|
*/
|
||||||
|
@ -386,6 +368,13 @@ export const expelFailure = createAction(
|
||||||
'[ucap::chat::room] expel Failure',
|
'[ucap::chat::room] expel Failure',
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
/**
|
||||||
|
* expel notification
|
||||||
|
*/
|
||||||
|
export const expelNotification = createAction(
|
||||||
|
'[ucap::chat::room] expel Notification',
|
||||||
|
props<{ res: ExitForcingResponse }>()
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update interval of timer room
|
* update interval of timer room
|
||||||
|
@ -422,10 +411,24 @@ export const inviteNotification = createAction(
|
||||||
*/
|
*/
|
||||||
export const exitNotification = createAction(
|
export const exitNotification = createAction(
|
||||||
'[ucap::chat::room] Exit Notification',
|
'[ucap::chat::room] Exit Notification',
|
||||||
props<{ roomId: string; userSeq: string; senderSeq: string }>()
|
props<{ roomId: string; senderSeq: string }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const updateUnreadCount = createAction(
|
export const updateUnreadCount = createAction(
|
||||||
'[ucap::chat::room] Update unread count',
|
'[ucap::chat::room] Update unread count',
|
||||||
props<{ roomId: string; noReadCnt?: number }>()
|
props<{ roomId: string; noReadCnt?: number }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* room user info update by Notification
|
||||||
|
*/
|
||||||
|
// export const userInfoListUpdate = createAction(
|
||||||
|
// '[ucap::chat::room] user info update',
|
||||||
|
// props<{
|
||||||
|
// roomInfo: RoomInfo;
|
||||||
|
// roomUserInfo: {
|
||||||
|
// userInfoShortList: UserInfoShort[];
|
||||||
|
// userInfoList: RoomUserInfo[];
|
||||||
|
// };
|
||||||
|
// }>()
|
||||||
|
// );
|
||||||
|
|
|
@ -14,8 +14,8 @@ import { Injectable } from '@angular/core';
|
||||||
import { Store, select } from '@ngrx/store';
|
import { Store, select } from '@ngrx/store';
|
||||||
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
||||||
|
|
||||||
|
import { LocaleCode } from '@ucap/core';
|
||||||
import {
|
import {
|
||||||
RoomType,
|
|
||||||
OpenResponse as CreateResponse,
|
OpenResponse as CreateResponse,
|
||||||
Open3Response as CreateTimerResponse,
|
Open3Response as CreateTimerResponse,
|
||||||
ExitResponse as DeleteResponse,
|
ExitResponse as DeleteResponse,
|
||||||
|
@ -24,22 +24,30 @@ import {
|
||||||
InviteResponse,
|
InviteResponse,
|
||||||
ExitForcingResponse,
|
ExitForcingResponse,
|
||||||
UpdateTimerSetResponse,
|
UpdateTimerSetResponse,
|
||||||
InfoRequest
|
InfoRequest,
|
||||||
|
UserInfoShort,
|
||||||
|
UserInfo,
|
||||||
|
RoomInfo
|
||||||
} from '@ucap/protocol-room';
|
} from '@ucap/protocol-room';
|
||||||
|
import { UserNotification, UserInfoUpdateType } from '@ucap/protocol-info';
|
||||||
|
|
||||||
|
import { I18nService } from '@ucap/ng-i18n';
|
||||||
import { RoomProtocolService } from '@ucap/ng-protocol-room';
|
import { RoomProtocolService } from '@ucap/ng-protocol-room';
|
||||||
import { SyncProtocolService } from '@ucap/ng-protocol-sync';
|
import { SyncProtocolService } from '@ucap/ng-protocol-sync';
|
||||||
|
|
||||||
|
import {
|
||||||
|
PresenceActions,
|
||||||
|
CommonActions,
|
||||||
|
UserSelector
|
||||||
|
} from '@ucap/ng-store-organization';
|
||||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
import { LoginActions } from '@ucap/ng-store-authentication';
|
||||||
import * as ChattingAction from '../Chatting/actions';
|
|
||||||
import { RoomSelector } from '../state';
|
import * as ChattingAction from '../chatting/actions';
|
||||||
|
import { RoomSelector, ChattingSelector } from '../state';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
rooms,
|
rooms,
|
||||||
roomsFailure,
|
|
||||||
roomsSuccess,
|
|
||||||
room,
|
room,
|
||||||
roomSuccess,
|
|
||||||
roomFailure,
|
roomFailure,
|
||||||
inviteNotification,
|
inviteNotification,
|
||||||
exitNotification,
|
exitNotification,
|
||||||
|
@ -48,11 +56,9 @@ import {
|
||||||
close,
|
close,
|
||||||
delSuccess,
|
delSuccess,
|
||||||
create,
|
create,
|
||||||
createSuccess,
|
|
||||||
createFailure,
|
|
||||||
createTimer,
|
createTimer,
|
||||||
createTimerSuccess,
|
createRoomSuccess,
|
||||||
createTimerFailure,
|
createRoomFailure,
|
||||||
del,
|
del,
|
||||||
delFailure,
|
delFailure,
|
||||||
update,
|
update,
|
||||||
|
@ -61,7 +67,6 @@ import {
|
||||||
open,
|
open,
|
||||||
openSuccess,
|
openSuccess,
|
||||||
closeSuccess,
|
closeSuccess,
|
||||||
inviteOrCreate,
|
|
||||||
invite,
|
invite,
|
||||||
inviteSuccess,
|
inviteSuccess,
|
||||||
inviteFailure,
|
inviteFailure,
|
||||||
|
@ -79,10 +84,9 @@ import {
|
||||||
selectedRoom,
|
selectedRoom,
|
||||||
room2Success,
|
room2Success,
|
||||||
selectedRoomSuccess,
|
selectedRoomSuccess,
|
||||||
clearSelectedRoom
|
clearSelectedRoom,
|
||||||
|
expelNotification
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { LocaleCode } from '@ucap/core';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Effects {
|
export class Effects {
|
||||||
|
@ -121,14 +125,25 @@ export class Effects {
|
||||||
roomInfo2Res: res
|
roomInfo2Res: res
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Buddy Presence
|
||||||
|
const targetUserInfos = req.isDetail
|
||||||
|
? res.roomUserInfo.userInfoList.map(
|
||||||
|
(userInfo) => userInfo.seq + ''
|
||||||
|
)
|
||||||
|
: res.roomUserInfo.userInfoShortList.map(
|
||||||
|
(userInfo) => userInfo.seq + ''
|
||||||
|
);
|
||||||
|
if (!!targetUserInfos && targetUserInfos.length > 0) {
|
||||||
|
this.store.dispatch(
|
||||||
|
PresenceActions.bulkInfo({
|
||||||
|
divCd: 'roomBulk',
|
||||||
|
userSeqs: targetUserInfos
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// is not join room. so, redirect chat main.
|
// is not join room. so, redirect chat main.
|
||||||
this.router.navigate([
|
|
||||||
'chat',
|
|
||||||
{
|
|
||||||
outlets: { content: 'index' }
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
clearSelectedRoom({ roomId: req.roomId })
|
clearSelectedRoom({ roomId: req.roomId })
|
||||||
);
|
);
|
||||||
|
@ -237,27 +252,9 @@ export class Effects {
|
||||||
exhaustMap((req) => {
|
exhaustMap((req) => {
|
||||||
return this.roomProtocolService.open(req).pipe(
|
return this.roomProtocolService.open(req).pipe(
|
||||||
map((res: CreateResponse) => {
|
map((res: CreateResponse) => {
|
||||||
console.log('CreateResponse', res);
|
this.store.dispatch(createRoomSuccess({ res }));
|
||||||
|
|
||||||
this.store.dispatch(createSuccess({ res }));
|
|
||||||
|
|
||||||
this.router.navigate(
|
|
||||||
[
|
|
||||||
'chat',
|
|
||||||
{
|
|
||||||
outlets: { content: 'chatroom' }
|
|
||||||
}
|
|
||||||
],
|
|
||||||
{
|
|
||||||
queryParams: { roomId: res.roomId }
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// if (!res.newRoom) {
|
|
||||||
// } else {
|
|
||||||
// }
|
|
||||||
}),
|
}),
|
||||||
catchError((error) => of(createFailure({ error })))
|
catchError((error) => of(createRoomFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -265,33 +262,53 @@ export class Effects {
|
||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
createTimer$ = createEffect(() =>
|
createTimer$ = createEffect(
|
||||||
this.actions$.pipe(
|
() => {
|
||||||
ofType(createTimer),
|
return this.actions$.pipe(
|
||||||
map((action) => action.req),
|
ofType(createTimer),
|
||||||
exhaustMap((req) => {
|
map((action) => action.req),
|
||||||
return this.roomProtocolService.open3(req).pipe(
|
exhaustMap((req) => {
|
||||||
map((res: CreateTimerResponse) => {
|
return this.roomProtocolService.open3(req).pipe(
|
||||||
return createTimerSuccess({ res });
|
map((res: CreateTimerResponse) => {
|
||||||
}),
|
// return createTimerSuccess({ res });
|
||||||
catchError((error) => of(createTimerFailure({ error })))
|
this.store.dispatch(createRoomSuccess({ res }));
|
||||||
);
|
}),
|
||||||
})
|
catchError((error) => of(createRoomFailure({ error })))
|
||||||
)
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
del$ = createEffect(() =>
|
del$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(del),
|
ofType(del),
|
||||||
map((action) => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap((req) => {
|
withLatestFrom(this.store.pipe(select(ChattingSelector.activeRoomId))),
|
||||||
|
exhaustMap(([req, activeRoomId]) => {
|
||||||
|
const existActiveRoomId = req.roomId === activeRoomId;
|
||||||
|
|
||||||
return this.roomProtocolService.exit(req).pipe(
|
return this.roomProtocolService.exit(req).pipe(
|
||||||
switchMap((res: DeleteResponse) => [
|
switchMap((res: DeleteResponse) => {
|
||||||
// close room, clear chatting
|
if (!!existActiveRoomId) {
|
||||||
close({ roomIds: [res.roomId] }),
|
return [
|
||||||
// clear room in rooms.
|
// clear activeRoomId
|
||||||
delSuccess({ res })
|
clearSelectedRoom({ roomId: res.roomId }),
|
||||||
]),
|
// close room, clear chatting
|
||||||
|
close({ roomIds: [res.roomId] }),
|
||||||
|
// clear room in rooms.
|
||||||
|
delSuccess({ res })
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
// close room, clear chatting
|
||||||
|
close({ roomIds: [res.roomId] }),
|
||||||
|
// clear room in rooms.
|
||||||
|
delSuccess({ res })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
catchError((error) => of(delFailure({ error })))
|
catchError((error) => of(delFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -302,14 +319,32 @@ export class Effects {
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(delMulti),
|
ofType(delMulti),
|
||||||
map((action) => action.req),
|
map((action) => action.req),
|
||||||
exhaustMap((req) => {
|
withLatestFrom(this.store.pipe(select(ChattingSelector.activeRoomId))),
|
||||||
|
exhaustMap(([req, activeRoomId]) => {
|
||||||
|
const existActiveRoomId = req.roomIds.find(
|
||||||
|
(roomId) => roomId === activeRoomId
|
||||||
|
);
|
||||||
|
|
||||||
return this.roomProtocolService.exitAll(req).pipe(
|
return this.roomProtocolService.exitAll(req).pipe(
|
||||||
switchMap((res: DeleteMultiResponse) => [
|
switchMap((res: DeleteMultiResponse) => {
|
||||||
// close room, clear chatting
|
if (!!existActiveRoomId) {
|
||||||
close({ roomIds: res.roomIds }),
|
return [
|
||||||
// clear room in rooms.
|
// clear selected room
|
||||||
delMultiSuccess({ res })
|
clearSelectedRoom({ roomId: existActiveRoomId }),
|
||||||
]),
|
// close room, clear chatting
|
||||||
|
close({ roomIds: res.roomIds }),
|
||||||
|
// clear room in rooms.
|
||||||
|
delMultiSuccess({ res })
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
// close room, clear chatting
|
||||||
|
close({ roomIds: res.roomIds }),
|
||||||
|
// clear room in rooms.
|
||||||
|
delMultiSuccess({ res })
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}),
|
||||||
catchError((error) => of(delMultiFailure({ error })))
|
catchError((error) => of(delMultiFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -357,33 +392,6 @@ export class Effects {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
inviteOrCreate$ = createEffect(() =>
|
|
||||||
this.actions$.pipe(
|
|
||||||
ofType(inviteOrCreate),
|
|
||||||
map((action) => {
|
|
||||||
const roomInfo = action.roomInfo;
|
|
||||||
const localeCode = action.localeCode;
|
|
||||||
|
|
||||||
switch (roomInfo.roomType) {
|
|
||||||
case RoomType.Single:
|
|
||||||
return create({ req: action.req });
|
|
||||||
case RoomType.Multi:
|
|
||||||
return invite({
|
|
||||||
req: {
|
|
||||||
roomId: roomInfo.roomId,
|
|
||||||
inviteUserSeqs: [...action.req.userSeqs]
|
|
||||||
},
|
|
||||||
localeCode
|
|
||||||
});
|
|
||||||
default:
|
|
||||||
return inviteFailure({
|
|
||||||
error: `type[${roomInfo.roomType}] of room is not valid`
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
invite$ = createEffect(() =>
|
invite$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(invite),
|
ofType(invite),
|
||||||
|
@ -398,7 +406,7 @@ export class Effects {
|
||||||
room({
|
room({
|
||||||
req: {
|
req: {
|
||||||
roomId: req.roomId,
|
roomId: req.roomId,
|
||||||
isDetail: true,
|
isDetail: false,
|
||||||
localeCode
|
localeCode
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -425,6 +433,47 @@ export class Effects {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expelSuccess$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(expelSuccess),
|
||||||
|
map((action) => action.res),
|
||||||
|
tap((res) => {
|
||||||
|
this.store.dispatch(
|
||||||
|
excludeUser({ roomId: res.roomId, userSeqs: res.userSeqs })
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
expelNotification$ = createEffect(() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(expelNotification),
|
||||||
|
withLatestFrom(this.store.pipe(select(UserSelector.user))),
|
||||||
|
exhaustMap(([action, user]) => {
|
||||||
|
const roomId = action.res.roomId;
|
||||||
|
const userSeqs = action.res.userSeqs;
|
||||||
|
const existMe = userSeqs.indexOf(String(user.info.seq)) > -1;
|
||||||
|
|
||||||
|
// 내가 강퇴 대상에 포함되어 있으면 우선 처리.
|
||||||
|
if (!!existMe) {
|
||||||
|
return [
|
||||||
|
close({ roomIds: [roomId] }),
|
||||||
|
clearSelectedRoom({ roomId }),
|
||||||
|
delSuccess({ res: { roomId } })
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
// 나를 제외한 강퇴 인원 처리.
|
||||||
|
if (!!userSeqs && userSeqs.length > 0) {
|
||||||
|
return [excludeUser({ roomId, userSeqs })];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
updateTimeRoomInterval$ = createEffect(() =>
|
updateTimeRoomInterval$ = createEffect(() =>
|
||||||
this.actions$.pipe(
|
this.actions$.pipe(
|
||||||
ofType(updateTimeRoomInterval),
|
ofType(updateTimeRoomInterval),
|
||||||
|
@ -440,28 +489,48 @@ export class Effects {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
inviteNotification$ = createEffect(() => {
|
/**
|
||||||
return this.actions$.pipe(
|
* @discription Call by notifications case in SSVC_TYPE_ROOM_INVITE_RES, SSVC_TYPE_ROOM_INVITE_NOTI
|
||||||
ofType(inviteNotification),
|
* 1. roomlist 를 체크하여 없을경우 내가 초대된 경우라 간주하고 방 조회하여 갱신하지 않도록 한다.(첫 대화가 들어오면 그때 조회.)
|
||||||
map((action) =>
|
* 2. roomlist 를 체크하여 있을 경우 기존방에 다른 인원이 추가되었을 경우이므로 방 조회하여 갱신한다.
|
||||||
room({
|
*/
|
||||||
req: {
|
inviteNotification$ = createEffect(
|
||||||
roomId: action.noti.roomId,
|
() => {
|
||||||
isDetail: true,
|
return this.actions$.pipe(
|
||||||
localeCode: action.localeCode
|
ofType(inviteNotification),
|
||||||
|
withLatestFrom(this.store.pipe(select(RoomSelector.rooms))),
|
||||||
|
map(([action, roomList]) => {
|
||||||
|
const roomId = action.noti.roomId;
|
||||||
|
if (!!roomList && roomList.length > 0) {
|
||||||
|
if (roomList.some((roomInfo) => roomId === roomInfo.roomId)) {
|
||||||
|
this.store.dispatch(
|
||||||
|
room({
|
||||||
|
req: {
|
||||||
|
roomId,
|
||||||
|
isDetail: false,
|
||||||
|
localeCode: action.localeCode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
);
|
||||||
);
|
},
|
||||||
});
|
{
|
||||||
|
dispatch: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
exitNotification$ = createEffect(() => {
|
exitNotification$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(exitNotification),
|
ofType(exitNotification),
|
||||||
switchMap((action) => {
|
withLatestFrom(this.store.pipe(select(UserSelector.user))),
|
||||||
if (action.userSeq === action.senderSeq) {
|
switchMap(([action, user]) => {
|
||||||
|
if (String(user.info.seq) === String(action.senderSeq)) {
|
||||||
return [
|
return [
|
||||||
close({ roomIds: [action.roomId] }),
|
close({ roomIds: [action.roomId] }),
|
||||||
|
clearSelectedRoom({ roomId: action.roomId }),
|
||||||
delSuccess({
|
delSuccess({
|
||||||
res: { roomId: action.roomId }
|
res: { roomId: action.roomId }
|
||||||
})
|
})
|
||||||
|
@ -490,7 +559,134 @@ export class Effects {
|
||||||
const roomId = action.roomId;
|
const roomId = action.roomId;
|
||||||
|
|
||||||
if (!roomList.find((roomInfo) => roomInfo.roomId === roomId)) {
|
if (!roomList.find((roomInfo) => roomInfo.roomId === roomId)) {
|
||||||
this.store.dispatch(rooms({ localeCode: LocaleCode.Korean }));
|
this.store.dispatch(
|
||||||
|
rooms({
|
||||||
|
localeCode: this.i18nService.currentLng.toUpperCase() as LocaleCode
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
userNotificationForRoom$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(CommonActions.userNotification),
|
||||||
|
withLatestFrom(
|
||||||
|
this.store.pipe(select(UserSelector.user)),
|
||||||
|
this.store.pipe(select(RoomSelector.rooms)),
|
||||||
|
this.store.pipe(select(RoomSelector.roomUsers)),
|
||||||
|
this.store.pipe(select(RoomSelector.roomUsersShort))
|
||||||
|
),
|
||||||
|
tap(([action, user, roomList, roomUsers, roomUsersShort]) => {
|
||||||
|
const noti = action.noti as UserNotification;
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number(action.noti.SENDER_SEQ) !== Number(user.info.seq) &&
|
||||||
|
noti.type === UserInfoUpdateType.Image
|
||||||
|
) {
|
||||||
|
roomUsers = (roomUsers || []).filter(
|
||||||
|
(userMap) =>
|
||||||
|
roomList.findIndex((rInfo) => rInfo.roomId === userMap.roomId) >
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
|
||||||
|
roomUsersShort = (roomUsersShort || []).filter(
|
||||||
|
(userMap) =>
|
||||||
|
roomList.findIndex((rInfo) => rInfo.roomId === userMap.roomId) >
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
|
||||||
|
const tempRoomList: {
|
||||||
|
roomInfo: RoomInfo;
|
||||||
|
uInfos?: UserInfo[];
|
||||||
|
userInfoS?: UserInfoShort[];
|
||||||
|
}[] = [];
|
||||||
|
|
||||||
|
const findIdx = noti.info.indexOf('ProfileImage');
|
||||||
|
let imgInfo: string = noti.info;
|
||||||
|
|
||||||
|
if (findIdx > -1) {
|
||||||
|
const startIdx = noti.info.indexOf('/', findIdx);
|
||||||
|
imgInfo = noti.info.substring(startIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ru of roomUsers) {
|
||||||
|
ru.userInfos.every((u) => {
|
||||||
|
if (Number(u.seq) === Number(noti.SENDER_SEQ)) {
|
||||||
|
const tempInfos: UserInfo[] = [];
|
||||||
|
const roomInfo = roomList.filter(
|
||||||
|
(r) => r.roomId === ru.roomId
|
||||||
|
)[0];
|
||||||
|
const cUser = {
|
||||||
|
...u,
|
||||||
|
profileImageFile: imgInfo
|
||||||
|
};
|
||||||
|
|
||||||
|
ru.userInfos.map((u2) => {
|
||||||
|
if (Number(u2.seq) === Number(u.seq)) {
|
||||||
|
tempInfos.push(cUser);
|
||||||
|
} else {
|
||||||
|
tempInfos.push(u2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tempRoomList.push({
|
||||||
|
roomInfo,
|
||||||
|
uInfos: tempInfos
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ru of roomUsersShort) {
|
||||||
|
ru.userInfos.every((u) => {
|
||||||
|
if (Number(u.seq) === Number(noti.SENDER_SEQ)) {
|
||||||
|
const tempShorts: UserInfoShort[] = [];
|
||||||
|
const roomInfo = roomList.filter(
|
||||||
|
(r) => r.roomId === ru.roomId
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const cUser = {
|
||||||
|
...u,
|
||||||
|
profileImageFile: imgInfo
|
||||||
|
};
|
||||||
|
ru.userInfos.map((u2) => {
|
||||||
|
if (Number(u2.seq) === Number(u.seq)) {
|
||||||
|
tempShorts.push(cUser);
|
||||||
|
} else {
|
||||||
|
tempShorts.push(u2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tempRoomList.push({
|
||||||
|
roomInfo,
|
||||||
|
userInfoS: tempShorts
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
tempRoomList.map((obj) => {
|
||||||
|
this.store.dispatch(
|
||||||
|
room2Success({
|
||||||
|
roomInfo: obj.roomInfo,
|
||||||
|
roomUserInfo: {
|
||||||
|
userInfoList: obj?.uInfos,
|
||||||
|
userInfoShortList: obj?.userInfoS
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -501,8 +697,8 @@ export class Effects {
|
||||||
constructor(
|
constructor(
|
||||||
private actions$: Actions,
|
private actions$: Actions,
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
private router: Router,
|
|
||||||
private syncProtocolService: SyncProtocolService,
|
private syncProtocolService: SyncProtocolService,
|
||||||
private roomProtocolService: RoomProtocolService
|
private roomProtocolService: RoomProtocolService,
|
||||||
|
private i18nService: I18nService
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
import { createReducer, on } from '@ngrx/store';
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
|
||||||
import {
|
import { RoomInfo } from '@ucap/protocol-room';
|
||||||
UserInfo as RoomUserInfo,
|
import { Info, EventJson } from '@ucap/protocol-event';
|
||||||
UserInfoShort as RoomUserInfoShort,
|
|
||||||
RoomInfo
|
|
||||||
} from '@ucap/protocol-room';
|
|
||||||
|
|
||||||
import * as chattingActions from '../chatting/actions';
|
import * as ChattingActions from '../chatting/actions';
|
||||||
|
import { ChatUtil } from '../../utils/chat.util';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
initialState,
|
initialState,
|
||||||
|
@ -19,18 +17,17 @@ import {
|
||||||
import {
|
import {
|
||||||
roomsSuccess,
|
roomsSuccess,
|
||||||
roomSuccess,
|
roomSuccess,
|
||||||
excludeUser,
|
|
||||||
excludeUserSuccess,
|
excludeUserSuccess,
|
||||||
delSuccess,
|
delSuccess,
|
||||||
rooms2Success,
|
rooms2Success,
|
||||||
delMultiSuccess,
|
delMultiSuccess,
|
||||||
updateSuccess,
|
updateSuccess,
|
||||||
room2Success,
|
room2Success,
|
||||||
createSuccess,
|
createRoomSuccess,
|
||||||
updateUnreadCount
|
updateUnreadCount,
|
||||||
|
updateRoomName,
|
||||||
|
updateTimeRoomIntervalSuccess
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { Info, EventJson } from '@ucap/protocol-event';
|
|
||||||
import { ChatUtil } from '../../utils/chat.util';
|
|
||||||
|
|
||||||
export const reducer = createReducer(
|
export const reducer = createReducer(
|
||||||
initialState,
|
initialState,
|
||||||
|
@ -184,63 +181,88 @@ export const reducer = createReducer(
|
||||||
|
|
||||||
on(excludeUserSuccess, (state, action) => {
|
on(excludeUserSuccess, (state, action) => {
|
||||||
const roomId = action.roomId;
|
const roomId = action.roomId;
|
||||||
const roomUserMap = state.roomUsers.entities[roomId];
|
|
||||||
const roomUserMapShort = state.roomUsersShort.entities[roomId];
|
|
||||||
|
|
||||||
const userInfos: RoomUserInfo[] = [...roomUserMap.userInfos];
|
let roomUser: RoomUserMap = state.roomUsers.entities[roomId];
|
||||||
const userInfosShort: RoomUserInfoShort[] = [...roomUserMapShort.userInfos];
|
if (!!roomUser) {
|
||||||
|
roomUser = {
|
||||||
|
...roomUser,
|
||||||
|
userInfos: state.roomUsers.entities[roomId].userInfos.map(
|
||||||
|
(roomUserInfo) => {
|
||||||
|
if (
|
||||||
|
action.userSeqs.some((seq) => seq + '' === roomUserInfo.seq + '')
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...roomUserInfo,
|
||||||
|
isJoinRoom: false
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return roomUserInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let roomUserShort: RoomUserShortMap = state.roomUsersShort.entities[roomId];
|
||||||
|
if (!!roomUserShort) {
|
||||||
|
roomUserShort = {
|
||||||
|
...roomUserShort,
|
||||||
|
userInfos: state.roomUsersShort.entities[roomId].userInfos.map(
|
||||||
|
(roomUserInfo) => {
|
||||||
|
if (
|
||||||
|
action.userSeqs.some((seq) => seq + '' === roomUserInfo.seq + '')
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
...roomUserInfo,
|
||||||
|
isJoinRoom: false
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return roomUserInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
action.userSeqs.forEach((userSeq) => {
|
const currentRoomInfo = state.rooms.entities[roomId];
|
||||||
const userInfo: RoomUserInfo = userInfos.find(
|
let roomInfo: RoomInfo;
|
||||||
(u) => userSeq === String(u.seq)
|
if (!!currentRoomInfo) {
|
||||||
);
|
roomInfo = {
|
||||||
|
...currentRoomInfo,
|
||||||
if (!!userInfo && !!userInfo.seq) {
|
joinUserCount: !!roomUser
|
||||||
userInfo.isJoinRoom = false;
|
? roomUser.userInfos.filter((item) => !!item.isJoinRoom).length
|
||||||
}
|
: !!roomUserShort
|
||||||
|
? roomUserShort.userInfos.filter((item) => !!item.isJoinRoom).length
|
||||||
const userInfoShort: RoomUserInfoShort = userInfosShort.find(
|
: currentRoomInfo.joinUserCount
|
||||||
(u) => userSeq === String(u.seq)
|
};
|
||||||
);
|
}
|
||||||
|
|
||||||
if (!!userInfoShort && !!userInfoShort.seq) {
|
|
||||||
userInfoShort.isJoinRoom = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
roomUsers: adapterRoomUser.updateOne(
|
rooms: !!roomInfo
|
||||||
{
|
? adapterRoom.upsertOne(roomInfo, { ...state.rooms })
|
||||||
id: roomId,
|
: state.rooms,
|
||||||
changes: {
|
roomUsers: !!roomUser
|
||||||
roomId,
|
? adapterRoomUser.upsertOne(roomUser, {
|
||||||
userInfos
|
...state.roomUsers
|
||||||
}
|
})
|
||||||
},
|
: state.roomUsers,
|
||||||
{
|
roomUsersShort: !!roomUserShort
|
||||||
...state.roomUsers
|
? adapterRoomUserShort.upsertOne(roomUserShort, {
|
||||||
}
|
...state.roomUsersShort
|
||||||
),
|
})
|
||||||
roomUsersShort: adapterRoomUserShort.updateOne(
|
: state.roomUsersShort
|
||||||
{
|
|
||||||
id: roomId,
|
|
||||||
changes: {
|
|
||||||
roomId,
|
|
||||||
userInfos: userInfosShort
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...state.roomUsersShort
|
|
||||||
}
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
on(createSuccess, (state, action) => {
|
on(createRoomSuccess, (state, action) => {
|
||||||
const standby = state.standbyRooms;
|
const standby = state.standbyRooms;
|
||||||
const curRoomId = action.res.roomId;
|
const curRoomId = action.res.roomId;
|
||||||
if (standby.findIndex((roomId) => roomId === curRoomId) > -1) {
|
|
||||||
|
if (
|
||||||
|
!!action.isForward ||
|
||||||
|
standby.findIndex((roomId) => roomId === curRoomId) > -1 ||
|
||||||
|
!!state.rooms.entities[curRoomId]
|
||||||
|
) {
|
||||||
// Exist.
|
// Exist.
|
||||||
return state;
|
return state;
|
||||||
} else {
|
} else {
|
||||||
|
@ -252,26 +274,6 @@ export const reducer = createReducer(
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
on(delSuccess, (state, action) => {
|
|
||||||
const roomId = action.res.roomId;
|
|
||||||
const room = state.rooms.entities[roomId];
|
|
||||||
|
|
||||||
if (!room) {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
rooms: adapterRoom.removeOne(roomId, { ...state.rooms }),
|
|
||||||
roomUsers: adapterRoomUser.removeOne(roomId, {
|
|
||||||
...state.roomUsers
|
|
||||||
}),
|
|
||||||
roomUsersShort: adapterRoomUserShort.removeOne(roomId, {
|
|
||||||
...state.roomUsersShort
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
|
|
||||||
on(updateSuccess, (state, action) => {
|
on(updateSuccess, (state, action) => {
|
||||||
const roomInfo = {
|
const roomInfo = {
|
||||||
...state.rooms.entities[action.res.roomId],
|
...state.rooms.entities[action.res.roomId],
|
||||||
|
@ -285,6 +287,30 @@ export const reducer = createReducer(
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
on(updateTimeRoomIntervalSuccess, (state, action) => {
|
||||||
|
const roomInfo = {
|
||||||
|
...state.rooms.entities[action.res.roomId],
|
||||||
|
timeRoomInterval: action.res.timerInterval
|
||||||
|
} as RoomInfo;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
rooms: adapterRoom.upsertOne(roomInfo, { ...state.rooms })
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(updateRoomName, (state, action) => {
|
||||||
|
const roomInfo = {
|
||||||
|
...state.rooms.entities[action.res.roomId],
|
||||||
|
roomName: action.res.roomName
|
||||||
|
} as RoomInfo;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
rooms: adapterRoom.upsertOne(roomInfo, { ...state.rooms })
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
on(delSuccess, (state, action) => {
|
on(delSuccess, (state, action) => {
|
||||||
const roomId = action.res.roomId;
|
const roomId = action.res.roomId;
|
||||||
const room = state.rooms.entities[roomId];
|
const room = state.rooms.entities[roomId];
|
||||||
|
@ -353,7 +379,7 @@ export const reducer = createReducer(
|
||||||
/*******************************************************************
|
/*******************************************************************
|
||||||
* [Chatting Action watching.]
|
* [Chatting Action watching.]
|
||||||
*******************************************************************/
|
*******************************************************************/
|
||||||
on(chattingActions.readSuccess, (state, action) => {
|
on(ChattingActions.readSuccess, (state, action) => {
|
||||||
const roomId = action.roomId;
|
const roomId = action.roomId;
|
||||||
const trgtUserSeq = action.SENDER_SEQ;
|
const trgtUserSeq = action.SENDER_SEQ;
|
||||||
|
|
||||||
|
@ -413,7 +439,7 @@ export const reducer = createReducer(
|
||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
|
|
||||||
on(chattingActions.addEventSuccess, (state, action) => {
|
on(ChattingActions.addEventSuccess, (state, action) => {
|
||||||
const roomId = action.roomId;
|
const roomId = action.roomId;
|
||||||
const info: Info<EventJson> = action.info;
|
const info: Info<EventJson> = action.info;
|
||||||
|
|
||||||
|
@ -434,19 +460,26 @@ export const reducer = createReducer(
|
||||||
action.info.sentMessageJson || action.info.sentMessage
|
action.info.sentMessageJson || action.info.sentMessage
|
||||||
);
|
);
|
||||||
|
|
||||||
const roomInfo = {
|
if (!finalEventMessage) {
|
||||||
...currentRoomInfo,
|
/**
|
||||||
finalEventType: info.type,
|
* 해당 타입은 메시지를 갱신하지 않는다.
|
||||||
finalEventDate: info.sendDate,
|
*/
|
||||||
finalEventMessage,
|
return state;
|
||||||
finalEventSeq: info.seq
|
} else {
|
||||||
} as RoomInfo;
|
const roomInfo = {
|
||||||
|
...currentRoomInfo,
|
||||||
|
finalEventType: info.type,
|
||||||
|
finalEventDate: info.sendDate,
|
||||||
|
finalEventMessage,
|
||||||
|
finalEventSeq: info.seq
|
||||||
|
} as RoomInfo;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
rooms: adapterRoom.upsertOne(roomInfo, { ...state.rooms }),
|
rooms: adapterRoom.upsertOne(roomInfo, { ...state.rooms }),
|
||||||
standbyRooms: fixedStandByRooms
|
standbyRooms: fixedStandByRooms
|
||||||
};
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import { Selector, createSelector } from '@ngrx/store';
|
import { Selector, createSelector } from '@ngrx/store';
|
||||||
import { EntityState, createEntityAdapter, Dictionary } from '@ngrx/entity';
|
import { EntityState, createEntityAdapter } from '@ngrx/entity';
|
||||||
|
|
||||||
import { RoomUserDetailData, RoomUserData } from '@ucap/protocol-sync';
|
|
||||||
import {
|
import {
|
||||||
RoomInfo,
|
RoomInfo,
|
||||||
UserInfo as RoomUserInfo,
|
UserInfo as RoomUserInfo,
|
||||||
|
@ -134,16 +133,23 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
selector,
|
selector,
|
||||||
(state: State) => state.standbyRooms
|
(state: State) => state.standbyRooms
|
||||||
),
|
),
|
||||||
unreadTotal: createSelector(
|
unreadTotal: createSelector(selectRooms, (roomState: RoomState) => {
|
||||||
selectRooms,
|
let unreadTotal = 0;
|
||||||
selectAllForRoom,
|
|
||||||
(roomState: RoomState, rooms: RoomInfo[]) => {
|
// tslint:disable-next-line: forin
|
||||||
let unreadTotal = 0;
|
for (const key in roomState.ids) {
|
||||||
for (const room of rooms) {
|
const roomId = roomState.ids[key];
|
||||||
|
if (roomId === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roomState.entities.hasOwnProperty(roomId)) {
|
||||||
|
const room = roomState.entities[roomId];
|
||||||
unreadTotal += room.noReadCnt;
|
unreadTotal += room.noReadCnt;
|
||||||
}
|
}
|
||||||
return unreadTotal;
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
return unreadTotal;
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,7 +128,12 @@ export class ChatUtil {
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
const m = finalEventMessage as string;
|
const m = finalEventMessage as string;
|
||||||
eventMessage = m;
|
|
||||||
|
if (eventType === EventType.Character && m.trim().length === 0) {
|
||||||
|
eventMessage = '최근 대화 없음';
|
||||||
|
} else {
|
||||||
|
eventMessage = m;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -10,6 +10,9 @@ export * from './lib/config/module-config';
|
||||||
|
|
||||||
export { CommonActions, RoomActions, ChattingActions };
|
export { CommonActions, RoomActions, ChattingActions };
|
||||||
|
|
||||||
|
export * from './lib/utils/chat.util';
|
||||||
export * from './lib/store/state';
|
export * from './lib/store/state';
|
||||||
|
export { Chatting } from './lib/store/chatting/state';
|
||||||
|
export { RoomUserMap, RoomUserShortMap } from './lib/store/room/state';
|
||||||
|
|
||||||
export * from './lib/chat-store.module';
|
export * from './lib/chat-store.module';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-store-group",
|
"name": "@ucap/ng-store-group",
|
||||||
"version": "0.0.14",
|
"version": "0.0.22",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createAction, props } from '@ngrx/store';
|
import { createAction, props } from '@ngrx/store';
|
||||||
import { UserInfo } from '@ucap/protocol-sync';
|
|
||||||
|
|
||||||
|
import { UserInfo } from '@ucap/protocol-sync';
|
||||||
import {
|
import {
|
||||||
AddRequest as BuddyAddRequest,
|
AddRequest as BuddyAddRequest,
|
||||||
AddResponse as BuddyAddResponse,
|
AddResponse as BuddyAddResponse,
|
||||||
|
@ -11,7 +11,8 @@ import {
|
||||||
} from '@ucap/protocol-buddy';
|
} from '@ucap/protocol-buddy';
|
||||||
import {
|
import {
|
||||||
UserNicknameRequest as NicknameRequest,
|
UserNicknameRequest as NicknameRequest,
|
||||||
UserNicknameResponse as NicknameResponse
|
UserNicknameResponse as NicknameResponse,
|
||||||
|
UserNotification
|
||||||
} from '@ucap/protocol-info';
|
} from '@ucap/protocol-info';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,3 +143,10 @@ export const nicknameFailure = createAction(
|
||||||
'[ucap::group::buddy] user nickname Failure',
|
'[ucap::group::buddy] user nickname Failure',
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
/**
|
||||||
|
* buddy info update by Notification
|
||||||
|
*/
|
||||||
|
export const buddyInfoUpdate = createAction(
|
||||||
|
'[ucap::group::buddy] buddy info update notification',
|
||||||
|
props<{ noti: UserNotification }>()
|
||||||
|
);
|
||||||
|
|
|
@ -30,8 +30,11 @@ import { BuddyProtocolService } from '@ucap/ng-protocol-buddy';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DepartmentSelector,
|
DepartmentSelector,
|
||||||
PresenceActions
|
PresenceActions,
|
||||||
|
CommonActions,
|
||||||
|
UserSelector
|
||||||
} from '@ucap/ng-store-organization';
|
} from '@ucap/ng-store-organization';
|
||||||
|
|
||||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
import { LoginActions } from '@ucap/ng-store-authentication';
|
||||||
|
|
||||||
import * as groupActions from '../group/actions';
|
import * as groupActions from '../group/actions';
|
||||||
|
@ -51,7 +54,8 @@ import {
|
||||||
nickname,
|
nickname,
|
||||||
nicknameSuccess,
|
nicknameSuccess,
|
||||||
nicknameFailure,
|
nicknameFailure,
|
||||||
delAndClear
|
delAndClear,
|
||||||
|
buddyInfoUpdate
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
import { BuddySelector, GroupSelector } from '../state';
|
import { BuddySelector, GroupSelector } from '../state';
|
||||||
|
@ -135,7 +139,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(req.seq as any) > -1) {
|
if (group.userSeqs.indexOf(String(req.seq)) > -1) {
|
||||||
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
||||||
if (
|
if (
|
||||||
!!this.moduleConfig.useMyDeptGroup &&
|
!!this.moduleConfig.useMyDeptGroup &&
|
||||||
|
@ -154,7 +158,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 !== (req.seq as any)
|
(userSeq) => userSeq !== String(req.seq)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -169,7 +173,7 @@ export class Effects {
|
||||||
this.moduleConfig.useMyDeptGroup &&
|
this.moduleConfig.useMyDeptGroup &&
|
||||||
!!myDeptUserList &&
|
!!myDeptUserList &&
|
||||||
myDeptUserList.filter(
|
myDeptUserList.filter(
|
||||||
(deptUser) => deptUser.seq === (req.seq as any)
|
(deptUser) => deptUser.seq === String(req.seq)
|
||||||
).length > 0
|
).length > 0
|
||||||
) {
|
) {
|
||||||
// skip;;
|
// skip;;
|
||||||
|
@ -268,25 +272,42 @@ export class Effects {
|
||||||
const userSeqsForDelete = action.userSeqsForDelete;
|
const userSeqsForDelete = action.userSeqsForDelete;
|
||||||
|
|
||||||
if (!!targetUserSeqs && 0 < targetUserSeqs.length) {
|
if (!!targetUserSeqs && 0 < targetUserSeqs.length) {
|
||||||
const addBuddyList: string[] = [];
|
// Add Buddy
|
||||||
targetUserSeqs.forEach((userSeq) => {
|
const userSeqsForAdd: string[] = [];
|
||||||
if (!buddyList) {
|
|
||||||
addBuddyList.push(userSeq);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const index = buddyList.findIndex(
|
targetUserSeqs.map((seq) => {
|
||||||
(b) => b.seq === Number(userSeq)
|
const findBuddy = buddyList.filter(
|
||||||
|
(user) => Number(user.seq) === Number(seq)
|
||||||
);
|
);
|
||||||
if (-1 < index) {
|
|
||||||
addBuddyList.push(userSeq);
|
if (!!findBuddy && findBuddy.length === 0) {
|
||||||
return;
|
userSeqsForAdd.push(seq);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (addBuddyList.length > 0) {
|
if (userSeqsForAdd.length > 0) {
|
||||||
this.store.dispatch(add({ req: { userSeqs: addBuddyList } }));
|
this.store.dispatch(add({ req: { userSeqs: userSeqsForAdd } }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// const addBuddyList: string[] = [];
|
||||||
|
// targetUserSeqs.forEach((userSeq) => {
|
||||||
|
// if (!buddyList) {
|
||||||
|
// addBuddyList.push(userSeq);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const index = buddyList.findIndex(
|
||||||
|
// (b) => b.seq === Number(userSeq)
|
||||||
|
// );
|
||||||
|
// if (-1 < index) {
|
||||||
|
// addBuddyList.push(userSeq);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (addBuddyList.length > 0) {
|
||||||
|
// this.store.dispatch(add({ req: { userSeqs: addBuddyList } }));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!userSeqsForDelete && 0 < userSeqsForDelete.length) {
|
if (!!userSeqsForDelete && 0 < userSeqsForDelete.length) {
|
||||||
|
@ -304,6 +325,21 @@ export class Effects {
|
||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
userNotificationForBuddy$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(CommonActions.userNotification),
|
||||||
|
withLatestFrom(this.store.pipe(select(UserSelector.user))),
|
||||||
|
tap(([action, user]) => {
|
||||||
|
if (Number(action.noti.SENDER_SEQ) !== Number(user.info.seq)) {
|
||||||
|
this.store.dispatch(buddyInfoUpdate({ noti: action.noti }));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private actions$: Actions,
|
private actions$: Actions,
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { createReducer, on } from '@ngrx/store';
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { UserInfoUpdateType } from '@ucap/protocol-info';
|
||||||
import { UserInfo } from '@ucap/protocol-sync';
|
import { UserInfo } from '@ucap/protocol-sync';
|
||||||
|
|
||||||
import { initialState, adapterBuddy } from './state';
|
import { initialState, adapterBuddy } from './state';
|
||||||
|
@ -7,7 +8,8 @@ import {
|
||||||
buddy2Success,
|
buddy2Success,
|
||||||
delSuccess,
|
delSuccess,
|
||||||
updateSuccess,
|
updateSuccess,
|
||||||
nicknameSuccess
|
nicknameSuccess,
|
||||||
|
buddyInfoUpdate
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
export const reducer = createReducer(
|
export const reducer = createReducer(
|
||||||
|
@ -56,5 +58,49 @@ export const reducer = createReducer(
|
||||||
...state,
|
...state,
|
||||||
buddies: adapterBuddy.upsertOne(userInfo, { ...state.buddies })
|
buddies: adapterBuddy.upsertOne(userInfo, { ...state.buddies })
|
||||||
};
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(buddyInfoUpdate, (state, action) => {
|
||||||
|
const noti = action.noti;
|
||||||
|
let buddyInfo: UserInfo;
|
||||||
|
|
||||||
|
switch (noti.type) {
|
||||||
|
case UserInfoUpdateType.Image:
|
||||||
|
{
|
||||||
|
const findIdx = noti.info.indexOf('ProfileImage');
|
||||||
|
let imgInfo: string = noti.info;
|
||||||
|
|
||||||
|
if (findIdx > -1) {
|
||||||
|
const startIdx = noti.info.indexOf('/', findIdx);
|
||||||
|
imgInfo = noti.info.substring(startIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
buddyInfo = {
|
||||||
|
...state.buddies.entities[noti.SENDER_SEQ],
|
||||||
|
profileImageFile: imgInfo
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UserInfoUpdateType.Intro:
|
||||||
|
{
|
||||||
|
buddyInfo = {
|
||||||
|
...state.buddies.entities[noti.SENDER_SEQ],
|
||||||
|
intro: noti.info
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UserInfoUpdateType.TelephoneVisible:
|
||||||
|
{
|
||||||
|
buddyInfo = {
|
||||||
|
...state.buddies.entities[noti.SENDER_SEQ]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
buddies: adapterBuddy.upsertOne(buddyInfo, { ...state.buddies })
|
||||||
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { Selector, createSelector } from '@ngrx/store';
|
import { Selector, createSelector } from '@ngrx/store';
|
||||||
import { EntityState, createEntityAdapter } from '@ngrx/entity';
|
import { EntityState, createEntityAdapter } from '@ngrx/entity';
|
||||||
|
|
||||||
import { UserInfo } from '@ucap/protocol-sync';
|
import { UserInfo } from '@ucap/protocol-sync';
|
||||||
|
|
||||||
export interface BuddyState extends EntityState<UserInfo> {
|
export interface BuddyState extends EntityState<UserInfo> {
|
||||||
syncDate: string;
|
syncDate: string;
|
||||||
}
|
}
|
||||||
export const adapterBuddy = createEntityAdapter<UserInfo>({
|
export const adapterBuddy = createEntityAdapter<UserInfo>({
|
||||||
selectId: userInfo => userInfo.seq
|
selectId: (userInfo) => userInfo.seq
|
||||||
});
|
});
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
|
@ -38,7 +39,7 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
buddies: createSelector(selectBuddies, selectAllForBuddy),
|
buddies: createSelector(selectBuddies, selectAllForBuddy),
|
||||||
buddySyncDate: createSelector(
|
buddySyncDate: createSelector(
|
||||||
selectBuddies,
|
selectBuddies,
|
||||||
buddyState => buddyState.syncDate
|
(buddyState) => buddyState.syncDate
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import {
|
||||||
} from '@ucap/protocol-group';
|
} from '@ucap/protocol-group';
|
||||||
|
|
||||||
import { GroupProtocolService } from '@ucap/ng-protocol-group';
|
import { GroupProtocolService } from '@ucap/ng-protocol-group';
|
||||||
|
|
||||||
import { SyncProtocolService } from '@ucap/ng-protocol-sync';
|
import { SyncProtocolService } from '@ucap/ng-protocol-sync';
|
||||||
|
|
||||||
import { DepartmentSelector } from '@ucap/ng-store-organization';
|
import { DepartmentSelector } from '@ucap/ng-store-organization';
|
||||||
|
@ -132,17 +131,20 @@ export class Effects {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(updateMember),
|
ofType(updateMember),
|
||||||
withLatestFrom(
|
withLatestFrom(
|
||||||
|
this.store.pipe(select(BuddySelector.buddies)),
|
||||||
this.store.pipe(select(GroupSelector.groups)),
|
this.store.pipe(select(GroupSelector.groups)),
|
||||||
this.store.pipe(select(DepartmentSelector.myDepartmentUserInfoList)) // 내 부서원 비교.
|
this.store.pipe(select(DepartmentSelector.myDepartmentUserInfoList)) // 내 부서원 비교.
|
||||||
),
|
),
|
||||||
switchMap(([action, groupList, myDeptUserList]) => {
|
switchMap(([action, buddyList, groupList, myDeptUserList]) => {
|
||||||
const targetGroup = action.targetGroup;
|
const targetGroup = action.targetGroup;
|
||||||
const targetUserSeqs = action.targetUserSeqs as any;
|
const targetUserSeqs = action.targetUserSeqs;
|
||||||
|
|
||||||
// Del Buddy
|
// Del Buddy
|
||||||
let userSeqsForDelete: string[] = targetGroup.userSeqs.filter(
|
let userSeqsForDelete: string[] = targetGroup.userSeqs.filter((v) => {
|
||||||
(v) => targetUserSeqs.indexOf(v + '') < 0
|
if (!targetUserSeqs.includes(v)) {
|
||||||
);
|
return v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
// 소속부서(내부서) 고정그룹 사용시 소속부서원을 삭제하지 않는다.
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
"@ucap/protocol-query": "@ucap/protocol-query",
|
"@ucap/protocol-query": "@ucap/protocol-query",
|
||||||
"@ucap/protocol-status": "@ucap/protocol-status",
|
"@ucap/protocol-status": "@ucap/protocol-status",
|
||||||
"@ucap/ng-api-external": "@ucap/ng-api-external",
|
"@ucap/ng-api-external": "@ucap/ng-api-external",
|
||||||
|
"@ucap/ng-protocol-info": "@ucap/ng-protocol-info",
|
||||||
"@ucap/ng-protocol-query": "@ucap/ng-protocol-query",
|
"@ucap/ng-protocol-query": "@ucap/ng-protocol-query",
|
||||||
"@ucap/ng-protocol-status": "@ucap/ng-protocol-status",
|
"@ucap/ng-protocol-status": "@ucap/ng-protocol-status"
|
||||||
"@ucap/ng-store-authentication": "@ucap/ng-store-authentication"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-store-organization",
|
"name": "@ucap/ng-store-organization",
|
||||||
"version": "0.0.8",
|
"version": "0.0.20",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
@ -10,8 +10,8 @@
|
||||||
"@ucap/core": "~0.0.1",
|
"@ucap/core": "~0.0.1",
|
||||||
"@ucap/protocol-query": "~0.0.1",
|
"@ucap/protocol-query": "~0.0.1",
|
||||||
"@ucap/ng-api-external": "~0.0.1",
|
"@ucap/ng-api-external": "~0.0.1",
|
||||||
|
"@ucap/ng-protocol-info": "~0.0.1",
|
||||||
"@ucap/ng-protocol-query": "~0.0.1",
|
"@ucap/ng-protocol-query": "~0.0.1",
|
||||||
"@ucap/ng-store-authentication": "~0.0.1",
|
|
||||||
"tslib": "^1.10.0"
|
"tslib": "^1.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
import { createAction } from '@ngrx/store';
|
import { createAction, props } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { UserNotification } from '@ucap/protocol-info';
|
||||||
|
|
||||||
export const init = createAction('[ucap::organization::common] init');
|
export const init = createAction('[ucap::organization::common] init');
|
||||||
|
|
||||||
|
export const userNotification = createAction(
|
||||||
|
'[ucap::organization::common] user Notification',
|
||||||
|
props<{ noti: UserNotification }>()
|
||||||
|
);
|
||||||
|
|
|
@ -1,8 +1,49 @@
|
||||||
|
import { withLatestFrom, tap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
import { Actions } from '@ngrx/effects';
|
import { select, Store } from '@ngrx/store';
|
||||||
|
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
||||||
|
|
||||||
|
import { modifyInfoSuccess } from '../user/actions';
|
||||||
|
import { UserSelector } from '../state';
|
||||||
|
|
||||||
|
import { userNotification } from './actions';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Effects {
|
export class Effects {
|
||||||
constructor(private actions$: Actions) {}
|
userNotification$ = createEffect(
|
||||||
|
() => {
|
||||||
|
return this.actions$.pipe(
|
||||||
|
ofType(userNotification),
|
||||||
|
withLatestFrom(this.store.pipe(select(UserSelector.user))),
|
||||||
|
tap(([action, user]) => {
|
||||||
|
if (Number(action.noti.SENDER_SEQ) === Number(user.info.seq)) {
|
||||||
|
// my
|
||||||
|
const noti = action.noti;
|
||||||
|
|
||||||
|
const findIdx = noti.info.indexOf('ProfileImage');
|
||||||
|
let notInfo: string = noti.info;
|
||||||
|
|
||||||
|
if (findIdx > -1) {
|
||||||
|
const startIdx = noti.info.indexOf('/', findIdx);
|
||||||
|
notInfo = noti.info.substring(startIdx);
|
||||||
|
}
|
||||||
|
this.store.dispatch(
|
||||||
|
modifyInfoSuccess({
|
||||||
|
res: {
|
||||||
|
SENDER_SEQ: noti.SENDER_SEQ,
|
||||||
|
info: notInfo,
|
||||||
|
type: noti.type
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
{ dispatch: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(private actions$: Actions, private store: Store<any>) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Selector, createSelector } from '@ngrx/store';
|
import { Selector, createSelector } from '@ngrx/store';
|
||||||
import { DeptInfo, UserInfoSS } from '@ucap/protocol-query';
|
|
||||||
import { Company } from '@ucap/api-external';
|
import { Company } from '@ucap/api-external';
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { Store } from '@ngrx/store';
|
||||||
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
import { Actions, ofType, createEffect } from '@ngrx/effects';
|
||||||
|
|
||||||
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
import { QueryProtocolService } from '@ucap/ng-protocol-query';
|
||||||
import { LoginActions } from '@ucap/ng-store-authentication';
|
|
||||||
|
import { init as userInit } from '../user/actions';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
dept,
|
dept,
|
||||||
|
@ -23,12 +24,12 @@ import { DeptDivisionCode } from '@ucap/protocol-query';
|
||||||
export class Effects {
|
export class Effects {
|
||||||
sessionCreatedForGroups$ = createEffect(() => {
|
sessionCreatedForGroups$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(LoginActions.sessionCreated),
|
ofType(userInit),
|
||||||
map(action =>
|
map((action) =>
|
||||||
dept({
|
dept({
|
||||||
req: {
|
req: {
|
||||||
divCd: DeptDivisionCode.Organization,
|
divCd: DeptDivisionCode.Organization,
|
||||||
companyCode: action.loginSession.companyCode
|
companyCode: action.user.companyCode
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
@ -38,17 +39,13 @@ export class Effects {
|
||||||
dept$ = createEffect(() => {
|
dept$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(dept),
|
ofType(dept),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
switchMap(req => {
|
switchMap((req) => {
|
||||||
return this.queryProtocolService.dept(req).pipe(
|
return this.queryProtocolService.dept(req).pipe(
|
||||||
map(res => {
|
map((res) => {
|
||||||
const departmentInfoList = res.departmentInfoList.sort((a, b) =>
|
return deptSuccess({ departmentInfoList: res.departmentInfoList });
|
||||||
a.order < b.order ? -1 : a.order > b.order ? 1 : 0
|
|
||||||
);
|
|
||||||
|
|
||||||
return deptSuccess({ departmentInfoList });
|
|
||||||
}),
|
}),
|
||||||
catchError(error => of(deptFailure({ error })))
|
catchError((error) => of(deptFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -57,24 +54,13 @@ export class Effects {
|
||||||
myDeptUser$ = createEffect(() => {
|
myDeptUser$ = createEffect(() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(myDeptUser),
|
ofType(myDeptUser),
|
||||||
map(action => action.req),
|
map((action) => action.req),
|
||||||
switchMap(req => {
|
switchMap((req) => {
|
||||||
return this.queryProtocolService.deptUser(req).pipe(
|
return this.queryProtocolService.deptUser(req).pipe(
|
||||||
map(res => {
|
map((res) => {
|
||||||
const userInfos = res.userInfos.sort((a, b) =>
|
return myDeptUserSuccess({ userInfos: res.userInfos });
|
||||||
a.order < b.order
|
|
||||||
? -1
|
|
||||||
: a.order > b.order
|
|
||||||
? 1
|
|
||||||
: a.name < b.name
|
|
||||||
? -1
|
|
||||||
: a.name > b.name
|
|
||||||
? 1
|
|
||||||
: 0
|
|
||||||
);
|
|
||||||
return myDeptUserSuccess({ userInfos });
|
|
||||||
}),
|
}),
|
||||||
catchError(error => of(myDeptUserFailure({ error })))
|
catchError((error) => of(myDeptUserFailure({ error })))
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,10 +4,12 @@ import { Effects as CommonEffects } from './common/effects';
|
||||||
import { Effects as CompanyEffects } from './company/effects';
|
import { Effects as CompanyEffects } from './company/effects';
|
||||||
import { Effects as DepartmentEffects } from './department/effects';
|
import { Effects as DepartmentEffects } from './department/effects';
|
||||||
import { Effects as PresenceEffects } from './presence/effects';
|
import { Effects as PresenceEffects } from './presence/effects';
|
||||||
|
import { Effects as UserEffects } from './user/effects';
|
||||||
|
|
||||||
export const effects: Type<any>[] = [
|
export const effects: Type<any>[] = [
|
||||||
CommonEffects,
|
CommonEffects,
|
||||||
CompanyEffects,
|
CompanyEffects,
|
||||||
DepartmentEffects,
|
DepartmentEffects,
|
||||||
PresenceEffects
|
PresenceEffects,
|
||||||
|
UserEffects
|
||||||
];
|
];
|
||||||
|
|
|
@ -5,54 +5,59 @@ import {
|
||||||
StatusNotification,
|
StatusNotification,
|
||||||
StatusRequest,
|
StatusRequest,
|
||||||
StatusResponse,
|
StatusResponse,
|
||||||
MessageUpdateRequest
|
MessageUpdateRequest,
|
||||||
|
MessageUpdateResponse
|
||||||
} from '@ucap/protocol-status';
|
} from '@ucap/protocol-status';
|
||||||
|
|
||||||
export const bulkInfo = createAction(
|
export const bulkInfo = createAction(
|
||||||
'[ucap::organization::presence] Bulk Info',
|
'[ucap::organization::presence] bulkInfo',
|
||||||
props<BulkInfoRequest>()
|
props<BulkInfoRequest>()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const bulkInfoSuccess = createAction(
|
export const bulkInfoSuccess = createAction(
|
||||||
'[ucap::organization::presence] Bulk Info Success',
|
'[ucap::organization::presence] bulkInfo Success',
|
||||||
props<{ statusBulkInfoList: StatusBulkInfo[] }>()
|
props<{ statusBulkInfoList: StatusBulkInfo[] }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const bulkInfoFailure = createAction(
|
export const bulkInfoFailure = createAction(
|
||||||
'[ucap::organization::presence] Bulk Info Failure',
|
'[ucap::organization::presence] bulkInfo Failure',
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const statusNotification = createAction(
|
export const statusNotification = createAction(
|
||||||
'[ucap::organization::presence] Status Notification',
|
'[ucap::organization::presence] statusNotification',
|
||||||
props<{ noti: StatusNotification }>()
|
props<{ noti: StatusNotification }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const status = createAction(
|
export const status = createAction(
|
||||||
'[ucap::organization::presence] Status',
|
'[ucap::organization::presence] status',
|
||||||
props<{ req: StatusRequest }>()
|
props<{ req: StatusRequest }>()
|
||||||
);
|
);
|
||||||
export const statusSuccess = createAction(
|
export const statusSuccess = createAction(
|
||||||
'[ucap::organization::presence] Status Success',
|
'[ucap::organization::presence] statusSuccess',
|
||||||
props<{
|
props<{
|
||||||
res: StatusResponse;
|
res: StatusResponse;
|
||||||
}>()
|
}>()
|
||||||
);
|
);
|
||||||
export const statusFailure = createAction(
|
export const statusFailure = createAction(
|
||||||
'[ucap::organization::presence] Status Failure',
|
'[ucap::organization::presence] statusFailure',
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const changeMyIdleCheckTime = createAction(
|
export const changeMyIdleCheckTime = createAction(
|
||||||
'[ucap::organization::presence] Change MyIdleCheckTime',
|
'[ucap::organization::presence] changeMyIdleCheckTime',
|
||||||
props<{ checkTime: number }>()
|
props<{ checkTime: number }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
export const messageUpdate = createAction(
|
export const messageUpdate = createAction(
|
||||||
'[ucap::organization::presence] status message update of Others',
|
'[ucap::organization::presence] messageUpdate',
|
||||||
props<{ req: MessageUpdateRequest }>()
|
props<{ req: MessageUpdateRequest }>()
|
||||||
);
|
);
|
||||||
|
export const messageUpdateSuccess = createAction(
|
||||||
|
'[ucap::organization::presence] messageUpdate Success',
|
||||||
|
props<{ res: MessageUpdateResponse }>()
|
||||||
|
);
|
||||||
export const messageUpdateFailure = createAction(
|
export const messageUpdateFailure = createAction(
|
||||||
'[ucap::organization::presence] status message update of Others Failure',
|
'[ucap::organization::presence] messageUpdate Failure',
|
||||||
props<{ error: any }>()
|
props<{ error: any }>()
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { map, exhaustMap, catchError, tap, switchMap } from 'rxjs/operators';
|
import { map, exhaustMap, catchError, concatMap } from 'rxjs/operators';
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
@ -12,10 +12,12 @@ import {
|
||||||
statusFailure,
|
statusFailure,
|
||||||
bulkInfo,
|
bulkInfo,
|
||||||
bulkInfoSuccess,
|
bulkInfoSuccess,
|
||||||
bulkInfoFailure
|
bulkInfoFailure,
|
||||||
|
messageUpdate,
|
||||||
|
messageUpdateSuccess,
|
||||||
|
messageUpdateFailure
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { StatusProtocolService } from '@ucap/ng-protocol-status';
|
import { StatusProtocolService } from '@ucap/ng-protocol-status';
|
||||||
import { StatusBulkInfo } from '@ucap/protocol-status';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Effects {
|
export class Effects {
|
||||||
|
@ -23,7 +25,7 @@ export class Effects {
|
||||||
() => {
|
() => {
|
||||||
return this.actions$.pipe(
|
return this.actions$.pipe(
|
||||||
ofType(bulkInfo),
|
ofType(bulkInfo),
|
||||||
switchMap((req) => {
|
concatMap((req) => {
|
||||||
return this.statusProtocolService.bulkInfo(req).pipe(
|
return this.statusProtocolService.bulkInfo(req).pipe(
|
||||||
map((res) => {
|
map((res) => {
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
|
@ -55,6 +57,21 @@ export class Effects {
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
messageUpdate$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(messageUpdate),
|
||||||
|
map((action) => action.req),
|
||||||
|
exhaustMap((req) => {
|
||||||
|
return this.statusProtocolService.messageUpdate(req).pipe(
|
||||||
|
map((res) => {
|
||||||
|
return messageUpdateSuccess({ res });
|
||||||
|
}),
|
||||||
|
catchError((error) => of(messageUpdateFailure({ error })))
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private actions$: Actions,
|
private actions$: Actions,
|
||||||
private store: Store<any>,
|
private store: Store<any>,
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import { createReducer, on } from '@ngrx/store';
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
|
||||||
|
import {
|
||||||
|
StatusBulkInfo,
|
||||||
|
TerminalStatusInfo,
|
||||||
|
TerminalStatusNumber
|
||||||
|
} from '@ucap/protocol-status';
|
||||||
|
|
||||||
import { initialState, State, adapterStatusBulkInfo } from './state';
|
import { initialState, State, adapterStatusBulkInfo } from './state';
|
||||||
import {
|
import {
|
||||||
bulkInfoSuccess,
|
bulkInfoSuccess,
|
||||||
|
@ -6,11 +13,6 @@ import {
|
||||||
statusSuccess,
|
statusSuccess,
|
||||||
bulkInfo
|
bulkInfo
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {
|
|
||||||
StatusBulkInfo,
|
|
||||||
TerminalStatusInfo,
|
|
||||||
TerminalStatusNumber
|
|
||||||
} from '@ucap/protocol-status';
|
|
||||||
|
|
||||||
export const reducer = createReducer(
|
export const reducer = createReducer(
|
||||||
initialState,
|
initialState,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Selector, createSelector } from '@ngrx/store';
|
import { Selector, createSelector } from '@ngrx/store';
|
||||||
import { EntityState, createEntityAdapter } from '@ngrx/entity';
|
import { EntityState, createEntityAdapter } from '@ngrx/entity';
|
||||||
|
|
||||||
import { StatusBulkInfo } from '@ucap/protocol-status';
|
import { StatusBulkInfo } from '@ucap/protocol-status';
|
||||||
|
|
||||||
export interface StatusBulkInfoState extends EntityState<StatusBulkInfo> {}
|
export interface StatusBulkInfoState extends EntityState<StatusBulkInfo> {}
|
||||||
|
@ -42,12 +43,14 @@ export function selectors<S>(selector: Selector<any, State>) {
|
||||||
selectStatusBulkInfo,
|
selectStatusBulkInfo,
|
||||||
ngeSelectEntitiesStatusBulkInfo
|
ngeSelectEntitiesStatusBulkInfo
|
||||||
),
|
),
|
||||||
selectStatusBulkInfo: (userSeq: number) =>
|
selectStatusBulkInfo: createSelector(
|
||||||
createSelector(
|
selectStatusBulkInfo,
|
||||||
selectStatusBulkInfo,
|
(statusBulkInfoState: StatusBulkInfoState, userSeq: number) => {
|
||||||
ngeSelectEntitiesStatusBulkInfo,
|
return (
|
||||||
(_, entities) => (!!entities ? entities[userSeq] : undefined)
|
statusBulkInfoState.entities && statusBulkInfoState.entities[userSeq]
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
),
|
||||||
statusBulkInfoProcessing: createSelector(
|
statusBulkInfoProcessing: createSelector(
|
||||||
selector,
|
selector,
|
||||||
(state: State) => state.statusBulkInfoProcessing
|
(state: State) => state.statusBulkInfoProcessing
|
||||||
|
|
|
@ -4,12 +4,14 @@ import { reducer as CommonReducer } from './common/reducers';
|
||||||
import { reducer as CompanyReducer } from './company/reducers';
|
import { reducer as CompanyReducer } from './company/reducers';
|
||||||
import { reducer as DepartmentReducer } from './department/reducers';
|
import { reducer as DepartmentReducer } from './department/reducers';
|
||||||
import { reducer as PresenceReducer } from './presence/reducers';
|
import { reducer as PresenceReducer } from './presence/reducers';
|
||||||
|
import { reducer as UserReducer } from './user/reducers';
|
||||||
|
|
||||||
export function reducers(state: any | undefined, action: Action) {
|
export function reducers(state: any | undefined, action: Action) {
|
||||||
return combineReducers({
|
return combineReducers({
|
||||||
common: CommonReducer,
|
common: CommonReducer,
|
||||||
company: CompanyReducer,
|
company: CompanyReducer,
|
||||||
department: DepartmentReducer,
|
department: DepartmentReducer,
|
||||||
presence: PresenceReducer
|
presence: PresenceReducer,
|
||||||
|
user: UserReducer
|
||||||
})(state, action);
|
})(state, action);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import * as CommonState from './common/state';
|
||||||
import * as CompanyState from './company/state';
|
import * as CompanyState from './company/state';
|
||||||
import * as DepartmentState from './department/state';
|
import * as DepartmentState from './department/state';
|
||||||
import * as PresenceState from './presence/state';
|
import * as PresenceState from './presence/state';
|
||||||
|
import * as UserState from './user/state';
|
||||||
|
|
||||||
export const KEY_FEATURE = 'organization';
|
export const KEY_FEATURE = 'organization';
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ export interface State {
|
||||||
company: CompanyState.State;
|
company: CompanyState.State;
|
||||||
department: DepartmentState.State;
|
department: DepartmentState.State;
|
||||||
presence: PresenceState.State;
|
presence: PresenceState.State;
|
||||||
|
user: UserState.State;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Selector = createFeatureSelector<State>(KEY_FEATURE);
|
export const Selector = createFeatureSelector<State>(KEY_FEATURE);
|
||||||
|
@ -31,3 +33,7 @@ export const DepartmentSelector = DepartmentState.selectors(
|
||||||
export const PresenceSelector = PresenceState.selectors(
|
export const PresenceSelector = PresenceState.selectors(
|
||||||
createSelector(Selector, (state: State) => state.presence)
|
createSelector(Selector, (state: State) => state.presence)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const UserSelector = UserState.selectors(
|
||||||
|
createSelector(Selector, (state: State) => state.user)
|
||||||
|
);
|
||||||
|
|
37
projects/store-organization/src/lib/store/user/actions.ts
Normal file
37
projects/store-organization/src/lib/store/user/actions.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { createAction, props } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { UserResponse, UserRequest, User } from '@ucap/protocol-info';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* info user request
|
||||||
|
*/
|
||||||
|
export const init = createAction(
|
||||||
|
'[ucap::organization::user] init',
|
||||||
|
props<{ user: User }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* modifyInfo user request
|
||||||
|
*/
|
||||||
|
export const modifyInfo = createAction(
|
||||||
|
'[ucap::organization::user] modifyInfo',
|
||||||
|
props<{ req: UserRequest }>()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success of modifyInfo request
|
||||||
|
*/
|
||||||
|
export const modifyInfoSuccess = createAction(
|
||||||
|
'[ucap::organization::user] modifyInfo Success',
|
||||||
|
props<{
|
||||||
|
res: UserResponse;
|
||||||
|
}>()
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Failure of modifyInfo request
|
||||||
|
*/
|
||||||
|
export const modifyInfoFailure = createAction(
|
||||||
|
'[ucap::organization::user] modifyInfo Failure',
|
||||||
|
props<{ error: any }>()
|
||||||
|
);
|
37
projects/store-organization/src/lib/store/user/effects.ts
Normal file
37
projects/store-organization/src/lib/store/user/effects.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { map, exhaustMap, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { Actions, createEffect, ofType } from '@ngrx/effects';
|
||||||
|
|
||||||
|
import { UserResponse } from '@ucap/protocol-info';
|
||||||
|
|
||||||
|
import { InfoProtocolService } from '@ucap/ng-protocol-info';
|
||||||
|
|
||||||
|
import { modifyInfo, modifyInfoSuccess, modifyInfoFailure } from './actions';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class Effects {
|
||||||
|
infoUser$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(modifyInfo),
|
||||||
|
map((action) => action.req),
|
||||||
|
exhaustMap((req) =>
|
||||||
|
this.infoProtocolService.user(req).pipe(
|
||||||
|
map((res: UserResponse) => {
|
||||||
|
return modifyInfoSuccess({
|
||||||
|
res
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
catchError((error) => of(modifyInfoFailure({ error })))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private actions$: Actions,
|
||||||
|
private infoProtocolService: InfoProtocolService
|
||||||
|
) {}
|
||||||
|
}
|
89
projects/store-organization/src/lib/store/user/reducers.ts
Normal file
89
projects/store-organization/src/lib/store/user/reducers.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import { createReducer, on } from '@ngrx/store';
|
||||||
|
|
||||||
|
import { UserInfoUpdateType } from '@ucap/protocol-info';
|
||||||
|
import { MessageIndexType } from '@ucap/protocol-status';
|
||||||
|
|
||||||
|
import { messageUpdateSuccess } from '../presence/actions';
|
||||||
|
|
||||||
|
import { init, modifyInfoSuccess } from './actions';
|
||||||
|
import { State, initialState } from './state';
|
||||||
|
|
||||||
|
export const reducer = createReducer(
|
||||||
|
initialState,
|
||||||
|
on(init, (state, action) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
user: action.user
|
||||||
|
} as State;
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(modifyInfoSuccess, (state, action) => {
|
||||||
|
let user = {
|
||||||
|
...state.user
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (action.res.type) {
|
||||||
|
case UserInfoUpdateType.Image:
|
||||||
|
user = {
|
||||||
|
...user,
|
||||||
|
info: {
|
||||||
|
...user.info,
|
||||||
|
profileImageFile: action.res.info
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case UserInfoUpdateType.Intro:
|
||||||
|
user = {
|
||||||
|
...user,
|
||||||
|
info: {
|
||||||
|
...user.info,
|
||||||
|
intro: action.res.info
|
||||||
|
}
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case UserInfoUpdateType.TelephoneVisible:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
user
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
on(messageUpdateSuccess, (state, action) => {
|
||||||
|
let user = {
|
||||||
|
...state.user
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (action.res.index) {
|
||||||
|
case MessageIndexType.First:
|
||||||
|
user = {
|
||||||
|
...user,
|
||||||
|
statusMessage1: action.res.statusMessage
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case MessageIndexType.Second:
|
||||||
|
user = {
|
||||||
|
...user,
|
||||||
|
statusMessage2: action.res.statusMessage
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case MessageIndexType.Third:
|
||||||
|
user = {
|
||||||
|
...user,
|
||||||
|
statusMessage3: action.res.statusMessage
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
user
|
||||||
|
} as State;
|
||||||
|
})
|
||||||
|
);
|
20
projects/store-organization/src/lib/store/user/state.ts
Normal file
20
projects/store-organization/src/lib/store/user/state.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { Selector, createSelector } from '@ngrx/store';
|
||||||
|
import { EntityState } from '@ngrx/entity';
|
||||||
|
import { StatusBulkInfo } from '@ucap/protocol-status';
|
||||||
|
import { User } from '@ucap/protocol-info';
|
||||||
|
|
||||||
|
export interface StatusBulkInfoState extends EntityState<StatusBulkInfo> {}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
user: User | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initialState: State = {
|
||||||
|
user: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
export function selectors<S>(selector: Selector<any, State>) {
|
||||||
|
return {
|
||||||
|
user: createSelector(selector, (state: State) => state.user)
|
||||||
|
};
|
||||||
|
}
|
|
@ -6,10 +6,17 @@ import * as CommonActions from './lib/store/common/actions';
|
||||||
import * as CompanyActions from './lib/store/company/actions';
|
import * as CompanyActions from './lib/store/company/actions';
|
||||||
import * as DepartmentActions from './lib/store/department/actions';
|
import * as DepartmentActions from './lib/store/department/actions';
|
||||||
import * as PresenceActions from './lib/store/presence/actions';
|
import * as PresenceActions from './lib/store/presence/actions';
|
||||||
|
import * as UserActions from './lib/store/user/actions';
|
||||||
|
|
||||||
export * from './lib/config/module-config';
|
export * from './lib/config/module-config';
|
||||||
|
|
||||||
export { CommonActions, CompanyActions, DepartmentActions, PresenceActions };
|
export {
|
||||||
|
CommonActions,
|
||||||
|
CompanyActions,
|
||||||
|
DepartmentActions,
|
||||||
|
PresenceActions,
|
||||||
|
UserActions
|
||||||
|
};
|
||||||
|
|
||||||
export * from './lib/store/state';
|
export * from './lib/store/state';
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-ui-authentication",
|
"name": "@ucap/ng-ui-authentication",
|
||||||
"version": "0.0.25",
|
"version": "0.0.29",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="change-password-form">
|
<div class="change-password-form">
|
||||||
<form name="changePasswordForm" [formGroup]="changePasswordForm" novalidate>
|
<form name="changePasswordForm" [formGroup]="changePasswordForm" novalidate>
|
||||||
<mat-form-field>
|
<mat-form-field floatLabel="never" appearance="none">
|
||||||
<mat-label>{{ 'accounts.fieldCurrentPassword' | ucapI18n }}</mat-label>
|
<mat-label>{{ 'accounts.fieldCurrentPassword' | ucapI18n }}</mat-label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
|
@ -8,11 +8,11 @@
|
||||||
[formControl]="currentLoginPwFormControl"
|
[formControl]="currentLoginPwFormControl"
|
||||||
/>
|
/>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field>
|
<mat-form-field floatLabel="never" appearance="none">
|
||||||
<mat-label>{{ 'accounts.fieldNewPassword' | ucapI18n }}</mat-label>
|
<mat-label>{{ 'accounts.fieldNewPassword' | ucapI18n }}</mat-label>
|
||||||
<input matInput type="password" [formControl]="newLoginPwFormControl" />
|
<input matInput type="password" [formControl]="newLoginPwFormControl" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<mat-form-field>
|
<mat-form-field floatLabel="never" appearance="none">
|
||||||
<mat-label>{{ 'accounts.fieldNewPasswordConfirm' | ucapI18n }}</mat-label>
|
<mat-label>{{ 'accounts.fieldNewPasswordConfirm' | ucapI18n }}</mat-label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
|
|
|
@ -7,11 +7,15 @@
|
||||||
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
[style.display]="!!fixedCompanyCode ? 'none' : 'block'"
|
||||||
>
|
>
|
||||||
<mat-form-field color="accent">
|
<mat-form-field color="accent">
|
||||||
<mat-label>{{ 'login.fields.company' | ucapI18n }}</mat-label>
|
<mat-label>{{
|
||||||
|
'authentication:login.fields.company' | ucapI18n
|
||||||
|
}}</mat-label>
|
||||||
<mat-select
|
<mat-select
|
||||||
[formControl]="companyCodeFormControl"
|
[formControl]="companyCodeFormControl"
|
||||||
[value]="companyCode || fixedCompanyCode"
|
[value]="companyCode || fixedCompanyCode"
|
||||||
placeholder="{{ 'login.labels.selectCompany' | ucapI18n }}"
|
placeholder="{{
|
||||||
|
'authentication:login.labels.selectCompany' | ucapI18n
|
||||||
|
}}"
|
||||||
>
|
>
|
||||||
<mat-option
|
<mat-option
|
||||||
*ngFor="let company of companyList"
|
*ngFor="let company of companyList"
|
||||||
|
@ -49,8 +53,10 @@
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<mat-form-field>
|
<mat-form-field floatLabel="never" appearance="none">
|
||||||
<mat-label>{{ 'login.fields.loginId' | ucapI18n }}</mat-label>
|
<mat-label>{{
|
||||||
|
'authentication:login.fields.loginId' | ucapI18n
|
||||||
|
}}</mat-label>
|
||||||
<input matInput [formControl]="loginIdFormControl" />
|
<input matInput [formControl]="loginIdFormControl" />
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
@ -95,8 +101,10 @@
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
</span>
|
</span>
|
||||||
<mat-form-field>
|
<mat-form-field floatLabel="never" appearance="none">
|
||||||
<mat-label>{{ 'login.fields.loginPw' | ucapI18n }}</mat-label>
|
<mat-label>{{
|
||||||
|
'authentication:login.fields.loginPw' | ucapI18n
|
||||||
|
}}</mat-label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
type="password"
|
type="password"
|
||||||
|
@ -115,7 +123,7 @@
|
||||||
"
|
"
|
||||||
class="ucap-authentication-login-error"
|
class="ucap-authentication-login-error"
|
||||||
>
|
>
|
||||||
{{ 'login.errors.requireCompany' | ucapI18n }}
|
{{ 'authentication:login.errors.requireCompany' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-error
|
<mat-error
|
||||||
*ngIf="
|
*ngIf="
|
||||||
|
@ -125,7 +133,7 @@
|
||||||
"
|
"
|
||||||
class="ucap-authentication-login-error"
|
class="ucap-authentication-login-error"
|
||||||
>
|
>
|
||||||
{{ 'login.errors.requireLoginId' | ucapI18n }}
|
{{ 'authentication:login.errors.requireLoginId' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-error
|
<mat-error
|
||||||
*ngIf="
|
*ngIf="
|
||||||
|
@ -135,10 +143,10 @@
|
||||||
"
|
"
|
||||||
class="ucap-authentication-login-error"
|
class="ucap-authentication-login-error"
|
||||||
>
|
>
|
||||||
{{ 'login.errors.requireLoginPw' | ucapI18n }}
|
{{ 'authentication:login.errors.requireLoginPw' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
<mat-error *ngIf="loginFailed" class="ucap-authentication-login-error">
|
<mat-error *ngIf="loginFailed" class="ucap-authentication-login-error">
|
||||||
{{ 'login.errors.failed' | ucapI18n }}
|
{{ 'authentication:login.errors.failed' | ucapI18n }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -156,11 +164,11 @@
|
||||||
<ng-container
|
<ng-container
|
||||||
*ngIf="!processing && (!loginTry || !loginTry.remainTimeForNextTry)"
|
*ngIf="!processing && (!loginTry || !loginTry.remainTimeForNextTry)"
|
||||||
>
|
>
|
||||||
{{ 'login.labels.doLogin' | ucapI18n }}
|
{{ 'authentication:login.labels.doLogin' | ucapI18n }}
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<ng-container *ngIf="!!loginTry && !!loginTry.remainTimeForNextTry">
|
<ng-container *ngIf="!!loginTry && !!loginTry.remainTimeForNextTry">
|
||||||
{{ 'login.errors.attemptsExceeded' | ucapI18n }}
|
{{ 'authentication:login.errors.attemptsExceeded' | ucapI18n }}
|
||||||
(
|
(
|
||||||
{{
|
{{
|
||||||
moment
|
moment
|
||||||
|
|
|
@ -2,6 +2,10 @@
|
||||||
* Public API Surface of ui-authentication
|
* Public API Surface of ui-authentication
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export * from './lib/components/change-password.component';
|
||||||
|
export * from './lib/components/login.component';
|
||||||
|
|
||||||
export * from './lib/config/module-config';
|
export * from './lib/config/module-config';
|
||||||
|
export * from './lib/config/token';
|
||||||
|
|
||||||
export * from './lib/authentication-ui.module';
|
export * from './lib/authentication-ui.module';
|
||||||
|
|
|
@ -5,12 +5,16 @@
|
||||||
"entryFile": "src/public-api.ts",
|
"entryFile": "src/public-api.ts",
|
||||||
"styleIncludePaths": ["./src/assets/scss"],
|
"styleIncludePaths": ["./src/assets/scss"],
|
||||||
"umdModuleIds": {
|
"umdModuleIds": {
|
||||||
|
"moment": "moment",
|
||||||
"ngx-perfect-scrollbar": "ngx-perfect-scrollbar",
|
"ngx-perfect-scrollbar": "ngx-perfect-scrollbar",
|
||||||
"@ucap/core": "@ucap/core",
|
"@ucap/core": "@ucap/core",
|
||||||
|
"@ucap/api": "@ucap/api",
|
||||||
|
"@ucap/protocol-event": "@ucap/protocol-event",
|
||||||
"@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-i18n": "@ucap/ng-i18n",
|
||||||
"@ucap/ng-ui": "@ucap/ng-ui"
|
"@ucap/ng-ui": "@ucap/ng-ui",
|
||||||
|
"@ucap/ng-ui-organization": "@ucap/ng-ui-organization"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@ucap/ng-ui-chat",
|
"name": "@ucap/ng-ui-chat",
|
||||||
"version": "0.0.13",
|
"version": "0.0.72",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
"registry": "https://nexus.loafle.net/repository/npm-ucap/"
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,25 +3,36 @@ import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
import { ScrollingModule } from '@angular/cdk/scrolling';
|
|
||||||
|
|
||||||
import { MatBadgeModule } from '@angular/material/badge';
|
import { MatBadgeModule } from '@angular/material/badge';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatRippleModule } from '@angular/material/core';
|
import { MatRippleModule } from '@angular/material/core';
|
||||||
import { MatTreeModule } from '@angular/material/tree';
|
import { MatTreeModule } from '@angular/material/tree';
|
||||||
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||||
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||||
import { MatMenuModule } from '@angular/material/menu';
|
import { MatMenuModule } from '@angular/material/menu';
|
||||||
|
|
||||||
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
import { PerfectScrollbarModule } from 'ngx-perfect-scrollbar';
|
||||||
|
|
||||||
import { UiModule } from '@ucap/ng-ui';
|
import { UiModule } from '@ucap/ng-ui';
|
||||||
|
import { OrganizationUiModule } from '@ucap/ng-ui-organization';
|
||||||
|
|
||||||
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
import { I18nModule, UCAP_I18N_NAMESPACE } from '@ucap/ng-i18n';
|
||||||
|
|
||||||
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 {
|
||||||
|
ImageListComponent,
|
||||||
|
ImageListNodeDirective
|
||||||
|
} from './components/image-list.component';
|
||||||
|
import {
|
||||||
|
FileListComponent,
|
||||||
|
FileListNodeDirective
|
||||||
|
} from './components/file-list.component';
|
||||||
|
import { ImageListItem01Component } from './components/image-list-item-01.component';
|
||||||
|
import { FileListItem01Component } from './components/file-list-item-01.component';
|
||||||
import {
|
import {
|
||||||
RoomExpansionComponent,
|
RoomExpansionComponent,
|
||||||
RoomExpansionHeaderDirective,
|
RoomExpansionHeaderDirective,
|
||||||
|
@ -45,10 +56,19 @@ import { ReadHereComponent } from './components/message-box/read-here.component'
|
||||||
import { DateSplitterComponent } from './components/message-box/date-splitter.component';
|
import { DateSplitterComponent } from './components/message-box/date-splitter.component';
|
||||||
import { SmsComponent } from './components/message-box/sms.component';
|
import { SmsComponent } from './components/message-box/sms.component';
|
||||||
import { ReplyComponent } from './components/message-box/reply.component';
|
import { ReplyComponent } from './components/message-box/reply.component';
|
||||||
|
import {
|
||||||
|
ChatItemListComponent,
|
||||||
|
ChatItemListNodeDirective
|
||||||
|
} from './components/item-list.component';
|
||||||
|
|
||||||
const COMPONENTS = [
|
const COMPONENTS = [
|
||||||
|
ImageListItem01Component,
|
||||||
|
FileListItem01Component,
|
||||||
|
FileListComponent,
|
||||||
|
ImageListComponent,
|
||||||
RoomExpansionComponent,
|
RoomExpansionComponent,
|
||||||
RoomListItem01Component,
|
RoomListItem01Component,
|
||||||
|
ChatItemListComponent,
|
||||||
|
|
||||||
InformationComponent,
|
InformationComponent,
|
||||||
TextComponent,
|
TextComponent,
|
||||||
|
@ -72,7 +92,13 @@ const COMPONENTS = [
|
||||||
];
|
];
|
||||||
const DIALOGS = [];
|
const DIALOGS = [];
|
||||||
const PIPES = [];
|
const PIPES = [];
|
||||||
const DIRECTIVES = [RoomExpansionHeaderDirective, RoomExpansionNodeDirective];
|
const DIRECTIVES = [
|
||||||
|
RoomExpansionHeaderDirective,
|
||||||
|
RoomExpansionNodeDirective,
|
||||||
|
FileListNodeDirective,
|
||||||
|
ImageListNodeDirective,
|
||||||
|
ChatItemListNodeDirective
|
||||||
|
];
|
||||||
const SERVICES = [];
|
const SERVICES = [];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -86,7 +112,6 @@ export class ChatUiRootModule {}
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
FlexLayoutModule,
|
FlexLayoutModule,
|
||||||
ScrollingModule,
|
|
||||||
|
|
||||||
MatBadgeModule,
|
MatBadgeModule,
|
||||||
MatButtonModule,
|
MatButtonModule,
|
||||||
|
@ -95,10 +120,13 @@ export class ChatUiRootModule {}
|
||||||
MatRippleModule,
|
MatRippleModule,
|
||||||
MatTreeModule,
|
MatTreeModule,
|
||||||
MatMenuModule,
|
MatMenuModule,
|
||||||
|
MatProgressBarModule,
|
||||||
|
MatTooltipModule,
|
||||||
|
|
||||||
PerfectScrollbarModule,
|
PerfectScrollbarModule,
|
||||||
|
|
||||||
I18nModule,
|
I18nModule,
|
||||||
|
OrganizationUiModule,
|
||||||
UiModule
|
UiModule
|
||||||
],
|
],
|
||||||
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
exports: [...COMPONENTS, ...DIRECTIVES, ...PIPES],
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
<div class="ucap-chat-file-list-item-01-container">
|
||||||
|
<div class="contents">
|
||||||
|
<!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가-->
|
||||||
|
<!-- <div class="file-img" [ngClass]="fileInfo.FileExt"></div> -->
|
||||||
|
<div
|
||||||
|
[ngClass]="[
|
||||||
|
'mime-icon',
|
||||||
|
'light',
|
||||||
|
'ico-' + fileInfo.sentMessageJson.fileExt
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div class="ico"></div>
|
||||||
|
</div>
|
||||||
|
<ul class="file-info">
|
||||||
|
<li class="file-name">
|
||||||
|
<span class="text-filename">{{
|
||||||
|
fileInfo.sentMessageJson.fileName
|
||||||
|
}}</span>
|
||||||
|
<span class="text-username">{{
|
||||||
|
senderInfo | ucapOrganizationTranslate: 'name'
|
||||||
|
}}</span>
|
||||||
|
</li>
|
||||||
|
<li class="file-bytes-date">
|
||||||
|
<span class="amount-bytes">{{
|
||||||
|
fileInfo.sentMessageJson.attachmentSize | ucapBytes
|
||||||
|
}}</span>
|
||||||
|
<span class="text-date">
|
||||||
|
<em>{{
|
||||||
|
fileInfo.sentMessageJson.attachmentRegDate | ucapDate: 'MM.DD'
|
||||||
|
}}</em>
|
||||||
|
<em *ngIf="!!fileRetentionPeriod && fileRetentionPeriod > 0">
|
||||||
|
~
|
||||||
|
{{
|
||||||
|
fileInfo.sentMessageJson.attachmentRegDate
|
||||||
|
| ucapDate: 'MM.DD':fileRetentionPeriodOptions
|
||||||
|
}}
|
||||||
|
</em>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li
|
||||||
|
*ngIf="!!downloadTotalCount && !!downloadedCount"
|
||||||
|
class="file-list-amount"
|
||||||
|
(click)="onClickShowDownCheck()"
|
||||||
|
>
|
||||||
|
<strong>{{ downloadedCount }}</strong
|
||||||
|
>/{{ downloadTotalCount }}
|
||||||
|
</li>
|
||||||
|
<li class="file-check-box">
|
||||||
|
<mat-checkbox
|
||||||
|
*ngIf="!!checkable"
|
||||||
|
#checkbox
|
||||||
|
aria-label="check"
|
||||||
|
[checked]="checked"
|
||||||
|
(change)="onChangeCheck(checkbox.checked)"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="progress"
|
||||||
|
*ngIf="!!fileDownloadItem"
|
||||||
|
[ngClass]="
|
||||||
|
!!fileDownloadItem &&
|
||||||
|
!!fileDownloadItem.downloadingProgress$ &&
|
||||||
|
(fileDownloadItem.downloadingProgress$ | async) > 0 &&
|
||||||
|
(fileDownloadItem.downloadingProgress$ | async) < 100
|
||||||
|
? 'over'
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="close"
|
||||||
|
class="btn-close"
|
||||||
|
color="accent"
|
||||||
|
style="display: none;"
|
||||||
|
>
|
||||||
|
<mat-icon color="accent">cancel</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-progress-bar
|
||||||
|
color="accent"
|
||||||
|
mode="determinate"
|
||||||
|
[value]="fileDownloadItem.downloadingProgress$ | async"
|
||||||
|
></mat-progress-bar>
|
||||||
|
|
||||||
|
<div class="progress-info-area">
|
||||||
|
<span style="display: none;">{{
|
||||||
|
'common:file.downloading' | ucapI18n
|
||||||
|
}}</span>
|
||||||
|
<strong>{{ fileDownloadItem.downloadingProgress$ | async }}%</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,21 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
.ucap-chat-file-list-item-01-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
.mime-icon {
|
||||||
|
flex: 0 0 60px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
flex: 1 1 auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
import { ProfileListItemComponent } from './profile-list-item.component';
|
import { ProfileListItemComponent } from './profile-list-item.component';
|
||||||
|
|
||||||
describe('ucap::ui-organization::ProfileListItemComponent', () => {
|
describe('ucap::ucap::organization::ProfileListItemComponent', () => {
|
||||||
let component: ProfileListItemComponent;
|
let component: ProfileListItemComponent;
|
||||||
let fixture: ComponentFixture<ProfileListItemComponent>;
|
let fixture: ComponentFixture<ProfileListItemComponent>;
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import {
|
||||||
|
UserInfo as RoomUserInfo,
|
||||||
|
UserInfoShort as RoomUserInfoShort
|
||||||
|
} from '@ucap/protocol-room';
|
||||||
|
import { FileInfo } from '@ucap/protocol-file';
|
||||||
|
import { DateOptions } from '@ucap/ng-ui';
|
||||||
|
import { FileDownloadItem } from '@ucap/api';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-file-list-item-01',
|
||||||
|
templateUrl: './file-list-item-01.component.html',
|
||||||
|
styleUrls: ['./file-list-item-01.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FileListItem01Component implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
fileInfo: FileInfo;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
senderInfo: RoomUserInfo | RoomUserInfoShort;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
downloadTotalCount: number;
|
||||||
|
@Input()
|
||||||
|
downloadedCount: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileRetentionPeriod: number;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
expired = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checked = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checkable = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changeCheck = new EventEmitter<{
|
||||||
|
checked: boolean;
|
||||||
|
fileInfo: FileInfo;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
showDownCheck = new EventEmitter<FileInfo>();
|
||||||
|
|
||||||
|
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.next();
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickShowDownCheck() {
|
||||||
|
this.showDownCheck.emit(this.fileInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeCheck(checked: boolean) {
|
||||||
|
this.changeCheck.emit({
|
||||||
|
checked,
|
||||||
|
fileInfo: this.fileInfo,
|
||||||
|
fileDownloadItem: this.fileDownloadItem
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get fileRetentionPeriodOptions(): DateOptions {
|
||||||
|
return {
|
||||||
|
manipulate: { amount: this.fileRetentionPeriod, unit: 'days' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
15
projects/ui-chat/src/lib/components/file-list.component.html
Normal file
15
projects/ui-chat/src/lib/components/file-list.component.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<div class="ucap-chat-file-list-container" fxFlexFill>
|
||||||
|
<ucap-virtual-scroll-viewport
|
||||||
|
#cvsvList
|
||||||
|
perfectScrollbar
|
||||||
|
fxFlexFill
|
||||||
|
[itemSize]="vsItemSize"
|
||||||
|
>
|
||||||
|
<ng-container *ucapVirtualFor="let fileInfo of fileInfos">
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="nodeTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: fileInfo }"
|
||||||
|
></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ucap-virtual-scroll-viewport>
|
||||||
|
</div>
|
|
@ -0,0 +1,7 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
.ucap-chat-file-list-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { FileListComponent } from './file-list.component';
|
||||||
|
|
||||||
|
describe('ucap::chat::FileListComponent', () => {
|
||||||
|
let component: FileListComponent;
|
||||||
|
let fixture: ComponentFixture<FileListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [FileListComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(FileListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { moduleMetadata } from '@storybook/angular';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { linkTo } from '@storybook/addon-links';
|
||||||
|
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { ChatUiModule } from '../chat-ui.module';
|
||||||
|
|
||||||
|
import { FileListComponent } from './file-list.component';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'ui-chat::FileListComponent',
|
||||||
|
component: FileListComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [BrowserModule, BrowserAnimationsModule, ChatUiModule],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = () => ({
|
||||||
|
component: FileListComponent,
|
||||||
|
props: {}
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
@mixin ucap-organization-profile-list-theme($theme) {
|
||||||
|
$is-dark-theme: map-get($theme, is-dark);
|
||||||
|
$primary: map-get($theme, primary);
|
||||||
|
$accent: map-get($theme, accent);
|
||||||
|
$warn: map-get($theme, warn);
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
|
||||||
|
.ucap-organization-profile-list-container {
|
||||||
|
border-color: mat-color($foreground, secondary-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ucap-organization-profile-list-typography($config) {
|
||||||
|
.ucap-organization-profile-list-container {
|
||||||
|
.searchword-container {
|
||||||
|
.form-field-input-searchword {
|
||||||
|
font-family: mat-font-family($config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
projects/ui-chat/src/lib/components/file-list.component.ts
Normal file
55
projects/ui-chat/src/lib/components/file-list.component.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
Directive,
|
||||||
|
ContentChild,
|
||||||
|
TemplateRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { FileInfo } from '@ucap/protocol-file';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapChatFileListNode]'
|
||||||
|
})
|
||||||
|
export class FileListNodeDirective {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-file-list',
|
||||||
|
templateUrl: './file-list.component.html',
|
||||||
|
styleUrls: ['./file-list.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class FileListComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
fileInfos: FileInfo[];
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
vsItemSize: number;
|
||||||
|
|
||||||
|
@ContentChild(FileListNodeDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
nodeTemplate: TemplateRef<FileListNodeDirective>;
|
||||||
|
|
||||||
|
private ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.next();
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
<div
|
||||||
|
class="ucap-chat-image-list-item-01-container"
|
||||||
|
(mouseover)="onMouseover($event)"
|
||||||
|
(mouseleave)="onMouseleave($event)"
|
||||||
|
>
|
||||||
|
<div class="contents">
|
||||||
|
<img
|
||||||
|
[src]="fileInfo.sentMessageJson.thumbUrl"
|
||||||
|
onerror="this.src='assets/sticker/sticker_default.png'"
|
||||||
|
/>
|
||||||
|
<mat-checkbox
|
||||||
|
#checkbox
|
||||||
|
*ngIf="!!checkable"
|
||||||
|
aria-label="check"
|
||||||
|
[checked]="checked"
|
||||||
|
(change)="onChangeCheck(checkbox.checked)"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
>
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="btn-box" [ngClass]="!!showOverButtons ? 'over' : ''">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="ico-btn-obj"
|
||||||
|
aria-label="저장"
|
||||||
|
[matTooltip]="'common:file.save' | ucapI18n"
|
||||||
|
(click)="onClickSave($event)"
|
||||||
|
>
|
||||||
|
<mat-icon>save_alt</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="ico-btn-obj"
|
||||||
|
aria-label="뷰어로열기"
|
||||||
|
[matTooltip]="'common:file.fileOpen' | ucapI18n"
|
||||||
|
(click)="onClickOpenViewer($event)"
|
||||||
|
>
|
||||||
|
<mat-icon>launch</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
class="ico-btn-obj"
|
||||||
|
aria-label="삭제"
|
||||||
|
[matTooltip]="'common:file.delete' | ucapI18n"
|
||||||
|
(click)="onClickDelete($event)"
|
||||||
|
>
|
||||||
|
<mat-icon class="material-icons-outlined">delete_outline</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="progress"
|
||||||
|
*ngIf="!!fileDownloadItem"
|
||||||
|
[ngClass]="
|
||||||
|
!!fileDownloadItem &&
|
||||||
|
!!fileDownloadItem.downloadingProgress$ &&
|
||||||
|
(fileDownloadItem.downloadingProgress$ | async) > 0 &&
|
||||||
|
(fileDownloadItem.downloadingProgress$ | async) < 100
|
||||||
|
? 'over'
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="close"
|
||||||
|
class="btn-close"
|
||||||
|
color="accent"
|
||||||
|
style="display: none;"
|
||||||
|
>
|
||||||
|
<mat-icon color="accent">cancel</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-progress-bar
|
||||||
|
color="accent"
|
||||||
|
mode="determinate"
|
||||||
|
[value]="fileDownloadItem.downloadingProgress$ | async"
|
||||||
|
></mat-progress-bar>
|
||||||
|
|
||||||
|
<div class="progress-info-area">
|
||||||
|
<span style="display: none;">{{
|
||||||
|
'common:file.downloading' | ucapI18n
|
||||||
|
}}</span>
|
||||||
|
<strong>{{ fileDownloadItem.downloadingProgress$ | async }}%</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,44 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
.ucap-chat-image-list-item-01-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
//padding-left: 4px;
|
||||||
|
//margin-left: -4px;
|
||||||
|
.contents {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
img {
|
||||||
|
}
|
||||||
|
.mat-checkbox {
|
||||||
|
position: absolute;
|
||||||
|
//right: 0;
|
||||||
|
//top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn-box {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
//background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
ul {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 30px;
|
||||||
|
position: absolute;
|
||||||
|
//bottom: 0;
|
||||||
|
li {
|
||||||
|
button {
|
||||||
|
//width: 30%;
|
||||||
|
//height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
|
||||||
|
import { ProfileListItemComponent } from './profile-list-item.component';
|
||||||
|
|
||||||
|
describe('ucap::ucap::organization::ProfileListItemComponent', () => {
|
||||||
|
let component: ProfileListItemComponent;
|
||||||
|
let fixture: ComponentFixture<ProfileListItemComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ProfileListItemComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProfileListItemComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,122 @@
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
EventEmitter,
|
||||||
|
Output
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
import { FileInfo } from '@ucap/protocol-file';
|
||||||
|
import { FileDownloadItem } from '@ucap/api';
|
||||||
|
import { FileEventJson } from '@ucap/protocol-event';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-image-list-item-01',
|
||||||
|
templateUrl: './image-list-item-01.component.html',
|
||||||
|
styleUrls: ['./image-list-item-01.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ImageListItem01Component implements OnInit, OnDestroy {
|
||||||
|
private ngOnDestroySubject: Subject<void> = new Subject();
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileInfo: FileInfo;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
expired = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checked = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
checkable = true;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
changeCheck = new EventEmitter<{
|
||||||
|
checked: boolean;
|
||||||
|
fileInfo: FileInfo;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
save = new EventEmitter<{
|
||||||
|
fileInfo: FileEventJson;
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
type: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openViewer = new EventEmitter<FileInfo>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
delete = new EventEmitter<FileInfo>();
|
||||||
|
|
||||||
|
showOverButtons = false;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.next();
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMouseover(event: MouseEvent): void {
|
||||||
|
if (!this.expired) {
|
||||||
|
this.showOverButtons = true;
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
onMouseleave(event: MouseEvent): void {
|
||||||
|
this.showOverButtons = false;
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeCheck(checked: boolean) {
|
||||||
|
this.changeCheck.emit({
|
||||||
|
checked,
|
||||||
|
fileInfo: this.fileInfo,
|
||||||
|
fileDownloadItem: this.fileDownloadItem
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickSave(event: MouseEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const param = {
|
||||||
|
fileInfo: {
|
||||||
|
// require parameters.
|
||||||
|
fileName: this.fileInfo.name,
|
||||||
|
attachmentSeq: this.fileInfo.seq
|
||||||
|
} as FileEventJson,
|
||||||
|
fileDownloadItem: this.fileDownloadItem,
|
||||||
|
type: 'save'
|
||||||
|
};
|
||||||
|
this.save.emit(param);
|
||||||
|
}
|
||||||
|
onClickOpenViewer(event: MouseEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.openViewer.emit(this.fileInfo);
|
||||||
|
}
|
||||||
|
onClickDelete(event: MouseEvent) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.delete.emit(this.fileInfo);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<div class="ucap-chat-image-list-container" fxFlexFill>
|
||||||
|
<ucap-virtual-scroll-viewport
|
||||||
|
#cvsvList
|
||||||
|
perfectScrollbar
|
||||||
|
fxFlexFill
|
||||||
|
[itemSize]="vsItemSize"
|
||||||
|
>
|
||||||
|
<ng-container *ucapVirtualFor="let fileInfo of fileInfos">
|
||||||
|
<ng-container
|
||||||
|
[ngTemplateOutlet]="nodeTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: fileInfo }"
|
||||||
|
></ng-container>
|
||||||
|
</ng-container>
|
||||||
|
</ucap-virtual-scroll-viewport>
|
||||||
|
</div>
|
|
@ -0,0 +1,7 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
.ucap-chat-image-list-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { ImageListComponent } from './image-list.component';
|
||||||
|
|
||||||
|
describe('ucap::chat::ImageListComponent', () => {
|
||||||
|
let component: ImageListComponent;
|
||||||
|
let fixture: ComponentFixture<ImageListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ImageListComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ImageListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { moduleMetadata } from '@storybook/angular';
|
||||||
|
import { action } from '@storybook/addon-actions';
|
||||||
|
import { linkTo } from '@storybook/addon-links';
|
||||||
|
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { ChatUiModule } from '../chat-ui.module';
|
||||||
|
|
||||||
|
import { ImageListComponent } from './image-list.component';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'ui-chat::ImageListComponent',
|
||||||
|
component: ImageListComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [BrowserModule, BrowserAnimationsModule, ChatUiModule],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = () => ({
|
||||||
|
component: ImageListComponent,
|
||||||
|
props: {}
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
@mixin ucap-organization-profile-list-theme($theme) {
|
||||||
|
$is-dark-theme: map-get($theme, is-dark);
|
||||||
|
$primary: map-get($theme, primary);
|
||||||
|
$accent: map-get($theme, accent);
|
||||||
|
$warn: map-get($theme, warn);
|
||||||
|
$background: map-get($theme, background);
|
||||||
|
$foreground: map-get($theme, foreground);
|
||||||
|
|
||||||
|
.ucap-organization-profile-list-container {
|
||||||
|
border-color: mat-color($foreground, secondary-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ucap-organization-profile-list-typography($config) {
|
||||||
|
.ucap-organization-profile-list-container {
|
||||||
|
.searchword-container {
|
||||||
|
.form-field-input-searchword {
|
||||||
|
font-family: mat-font-family($config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
projects/ui-chat/src/lib/components/image-list.component.ts
Normal file
55
projects/ui-chat/src/lib/components/image-list.component.ts
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
Directive,
|
||||||
|
ContentChild,
|
||||||
|
TemplateRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { FileInfo } from '@ucap/protocol-file';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapChatImageListNode]'
|
||||||
|
})
|
||||||
|
export class ImageListNodeDirective {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-image-list',
|
||||||
|
templateUrl: './image-list.component.html',
|
||||||
|
styleUrls: ['./image-list.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ImageListComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
fileInfos: FileInfo[];
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
vsItemSize: number;
|
||||||
|
|
||||||
|
@ContentChild(ImageListNodeDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
nodeTemplate: TemplateRef<ImageListNodeDirective>;
|
||||||
|
|
||||||
|
private ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.next();
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
projects/ui-chat/src/lib/components/item-list.component.html
Normal file
17
projects/ui-chat/src/lib/components/item-list.component.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="ucap-chat-item-list-container" #container fxFlexFill>
|
||||||
|
<ucap-virtual-scroll-viewport
|
||||||
|
#cvsvList
|
||||||
|
fxFlexFill
|
||||||
|
perfectScrollbar
|
||||||
|
[itemSize]="vsItemSize"
|
||||||
|
>
|
||||||
|
<ng-container *ucapVirtualFor="let itemInfo of itemInfos">
|
||||||
|
<div [style.height.px]="vsItemSize">
|
||||||
|
<ng-template
|
||||||
|
[ngTemplateOutlet]="nodeTemplate"
|
||||||
|
[ngTemplateOutletContext]="{ $implicit: itemInfo }"
|
||||||
|
></ng-template>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
</ucap-virtual-scroll-viewport>
|
||||||
|
</div>
|
11
projects/ui-chat/src/lib/components/item-list.component.scss
Normal file
11
projects/ui-chat/src/lib/components/item-list.component.scss
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
.ucap-chat-item-list-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
ucap-virtual-scroll-viewport {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { DebugElement } from '@angular/core';
|
||||||
|
import { ChatItemListComponent } from './item-list.component';
|
||||||
|
|
||||||
|
describe('ucap::organization::ChatItemListComponent', () => {
|
||||||
|
let component: ChatItemListComponent;
|
||||||
|
let fixture: ComponentFixture<ChatItemListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ChatItemListComponent]
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ChatItemListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { moduleMetadata } from '@storybook/angular';
|
||||||
|
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { ChatItemListComponent } from './item-list.component';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'ui-chat::ChatItemListComponent',
|
||||||
|
component: ChatItemListComponent,
|
||||||
|
decorators: [
|
||||||
|
moduleMetadata({
|
||||||
|
imports: [BrowserModule, BrowserAnimationsModule],
|
||||||
|
providers: []
|
||||||
|
})
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = () => ({
|
||||||
|
component: ChatItemListComponent,
|
||||||
|
props: {}
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
@mixin ucap-chat-item-list-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-chat-item-list-container {
|
||||||
|
border-color: mat-color($foreground, secondary-text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ucap-chat-item-list-typography($config) {
|
||||||
|
.ucap-chat-item-list-container {
|
||||||
|
.searchword-container {
|
||||||
|
.form-field-input-searchword {
|
||||||
|
font-family: mat-font-family($config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
projects/ui-chat/src/lib/components/item-list.component.ts
Normal file
71
projects/ui-chat/src/lib/components/item-list.component.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { Subject } from 'rxjs';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
OnInit,
|
||||||
|
OnDestroy,
|
||||||
|
ChangeDetectionStrategy,
|
||||||
|
ChangeDetectorRef,
|
||||||
|
Input,
|
||||||
|
Directive,
|
||||||
|
ContentChild,
|
||||||
|
TemplateRef,
|
||||||
|
ViewChild,
|
||||||
|
ElementRef
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import { ObjectUtil } from '@ucap/core';
|
||||||
|
|
||||||
|
import { UserInfo, UserInfoShort, RoomInfo } from '@ucap/protocol-room';
|
||||||
|
|
||||||
|
import { VirtualScrollViewportComponent } from '@ucap/ng-ui';
|
||||||
|
|
||||||
|
export type ItemInfoTypes = UserInfo | UserInfoShort | RoomInfo;
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[ucapChatItemListNode]'
|
||||||
|
})
|
||||||
|
export class ChatItemListNodeDirective {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ucap-chat-item-list',
|
||||||
|
templateUrl: './item-list.component.html',
|
||||||
|
styleUrls: ['./item-list.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
|
})
|
||||||
|
export class ChatItemListComponent implements OnInit, OnDestroy {
|
||||||
|
@Input()
|
||||||
|
itemInfos: ItemInfoTypes[];
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
vsItemSize: number;
|
||||||
|
|
||||||
|
@ContentChild(ChatItemListNodeDirective, {
|
||||||
|
read: TemplateRef,
|
||||||
|
static: false
|
||||||
|
})
|
||||||
|
nodeTemplate: TemplateRef<ChatItemListNodeDirective>;
|
||||||
|
|
||||||
|
@ViewChild('container', { static: true })
|
||||||
|
container: ElementRef<HTMLElement>;
|
||||||
|
|
||||||
|
@ViewChild('cvsvList', { static: true })
|
||||||
|
cvsvList: VirtualScrollViewportComponent;
|
||||||
|
|
||||||
|
ObjectUtil = ObjectUtil;
|
||||||
|
|
||||||
|
private ngOnDestroySubject: Subject<void>;
|
||||||
|
|
||||||
|
constructor(private changeDetectorRef: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.ngOnDestroySubject = new Subject();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (!!this.ngOnDestroySubject) {
|
||||||
|
this.ngOnDestroySubject.next();
|
||||||
|
this.ngOnDestroySubject.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,37 +1,128 @@
|
||||||
<div class="ucap-chat-message-box-attach-file" (click)="onClickOpenViewer()">
|
<div class="chat-bubble-area">
|
||||||
<!--파일명에 따라 doc exe hwp ppt xls zip 으로 추가되고 나머지 파일 명은 file로 기간이 만료된 파일은 그뒤에 disable도 추가-->
|
<div
|
||||||
<!-- <div class="file-img" [ngClass]="fileInfo.FileExt"></div> -->
|
class="ucap-chat-message-box-attach-file-bubble"
|
||||||
<div [ngClass]="['mime-icon', 'light', 'ico-' + fileInfo.fileExt]">
|
(mouseenter)="mouseEnter($event)"
|
||||||
<div class="ico"></div>
|
(mouseleave)="mouseLeave($event)"
|
||||||
|
>
|
||||||
|
<div class="file-info-box" (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 }}
|
||||||
|
<span *ngIf="!!fileRetentionPeriod && fileRetentionPeriod > 0">
|
||||||
|
~
|
||||||
|
{{
|
||||||
|
fileInfo.attachmentRegDate
|
||||||
|
| ucapDate: 'YYYY.MM.DD':fileRetentionPeriodOptions
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="btn-box" [ngClass]="!!showOverButtons ? 'over' : ''">
|
||||||
|
<ul>
|
||||||
|
<li *ngIf="!existFile">
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
color="accent"
|
||||||
|
class="btn-file-ctrl"
|
||||||
|
(click)="onClickSave()"
|
||||||
|
aria-label="저장"
|
||||||
|
[matTooltip]="'common:file.save' | ucapI18n"
|
||||||
|
>
|
||||||
|
<mat-icon>save_alt</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="!existFile">
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
*ngIf="deviceType !== DeviceType.Web"
|
||||||
|
color="accent"
|
||||||
|
class="btn-file-ctrl"
|
||||||
|
(click)="onClickSaveAs()"
|
||||||
|
aria-label="다른 이름 저장"
|
||||||
|
[matTooltip]="'common:file.saveAs' | ucapI18n"
|
||||||
|
>
|
||||||
|
<mat-icon>save</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="!!existFile">
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
*ngIf="deviceType !== DeviceType.Web"
|
||||||
|
color="accent"
|
||||||
|
class="btn-file-ctrl"
|
||||||
|
(click)="onClickOpenFile()"
|
||||||
|
aria-label="파일열기"
|
||||||
|
[matTooltip]="'common:file.fileOpen' | ucapI18n"
|
||||||
|
>
|
||||||
|
<mat-icon class="material-icons-outlined">text_snippet</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li *ngIf="!!existFile">
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
*ngIf="deviceType !== DeviceType.Web"
|
||||||
|
color="accent"
|
||||||
|
class="btn-file-ctrl"
|
||||||
|
(click)="onClickOpenFolder()"
|
||||||
|
aria-label="폴더열기"
|
||||||
|
[matTooltip]="'common:file.folderOpen' | ucapI18n"
|
||||||
|
>
|
||||||
|
<mat-icon class="material-icons-outlined">folder</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button
|
||||||
|
mat-flat-button
|
||||||
|
color="accent"
|
||||||
|
class="btn-file-ctrl"
|
||||||
|
(click)="onClickOpenViewer()"
|
||||||
|
aria-label="뷰어 열기"
|
||||||
|
[matTooltip]="'label.openViewer' | ucapI18n"
|
||||||
|
>
|
||||||
|
<mat-icon>open_in_new</mat-icon>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="progress"
|
||||||
|
[ngClass]="
|
||||||
|
!!fileDownloadItem &&
|
||||||
|
!!fileDownloadItem.downloadingProgress$ &&
|
||||||
|
(fileDownloadItem.downloadingProgress$ | async) > 0 &&
|
||||||
|
(fileDownloadItem.downloadingProgress$ | async) < 100
|
||||||
|
? 'over'
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
aria-label="close"
|
||||||
|
class="btn-close"
|
||||||
|
color="accent"
|
||||||
|
style="display: none;"
|
||||||
|
>
|
||||||
|
<mat-icon color="accent">cancel</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-progress-bar
|
||||||
|
color="accent"
|
||||||
|
mode="determinate"
|
||||||
|
[value]="fileDownloadItem.downloadingProgress$ | async"
|
||||||
|
></mat-progress-bar>
|
||||||
|
|
||||||
|
<div class="progress-info-area">
|
||||||
|
<span>{{ 'common:file.downloading' | ucapI18n }}</span
|
||||||
|
><strong>{{ fileDownloadItem.downloadingProgress$ | async }}%</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</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>
|
</div>
|
||||||
|
|
|
@ -1,126 +1,124 @@
|
||||||
// $tablet-s-width: 768px;
|
$tablet-s-width: 768px;
|
||||||
|
|
||||||
// .bubble-main {
|
.ucap-chat-message-box-attach-file-bubble {
|
||||||
// display: flex;
|
display: flex;
|
||||||
// flex-direction: row;
|
flex-direction: row;
|
||||||
// padding: 14px;
|
align-items: center;
|
||||||
// min-width: 300px;
|
//padding: 6px 12px 0 0;
|
||||||
// .file-img {
|
//border-radius: 2px;
|
||||||
// display: inline-flex;
|
cursor: pointer;
|
||||||
// width: 50px;
|
.file-info-box {
|
||||||
// height: 50px;
|
display: flex;
|
||||||
// float: left;
|
flex-direction: row;
|
||||||
// margin-right: 14px;
|
//align-items: center;
|
||||||
// background-repeat: no-repeat;
|
//padding: 6px 12px 0 0;
|
||||||
// &.doc {
|
//border-radius: 2px;
|
||||||
// background-image: url(/assets/images/file/icon_talk_doc.png);
|
cursor: pointer;
|
||||||
// &.disable {
|
.file-info {
|
||||||
// background-image: url(/assets/images/file/icon_talk_doc_d.png);
|
//border-radius: 2px;
|
||||||
// }
|
.file-name {
|
||||||
// }
|
font-weight: 600;
|
||||||
// &.exe {
|
//line-height: 1.2;
|
||||||
// background-image: url(/assets/images/file/icon_talk_exe.png);
|
//font-size: 0.929em;
|
||||||
// &.disable {
|
}
|
||||||
// background-image: url(/assets/images/file/icon_talk_exe_d.png);
|
.date {
|
||||||
// }
|
//color: #c92b5c;
|
||||||
// }
|
//font-size: 0.875em;
|
||||||
// &.file {
|
//margin-top: 4px;
|
||||||
// background-image: url(/assets/images/file/icon_talk_file.png);
|
}
|
||||||
// &.disable {
|
}
|
||||||
// background-image: url(/assets/images/file/icon_talk_file_d.png);
|
}
|
||||||
// }
|
.btn-box {
|
||||||
// }
|
width: 100%;
|
||||||
// &.hwp {
|
height: 100%;
|
||||||
// background-image: url(/assets/images/file/icon_talk_hwp.png);
|
//background-color: rgba(0, 0, 0, 0.6);
|
||||||
// &.disable {
|
display: none;
|
||||||
// background-image: url(/assets/images/file/icon_talk_hwp_d.png);
|
//transition: all 0.2s linear;
|
||||||
// }
|
//opacity: 0;
|
||||||
// }
|
//border-radius: 2px;
|
||||||
// &.ppt,
|
cursor: default;
|
||||||
// &.pptx {
|
&.over {
|
||||||
// background-image: url(/assets/images/file/icon_talk_ppt.png);
|
position: absolute;
|
||||||
// &.disable {
|
display: block;
|
||||||
// background-image: url(/assets/images/file/icon_talk_ppt_d.png);
|
z-index: 5;
|
||||||
// }
|
top: 0;
|
||||||
// }
|
left: 0;
|
||||||
// &.xls,
|
//transition: all 0.3s linear;
|
||||||
// &.xlsm,
|
opacity: 1;
|
||||||
// &.xlsx {
|
}
|
||||||
// background-image: url(/assets/images/file/icon_talk_xls.png);
|
ul {
|
||||||
// &.disable {
|
display: flex;
|
||||||
// background-image: url(/assets/images/file/icon_talk_xls_d.png);
|
flex-direction: row;
|
||||||
// }
|
align-items: center;
|
||||||
// }
|
flex-wrap: nowrap;
|
||||||
// &.zip {
|
justify-content: center;
|
||||||
// background-image: url(/assets/images/file/icon_talk_zip.png);
|
width: 100%;
|
||||||
// &.disable {
|
height: 100%;
|
||||||
// background-image: url(/assets/images/file/icon_talk_doc_d.png);
|
li {
|
||||||
// }
|
//padding-left: 16px;
|
||||||
// }
|
&:first-child {
|
||||||
// }
|
padding-left: 0;
|
||||||
// .file-info {
|
}
|
||||||
// display: inline-flex;
|
.btn-file-ctrl {
|
||||||
// flex-direction: column;
|
//@include matbtnCtrl(36px, 36px, 50%, 36px);
|
||||||
// text-align: left;
|
width: 36px;
|
||||||
// float: left;
|
}
|
||||||
// line-height: 1.6em;
|
}
|
||||||
// .file-name {
|
}
|
||||||
// font-size: 1em;
|
}
|
||||||
// font-weight: bold;
|
.progress {
|
||||||
// display: inline-flex;
|
//display: flex;
|
||||||
// }
|
display: none;
|
||||||
// .file-size {
|
flex-direction: column;
|
||||||
// display: inline-flex;
|
justify-content: center;
|
||||||
// font-size: 11px;
|
width: 100%;
|
||||||
// color: #666666;
|
height: 100%;
|
||||||
// }
|
//background-color: rgba(0, 0, 0, 0.6);
|
||||||
// .file-ext {
|
padding: 0 20px;
|
||||||
// font-size: 12px;
|
border-radius: 2px;
|
||||||
// color: #848d95;
|
cursor: default;
|
||||||
// }
|
&:before {
|
||||||
// }
|
content: '';
|
||||||
// }
|
//width: 16px;
|
||||||
// .btn-box {
|
// height: 16px;
|
||||||
// width: 100%;
|
position: absolute;
|
||||||
// height: 40px;
|
z-index: 0;
|
||||||
// border-top: 1px solid #dddddd;
|
//right: 22px;
|
||||||
// display: flex;
|
//top: calc(50% - 25px);
|
||||||
// width: 100%;
|
//background-color: $white;
|
||||||
// text-align: center;
|
display: inline-block;
|
||||||
// font-size: 1em;
|
//border-radius: 50%;
|
||||||
// ul {
|
}
|
||||||
// width: 100%;
|
&.over {
|
||||||
// li {
|
display: flex;
|
||||||
// width: 50%;
|
position: absolute;
|
||||||
// height: 100%;
|
z-index: 5;
|
||||||
// display: inline-block;
|
top: 0;
|
||||||
// text-align: center;
|
left: 0;
|
||||||
// align-items: center;
|
}
|
||||||
// border-right: 1px solid #dddddd;
|
.btn-close {
|
||||||
// @media screen and (max-width: #{$tablet-s-width}) {
|
align-self: flex-end;
|
||||||
// width: 30%;
|
//@include matbtnCtrl(20px, 20px, 0, 20px);
|
||||||
// }
|
// width: 20px;
|
||||||
// &:last-child {
|
//margin-bottom: 6px;
|
||||||
// border-right: none;
|
}
|
||||||
// @media screen and (max-width: #{$tablet-s-width}) {
|
.progress-info-area {
|
||||||
// width: 70%;
|
display: flex;
|
||||||
// }
|
flex-direction: row;
|
||||||
// }
|
justify-content: space-between;
|
||||||
// .mat-button {
|
//color: $white;
|
||||||
// width: 100%;
|
//font-size: 0.875em;
|
||||||
// display: block;
|
//margin-top: 4px;
|
||||||
// height: 100%;
|
//font-weight: 600;
|
||||||
// font-size: 1em;
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
// &.expired {
|
.desibled {
|
||||||
// li {
|
.ucap-file-bubble {
|
||||||
// width: 100%;
|
.ucap-chat-message-box-attach-file {
|
||||||
// white-space: nowrap;
|
// color: #999 !important;
|
||||||
// color: #999999;
|
// background: #f1f2f6 !important;
|
||||||
// align-items: center;
|
}
|
||||||
// line-height: 40px;
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
|
||||||
import { FileEventJson } from '@ucap/protocol-event';
|
import { FileEventJson } from '@ucap/protocol-event';
|
||||||
|
import { DateOptions } from '@ucap/ng-ui';
|
||||||
|
import { FileDownloadItem } from '@ucap/api';
|
||||||
|
import { DeviceType } from '@ucap/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-chat-message-box-attach-file',
|
selector: 'ucap-chat-message-box-attach-file',
|
||||||
|
@ -10,26 +13,83 @@ export class AttachFileComponent implements OnInit {
|
||||||
@Input()
|
@Input()
|
||||||
fileInfo: FileEventJson;
|
fileInfo: FileEventJson;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
existFile = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
deviceType: DeviceType;
|
||||||
|
|
||||||
@Input()
|
@Input()
|
||||||
expired = false;
|
expired = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileRetentionPeriod?: number;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
save = new EventEmitter<string>();
|
save = new EventEmitter<string>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openFile = new EventEmitter();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openFolder = new EventEmitter();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
openViewer = new EventEmitter();
|
openViewer = new EventEmitter();
|
||||||
|
|
||||||
|
showOverButtons = false;
|
||||||
|
|
||||||
|
DeviceType = DeviceType;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
ngOnInit() {}
|
ngOnInit() {}
|
||||||
|
|
||||||
onClickSave() {
|
onClickSave() {
|
||||||
this.save.emit('save');
|
if (!this.expired) {
|
||||||
|
this.save.emit('save');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onClickSaveAs() {
|
onClickSaveAs() {
|
||||||
this.save.emit('saveAs');
|
if (!this.expired) {
|
||||||
|
this.save.emit('saveAs');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClickOpenFile() {
|
||||||
|
if (!this.expired) {
|
||||||
|
this.openFile.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClickOpenFolder() {
|
||||||
|
if (!this.expired) {
|
||||||
|
this.openFolder.emit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
onClickOpenViewer() {
|
onClickOpenViewer() {
|
||||||
this.openViewer.emit();
|
if (!this.expired) {
|
||||||
|
this.openViewer.emit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mouseEnter(event: MouseEvent): void {
|
||||||
|
if (!this.expired) {
|
||||||
|
this.showOverButtons = true;
|
||||||
|
}
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
mouseLeave(event: MouseEvent): void {
|
||||||
|
this.showOverButtons = false;
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
get fileRetentionPeriodOptions(): DateOptions {
|
||||||
|
return {
|
||||||
|
manipulate: { amount: this.fileRetentionPeriod, unit: 'days' }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
@import '~@ucap/ng-ui-material/material';
|
||||||
|
|
||||||
|
.ucap-chat-message-box-date-splitter {
|
||||||
|
display: flex;
|
||||||
|
align-content: center;
|
||||||
|
flex-flow: row;
|
||||||
|
//height: 30px;
|
||||||
|
position: relative;
|
||||||
|
justify-content: center;
|
||||||
|
//margin: 20px 0;
|
||||||
|
&:before {
|
||||||
|
content: '';
|
||||||
|
// height: 1px;
|
||||||
|
// background-color: $gray-re3;
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 3;
|
||||||
|
// top: 50%;
|
||||||
|
}
|
||||||
|
.date {
|
||||||
|
min-width: 160px;
|
||||||
|
// font-size: 0.979em;
|
||||||
|
// text-align: center;
|
||||||
|
// font-weight: 600;
|
||||||
|
// color: $gray-re3;
|
||||||
|
position: relative;
|
||||||
|
z-index: 5;
|
||||||
|
// padding: 0 36px;
|
||||||
|
// align-self: center;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,8 +6,14 @@
|
||||||
<ucap-chat-message-box-attach-file
|
<ucap-chat-message-box-attach-file
|
||||||
*ngSwitchCase="FileType.File"
|
*ngSwitchCase="FileType.File"
|
||||||
[fileInfo]="fileInfo"
|
[fileInfo]="fileInfo"
|
||||||
|
[fileDownloadItem]="fileDownloadItem"
|
||||||
[expired]="getExpiredFile()"
|
[expired]="getExpiredFile()"
|
||||||
|
[deviceType]="deviceType"
|
||||||
|
[existFile]="existFile"
|
||||||
|
[fileRetentionPeriod]="fileRetentionPeriod"
|
||||||
(openViewer)="onClickFileViewer(fileInfo)"
|
(openViewer)="onClickFileViewer(fileInfo)"
|
||||||
|
(openFile)="onClickOpenFile(fileInfo)"
|
||||||
|
(openFolder)="onClickOpenFolder(fileInfo)"
|
||||||
(save)="onSave($event)"
|
(save)="onSave($event)"
|
||||||
>
|
>
|
||||||
</ucap-chat-message-box-attach-file>
|
</ucap-chat-message-box-attach-file>
|
||||||
|
@ -15,8 +21,14 @@
|
||||||
<ucap-chat-message-box-attach-file
|
<ucap-chat-message-box-attach-file
|
||||||
*ngSwitchCase="FileType.Sound"
|
*ngSwitchCase="FileType.Sound"
|
||||||
[fileInfo]="fileInfo"
|
[fileInfo]="fileInfo"
|
||||||
|
[fileDownloadItem]="fileDownloadItem"
|
||||||
[expired]="getExpiredFile()"
|
[expired]="getExpiredFile()"
|
||||||
|
[deviceType]="deviceType"
|
||||||
|
[existFile]="existFile"
|
||||||
|
[fileRetentionPeriod]="fileRetentionPeriod"
|
||||||
(openViewer)="onClickFileViewer(fileInfo)"
|
(openViewer)="onClickFileViewer(fileInfo)"
|
||||||
|
(openFile)="onClickOpenFile(fileInfo)"
|
||||||
|
(openFolder)="onClickOpenFolder(fileInfo)"
|
||||||
(save)="onSave($event)"
|
(save)="onSave($event)"
|
||||||
>
|
>
|
||||||
</ucap-chat-message-box-attach-file>
|
</ucap-chat-message-box-attach-file>
|
||||||
|
@ -25,6 +37,7 @@
|
||||||
*ngSwitchCase="FileType.Image"
|
*ngSwitchCase="FileType.Image"
|
||||||
[fileInfo]="fileInfo"
|
[fileInfo]="fileInfo"
|
||||||
[expired]="getExpiredFile()"
|
[expired]="getExpiredFile()"
|
||||||
|
[fileRetentionPeriod]="fileRetentionPeriod"
|
||||||
(openViewer)="onClickFileViewer(fileInfo)"
|
(openViewer)="onClickFileViewer(fileInfo)"
|
||||||
(save)="onSave($event)"
|
(save)="onSave($event)"
|
||||||
></ucap-chat-message-box-image>
|
></ucap-chat-message-box-image>
|
||||||
|
@ -32,8 +45,14 @@
|
||||||
<ucap-chat-message-box-video
|
<ucap-chat-message-box-video
|
||||||
*ngSwitchCase="FileType.Video"
|
*ngSwitchCase="FileType.Video"
|
||||||
[fileInfo]="fileInfo"
|
[fileInfo]="fileInfo"
|
||||||
|
[fileDownloadItem]="fileDownloadItem"
|
||||||
[expired]="getExpiredFile()"
|
[expired]="getExpiredFile()"
|
||||||
|
[deviceType]="deviceType"
|
||||||
|
[existFile]="existFile"
|
||||||
|
[fileRetentionPeriod]="fileRetentionPeriod"
|
||||||
(openViewer)="onClickFileViewer(fileInfo)"
|
(openViewer)="onClickFileViewer(fileInfo)"
|
||||||
|
(openFile)="onClickOpenFile(fileInfo)"
|
||||||
|
(openFolder)="onClickOpenFolder(fileInfo)"
|
||||||
(save)="onSave($event)"
|
(save)="onSave($event)"
|
||||||
></ucap-chat-message-box-video>
|
></ucap-chat-message-box-video>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { Component, OnInit, Output, Input, EventEmitter } from '@angular/core';
|
||||||
import { FileType, Info, FileEventJson } from '@ucap/protocol-event';
|
import { FileType, Info, FileEventJson } from '@ucap/protocol-event';
|
||||||
import { RoomInfo } from '@ucap/protocol-room';
|
import { RoomInfo } from '@ucap/protocol-room';
|
||||||
import { FileDownloadItem, StatusCode } from '@ucap/api';
|
import { FileDownloadItem, StatusCode } from '@ucap/api';
|
||||||
|
import { SelectFileInfo } from '@ucap/ng-ui';
|
||||||
|
import { DeviceType } from '@ucap/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ucap-chat-message-box-file',
|
selector: 'ucap-chat-message-box-file',
|
||||||
|
@ -15,14 +17,30 @@ export class FileComponent implements OnInit {
|
||||||
@Input()
|
@Input()
|
||||||
roomInfo: RoomInfo;
|
roomInfo: RoomInfo;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
existFile = false;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
deviceType?: DeviceType;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
fileRetentionPeriod?: number;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
save = new EventEmitter<{
|
save = new EventEmitter<{
|
||||||
fileInfo: FileEventJson;
|
fileInfo: FileEventJson;
|
||||||
fileDownloadItem: FileDownloadItem;
|
fileDownloadItem: FileDownloadItem;
|
||||||
type: string;
|
type: string;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
fileViewer = new EventEmitter<FileEventJson>();
|
fileViewer = new EventEmitter<SelectFileInfo>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openFile = new EventEmitter<FileEventJson>();
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
openFolder = new EventEmitter<FileEventJson>();
|
||||||
|
|
||||||
fileInfo?: FileEventJson;
|
fileInfo?: FileEventJson;
|
||||||
fileDownloadItem: FileDownloadItem;
|
fileDownloadItem: FileDownloadItem;
|
||||||
|
@ -55,7 +73,7 @@ export class FileComponent implements OnInit {
|
||||||
|
|
||||||
onClickFileViewer(fileInfo: FileEventJson) {
|
onClickFileViewer(fileInfo: FileEventJson) {
|
||||||
if (!this.getExpiredFile()) {
|
if (!this.getExpiredFile()) {
|
||||||
this.fileViewer.emit(fileInfo);
|
this.fileViewer.emit({ attachmentSeq: this.fileInfo.attachmentSeq });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,4 +86,14 @@ export class FileComponent implements OnInit {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onClickOpenFile(fileInfo: FileEventJson): void {
|
||||||
|
if (!this.getExpiredFile()) {
|
||||||
|
this.openFile.emit(fileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClickOpenFolder(fileInfo: FileEventJson): void {
|
||||||
|
if (!this.getExpiredFile()) {
|
||||||
|
this.openFolder.emit(fileInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user