Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6326b5fef6 | |||
| 4f2c149f5a | |||
| 48d620e878 | |||
| 92644d9008 | |||
| 2598e5864e | |||
| 8d2df73484 | |||
| 44575a8a08 | |||
| c82d784866 |
214
package-lock.json
generated
214
package-lock.json
generated
@@ -5,25 +5,25 @@
|
|||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/architect": {
|
"@angular-devkit/architect": {
|
||||||
"version": "0.1000.2",
|
"version": "0.1000.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1000.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1000.3.tgz",
|
||||||
"integrity": "sha512-n1e/ZdE70C6hDauTWLAiofKDI6m88nhb91Ddqum0eeUh3HL3Ppv/sT9O+UYsab3gIdIOFJwHBpZ96SM/2Ja5NA==",
|
"integrity": "sha512-8ZszTAkRvGGMXERFvyLT6SJPfJXjNNfHamA76uDPTBXy+EijJ1XVTUr1+SYEe73E4ovtxqxAnsApEFxS7/Ni5w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"rxjs": "6.5.5"
|
"rxjs": "6.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular-devkit/build-angular": {
|
"@angular-devkit/build-angular": {
|
||||||
"version": "0.1000.2",
|
"version": "0.1000.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1000.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1000.3.tgz",
|
||||||
"integrity": "sha512-Cl7JaXkE1OMpagMwPPNGq7IGy50p2F3R0iZFi38imq661YqH/FFv0SdbMqmpAHaIvvr6E1HJt5ltoLNERQWFjg==",
|
"integrity": "sha512-r3KJj39AwkYYzbixSM095l4fOGvhyByr0XvmAEu0l5dGGdL4tNXywvgXkNhEVRDo0jZYpTMegiTqzOik/9YCDw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/architect": "0.1000.2",
|
"@angular-devkit/architect": "0.1000.3",
|
||||||
"@angular-devkit/build-optimizer": "0.1000.2",
|
"@angular-devkit/build-optimizer": "0.1000.3",
|
||||||
"@angular-devkit/build-webpack": "0.1000.2",
|
"@angular-devkit/build-webpack": "0.1000.3",
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"@babel/core": "7.9.6",
|
"@babel/core": "7.9.6",
|
||||||
"@babel/generator": "7.9.6",
|
"@babel/generator": "7.9.6",
|
||||||
"@babel/plugin-transform-runtime": "7.9.6",
|
"@babel/plugin-transform-runtime": "7.9.6",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"@babel/runtime": "7.9.6",
|
"@babel/runtime": "7.9.6",
|
||||||
"@babel/template": "7.8.6",
|
"@babel/template": "7.8.6",
|
||||||
"@jsdevtools/coverage-istanbul-loader": "3.0.3",
|
"@jsdevtools/coverage-istanbul-loader": "3.0.3",
|
||||||
"@ngtools/webpack": "10.0.2",
|
"@ngtools/webpack": "10.0.3",
|
||||||
"ajv": "6.12.2",
|
"ajv": "6.12.2",
|
||||||
"autoprefixer": "9.8.0",
|
"autoprefixer": "9.8.0",
|
||||||
"babel-loader": "8.1.0",
|
"babel-loader": "8.1.0",
|
||||||
@@ -88,9 +88,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular-devkit/build-optimizer": {
|
"@angular-devkit/build-optimizer": {
|
||||||
"version": "0.1000.2",
|
"version": "0.1000.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1000.3.tgz",
|
||||||
"integrity": "sha512-wbrgJQw92+A7kFaG7U0F9MMzhVI32tcIdr26+SFXWGAeBaWIkBfMs/jfGLlEYESLqQQF5oMn7LJBwXu+nkPHvw==",
|
"integrity": "sha512-6mFoubg08UCWC0fE2mGoawEt2R1VlGStvUNAP2PRCjoj1ZySa1NnVYoKk65cyAAA3K2o7vSoDZesNq1uABjZbg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"loader-utils": "2.0.0",
|
"loader-utils": "2.0.0",
|
||||||
@@ -100,20 +100,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular-devkit/build-webpack": {
|
"@angular-devkit/build-webpack": {
|
||||||
"version": "0.1000.2",
|
"version": "0.1000.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1000.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1000.3.tgz",
|
||||||
"integrity": "sha512-x1fHnZFTwvAE3lB6XnlJmf0KNiiAsZKGdUuTXqzgsgh34A/aFOWtu0EB6cw6lvifMj1ioDT8Zjp8N89Lh5AtEA==",
|
"integrity": "sha512-+vmn9d9THFubSWS28K1+nElUfOrhT576ptVZMd0a5S24momV8loW3J8iBOBfnGal/P86ZCAyP46kSirlAzH9Jg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/architect": "0.1000.2",
|
"@angular-devkit/architect": "0.1000.3",
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"rxjs": "6.5.5"
|
"rxjs": "6.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular-devkit/core": {
|
"@angular-devkit/core": {
|
||||||
"version": "10.0.2",
|
"version": "10.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.0.3.tgz",
|
||||||
"integrity": "sha512-gD8iUP28GscsHqGKKu+NtX97bt7aXDZmIYqWTv4SThrcsPYY2A4tBw+NBbNAwjHkNfn4jflpqTmcN9gBAoLoSg==",
|
"integrity": "sha512-m27ogjq44j80x64RnEswSvy8UewUqeCVJBbEuY6fzrWoaiCf12sgPlrSCwjwfhtQrLgl1e/i9zYA7U6ulGRXyg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "6.12.2",
|
"ajv": "6.12.2",
|
||||||
@@ -124,28 +124,28 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular-devkit/schematics": {
|
"@angular-devkit/schematics": {
|
||||||
"version": "10.0.2",
|
"version": "10.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-10.0.3.tgz",
|
||||||
"integrity": "sha512-BUUWAreHoxk4Z91Xhr+wtFUhK6+7AOczsIxlZmJnQwSUhN2ZdiDjOXeO3NFkDdL8CtNOz5khGu2iNBqn08FpUw==",
|
"integrity": "sha512-TjA2ZSPCgUK9l4FiRTIQY7DceXMAvNzOMWffy9o3kv2HPtxG9kuBrQXk++Z99zpylK0cAsugV7t/5ANpUkrIiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"ora": "4.0.4",
|
"ora": "4.0.4",
|
||||||
"rxjs": "6.5.5"
|
"rxjs": "6.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/animations": {
|
"@angular/animations": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-10.0.4.tgz",
|
||||||
"integrity": "sha512-s7hob0TmSRqsgEWdB0EYsKY8eF8iD6Sj44XntecAhEc/IxVQGIIZiNwGvW43Lb8iEdbvdF+GJfgxu5I8ZWMX3Q==",
|
"integrity": "sha512-UzQiWhDHY6wixS1Nh+Jwpzq1weiLGXJPt3Pa4pETpt3Hg7MIUu62dik6OFWuGYQPbn9DJYH+CH+sRxN1GCVjww==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/cdk": {
|
"@angular/cdk": {
|
||||||
"version": "10.0.2",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-10.1.0.tgz",
|
||||||
"integrity": "sha512-ZvZkeh7qXllAGSOFvWSYurCslflTmB0JD3gonmUKOBl/O/AcOTPntP+iOrUaC3+eR3ohsfL5SswxChW0NF+oHQ==",
|
"integrity": "sha512-zSZcpsfhRWdNAzNXnKZIlaX1uAWY+8W2zV7ktQNJoNypo9X02rY0YtmPBzlPjT0ITjM4EqohZ07nfd+5bLUw4A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"parse5": "^5.0.0",
|
"parse5": "^5.0.0",
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
@@ -160,16 +160,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/cli": {
|
"@angular/cli": {
|
||||||
"version": "10.0.2",
|
"version": "10.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-10.0.3.tgz",
|
||||||
"integrity": "sha512-L/uLUrZNIwbYzIeU9R3SC2hblDgtxP57msmRjoOQBpSzwlOME+z0wlCXPv+h9NOzNPvVVbEtLtjBgZxUw0IHzg==",
|
"integrity": "sha512-ONK8YG20KuakQetY0lPKDAOA3uBoLurdpSfFspFkcECyDimwJYSEydi3FUnCxEexeoKvrQWcol+q+u9YPoHCyg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/architect": "0.1000.2",
|
"@angular-devkit/architect": "0.1000.3",
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"@angular-devkit/schematics": "10.0.2",
|
"@angular-devkit/schematics": "10.0.3",
|
||||||
"@schematics/angular": "10.0.2",
|
"@schematics/angular": "10.0.3",
|
||||||
"@schematics/update": "0.1000.2",
|
"@schematics/update": "0.1000.3",
|
||||||
"@yarnpkg/lockfile": "1.1.0",
|
"@yarnpkg/lockfile": "1.1.0",
|
||||||
"ansi-colors": "4.1.1",
|
"ansi-colors": "4.1.1",
|
||||||
"debug": "4.1.1",
|
"debug": "4.1.1",
|
||||||
@@ -202,25 +202,25 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/common": {
|
"@angular/common": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/common/-/common-10.0.4.tgz",
|
||||||
"integrity": "sha512-R2q/Vt07PHgcmvZBVGcYjf6K2xERCHWUYQYN1HsgjVQUu5ypvE7Kqs+6s0BfIoBKc+ejKmjMHHumnm+89O+gXg==",
|
"integrity": "sha512-9DJMD8GgHz7i2fMz0f1IHZlDSIz83uE6fcH8SIq1iCSWT2dubRRpCX000VpIyhAgfkCgdNCYXQ7VGNsZceoagQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/compiler": {
|
"@angular/compiler": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-10.0.4.tgz",
|
||||||
"integrity": "sha512-SmzMYxBprs8bVS9WctiCDj6KVp3jZFEaxGTbC/FwtdvesMIdOktpHggUeJW+OzUTwTnYVmKrm6d8rdg3QSaXFQ==",
|
"integrity": "sha512-1rnEmSHJtrKC1QD+PyF36xwMnSqG4Slgi5+PYk3BxIa5vbWBibrYijtd/uCNhscfPSpfb06MVM2mRsrc+BmbQg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/compiler-cli": {
|
"@angular/compiler-cli": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-10.0.4.tgz",
|
||||||
"integrity": "sha512-XK2xQX0RLr7QMkEseDhcdqZ2hMM5n1Q6LFvOJfxQXXBpa7DBC9mB6HLsn0vrLgwaAfz+SEQ7pLDgXQSy2tmJUg==",
|
"integrity": "sha512-uZKk6Ab4Pw8qcaXhpORuEoCbPSHWx3TWcs9OXIKIhzOOoMMe9OSt2SzOkHCrySSaik1IhQODnHww7sGRw5mxwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"canonical-path": "1.0.0",
|
"canonical-path": "1.0.0",
|
||||||
@@ -401,49 +401,57 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/core": {
|
"@angular/core": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/core/-/core-10.0.4.tgz",
|
||||||
"integrity": "sha512-EfWAz5StlPYo2ZtvVzeoNlGrFAXRncwGd/CExbLFOZx4HcDXVkATw5d4vnKHmmKacDqnbuvMD2M0Tl0EJi5q4g==",
|
"integrity": "sha512-lA8RDagJ/O0gUX95h00+nnUZs/1QmmhAVWVtHcuI12ueC836tJhLtPGnEx9ib9NXrgRyNwb8lO1xJPmmuQgdQQ==",
|
||||||
|
"requires": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@angular/flex-layout": {
|
||||||
|
"version": "10.0.0-beta.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-10.0.0-beta.32.tgz",
|
||||||
|
"integrity": "sha512-JvuY4dUoy5jyCTIrFiq7n30Znakh1pD3nbg0h0hs2r3t1OiDQb0ZSI1wcumosG/vYHsuJQTuNhbfaIZzA1x8nA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/forms": {
|
"@angular/forms": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-10.0.4.tgz",
|
||||||
"integrity": "sha512-08XlERBGVnzea4sVY7J1JsU6zX/ENGZGB9V4K1jpX6gNjlAk2AbskC76+ngdsgdzv0neNEqPoLdMAZerNECnUg==",
|
"integrity": "sha512-SJSYZCfHua9fi/dks1q+ad9OGBblRUn3Q1V4B7r99fJYr39qRiIHcegikhY4h8H3Wk1bJRGJG7iXmxJhjWXK3Q==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/material": {
|
"@angular/material": {
|
||||||
"version": "10.0.2",
|
"version": "10.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/material/-/material-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/material/-/material-10.1.0.tgz",
|
||||||
"integrity": "sha512-xG8lCG+QS+r61aDoBauH6RyNuOHai4yxhn3e8cb2ua86liarJmF9jqlW0tB49tE1ZLz9U/+ybJKkos2kX1eF1Q==",
|
"integrity": "sha512-zHJxMHYAyfJbhXGhsWUFTABBBQVzWNexuJGWh19MQx0jQn7aLek5nYQ0oLG00+ArISVAg/XMOtnbdRaemFWVzw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/platform-browser": {
|
"@angular/platform-browser": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-10.0.4.tgz",
|
||||||
"integrity": "sha512-f6JsFMunJNn7fPtwwWbZsXZ/JFA5RlwbyHKKkLAPMLLJwabOcSh/bUpPWKoRMHpUxr53PGEX4XY6QNPJXrUaag==",
|
"integrity": "sha512-iaZ8pFS5XUgPCO6/C47TzSFPzlzgleayq099cVOOx9z0t/SwUCSKt4AdAVhyQ8RTnx6l1JmmwBgRaXpScZlqzg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/platform-browser-dynamic": {
|
"@angular/platform-browser-dynamic": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.0.4.tgz",
|
||||||
"integrity": "sha512-ASZWymDVCgmDe0XE1djGKrfJnUR65PdfoZ51CQ7xSVJTnXp6kCAXzFeG2IyqU+O7IXvQRgvb4GOA4Y6GScNVKw==",
|
"integrity": "sha512-RoUMqYhUwF6+Mvk/aH0IZQ1D0SDEi9k5EZlx9CJ3RvNuKygk7to+S4vMWVpGxFQlwdS3bytRLKi+Kki6f4nLkg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular/router": {
|
"@angular/router": {
|
||||||
"version": "10.0.3",
|
"version": "10.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-10.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@angular/router/-/router-10.0.4.tgz",
|
||||||
"integrity": "sha512-WMNJ4yS3xvgOPa73hKQNHoTCT2l+bvompxdqZSqN7dh92QtPJBXCm+qBC2jRDj4wgP7Eq5dgymh7a/dZqseFEQ==",
|
"integrity": "sha512-iDLWdmltU5pZ6M/fBKC5Kg2o9Aqb1YJ+oHXFu186BQAl2RNeNCmMQ0VaCxjpMgD/MoSxpuRuGQ6rRrCSFCxtcQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
}
|
}
|
||||||
@@ -1556,12 +1564,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@ngtools/webpack": {
|
"@ngtools/webpack": {
|
||||||
"version": "10.0.2",
|
"version": "10.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-10.0.3.tgz",
|
||||||
"integrity": "sha512-Py8jkc6UIHtp5TKKAMkNiKhx0goL+d7RkQEBWIvO+9e5fBGIt0Npy3dBoJ9gRldaGIjLZWlHhGsgeaYbq5dlvA==",
|
"integrity": "sha512-0TuvYMCLtsApLtCHXeDBYGEoAQXzsRLpgFxPM5W7CGcj0ecthZO4NYrMAt+J8ky//KmbxqQSFHWmss2cbirIPA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"enhanced-resolve": "4.1.1",
|
"enhanced-resolve": "4.1.1",
|
||||||
"rxjs": "6.5.5",
|
"rxjs": "6.5.5",
|
||||||
"webpack-sources": "1.4.3"
|
"webpack-sources": "1.4.3"
|
||||||
@@ -1611,23 +1619,23 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@schematics/angular": {
|
"@schematics/angular": {
|
||||||
"version": "10.0.2",
|
"version": "10.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-10.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-10.0.3.tgz",
|
||||||
"integrity": "sha512-viSf1HQH2vxZAonkfdAjJ/0Z8VgKptsi4UYtJDwabKRbbyp2EQ/vhEPI/ddV4QgrQqR8mkOl7nu8JqL2M0+v6A==",
|
"integrity": "sha512-Or2pCqjpPbAvmbxtfMosGwQbNbSL4xodK5Key7678ZAPGB+rcxrVkBI9yxEJ/qzF/LrmMoKqy0JCmVLK7Grpog==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"@angular-devkit/schematics": "10.0.2"
|
"@angular-devkit/schematics": "10.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@schematics/update": {
|
"@schematics/update": {
|
||||||
"version": "0.1000.2",
|
"version": "0.1000.3",
|
||||||
"resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1000.2.tgz",
|
"resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1000.3.tgz",
|
||||||
"integrity": "sha512-6PSJ+WexBqY0ESo0Kp6fa9wPWAXKE9oyLNjISJ1S9QKCVowoHa8FV+7BalVOUYwUhnUzwPCF6n7PqcJiZR632g==",
|
"integrity": "sha512-Nncdklmzi1tyzkoAh7GlSslxriRhftlmfqPVmFHrrPRttYACtT/QH5qcWsrPgTPpHGINYEHrPjpeljsMoMchBQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/core": "10.0.2",
|
"@angular-devkit/core": "10.0.3",
|
||||||
"@angular-devkit/schematics": "10.0.2",
|
"@angular-devkit/schematics": "10.0.3",
|
||||||
"@yarnpkg/lockfile": "1.1.0",
|
"@yarnpkg/lockfile": "1.1.0",
|
||||||
"ini": "1.3.5",
|
"ini": "1.3.5",
|
||||||
"npm-package-arg": "^8.0.0",
|
"npm-package-arg": "^8.0.0",
|
||||||
@@ -2899,9 +2907,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001100",
|
"version": "1.0.30001104",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001100.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001104.tgz",
|
||||||
"integrity": "sha512-0eYdp1+wFCnMlCj2oudciuQn2B9xAFq3WpgpcBIZTxk/1HNA/O2YA7rpeYhnOqsqAJq1AHUgx6i1jtafg7m2zA==",
|
"integrity": "sha512-pkpCg7dmI/a7WcqM2yfdOiT4Xx5tzyoHAXWsX5/HxZ3TemwDZs0QXdqbE0UPLPVy/7BeK7693YfzfRYfu1YVpg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"canonical-path": {
|
"canonical-path": {
|
||||||
@@ -3027,9 +3035,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cli-spinners": {
|
"cli-spinners": {
|
||||||
"version": "2.3.0",
|
"version": "2.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz",
|
||||||
"integrity": "sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==",
|
"integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"cli-width": {
|
"cli-width": {
|
||||||
@@ -4324,9 +4332,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.498",
|
"version": "1.3.501",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.498.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.501.tgz",
|
||||||
"integrity": "sha512-W1hGwaQEU8j9su2jeAr3aabkPuuXw+j8t73eajGAkEJWbfWiwbxBwQN/8Qmv2qCy3uCDm2rOAaZneYQM8VGC4w==",
|
"integrity": "sha512-tyzuKaV2POw2mtqBBzQGNBojMZzH0MRu8bT8T/50x+hWeucyG/9pkgAATy+PcM2ySNM9+8eG2VllY9c6j4i+bg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
@@ -6990,9 +6998,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"less": {
|
"less": {
|
||||||
"version": "3.12.0",
|
"version": "3.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/less/-/less-3.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz",
|
||||||
"integrity": "sha512-3mmSHFRP9hGxxQgAKgChfau1LO3ksV/zyZf1qd2ENyBV778NA9Ids99wFRA20jE+5prT7oScKod8PoGlxSe1gg==",
|
"integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"errno": "^0.1.1",
|
"errno": "^0.1.1",
|
||||||
@@ -7687,9 +7695,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"native-request": {
|
"native-request": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.7.tgz",
|
||||||
"integrity": "sha512-7wU3DvBGAJQxWuMR3F62zrhB7hxNj2DdlC/eBVrCgavc6+ZpFZOqS/PsR7QyUPLMkFk0GvvzoeeOAZGLLnObnA==",
|
"integrity": "sha512-9nRjinI9bmz+S7dgNtf4A70+/vPhnd+2krGpy4SUlADuOuSa24IDkNaZ+R/QT1wQ6S8jBdi6wE7fLekFZNfUpQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
@@ -8692,9 +8700,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"portfinder": {
|
"portfinder": {
|
||||||
"version": "1.0.26",
|
"version": "1.0.27",
|
||||||
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz",
|
"resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.27.tgz",
|
||||||
"integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==",
|
"integrity": "sha512-bJ3U3MThKnyJ9Dx1Idtm5pQmxXqw08+XOHhi/Lie8OF1OlhVaBFhsntAIhkZYjfDcCzszSr0w1yCbccThhzgxQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "^2.6.2",
|
"async": "^2.6.2",
|
||||||
@@ -12018,9 +12026,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"typescript": {
|
"typescript": {
|
||||||
"version": "3.9.6",
|
"version": "3.9.7",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
|
||||||
"integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==",
|
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ua-parser-js": {
|
"ua-parser-js": {
|
||||||
|
|||||||
31
package.json
31
package.json
@@ -11,28 +11,29 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "~10.0.3",
|
"@angular/animations": "^10.0.4",
|
||||||
"@angular/cdk": "^10.0.2",
|
"@angular/cdk": "^10.1.0",
|
||||||
"@angular/common": "~10.0.3",
|
"@angular/common": "^10.0.4",
|
||||||
"@angular/compiler": "~10.0.3",
|
"@angular/compiler": "^10.0.4",
|
||||||
"@angular/core": "~10.0.3",
|
"@angular/core": "^10.0.4",
|
||||||
"@angular/forms": "~10.0.3",
|
"@angular/flex-layout": "^10.0.0-beta.32",
|
||||||
"@angular/material": "^10.0.2",
|
"@angular/forms": "^10.0.4",
|
||||||
"@angular/platform-browser": "~10.0.3",
|
"@angular/material": "^10.1.0",
|
||||||
"@angular/platform-browser-dynamic": "~10.0.3",
|
"@angular/platform-browser": "^10.0.4",
|
||||||
"@angular/router": "~10.0.3",
|
"@angular/platform-browser-dynamic": "^10.0.4",
|
||||||
|
"@angular/router": "^10.0.4",
|
||||||
"rxjs": "~6.5.5",
|
"rxjs": "~6.5.5",
|
||||||
"socket.io": "^2.3.0",
|
"socket.io": "^2.3.0",
|
||||||
"tslib": "^2.0.0",
|
"tslib": "^2.0.0",
|
||||||
"zone.js": "~0.10.3"
|
"zone.js": "~0.10.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular-devkit/build-angular": "~0.1000.2",
|
"@angular-devkit/build-angular": "^0.1000.3",
|
||||||
"@angular/cli": "~10.0.2",
|
"@angular/cli": "^10.0.3",
|
||||||
"@angular/compiler-cli": "~10.0.3",
|
"@angular/compiler-cli": "^10.0.4",
|
||||||
"@types/node": "^12.11.1",
|
|
||||||
"@types/jasmine": "~3.5.0",
|
"@types/jasmine": "~3.5.0",
|
||||||
"@types/jasminewd2": "~2.0.3",
|
"@types/jasminewd2": "~2.0.3",
|
||||||
|
"@types/node": "^12.11.1",
|
||||||
"codelyzer": "^6.0.0",
|
"codelyzer": "^6.0.0",
|
||||||
"jasmine-core": "~3.5.0",
|
"jasmine-core": "~3.5.0",
|
||||||
"jasmine-spec-reporter": "~5.0.0",
|
"jasmine-spec-reporter": "~5.0.0",
|
||||||
@@ -44,6 +45,6 @@
|
|||||||
"protractor": "~7.0.0",
|
"protractor": "~7.0.0",
|
||||||
"ts-node": "~8.3.0",
|
"ts-node": "~8.3.0",
|
||||||
"tslint": "~6.1.0",
|
"tslint": "~6.1.0",
|
||||||
"typescript": "~3.9.5"
|
"typescript": "^3.9.7"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/app/account/account.service.spec.ts
Normal file
16
src/app/account/account.service.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { AccountService } from './account.service';
|
||||||
|
|
||||||
|
describe('AccountService', () => {
|
||||||
|
let service: AccountService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(AccountService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
45
src/app/account/account.service.ts
Normal file
45
src/app/account/account.service.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient, HttpParams } from '@angular/common/http';
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import { User } from './user';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AccountService {
|
||||||
|
private userSubject: BehaviorSubject<User>;
|
||||||
|
public user: Observable<User>;
|
||||||
|
|
||||||
|
constructor(private httpClient: HttpClient) {
|
||||||
|
this.userSubject = new BehaviorSubject<User>(JSON.parse(localStorage.getItem('user')));
|
||||||
|
this.user = this.userSubject.asObservable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get userValue() {
|
||||||
|
return this.userSubject.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
login(username, password) {
|
||||||
|
const body = new HttpParams()
|
||||||
|
.set('username', username)
|
||||||
|
.set('password', password);
|
||||||
|
return this.httpClient.post<User>(environment.apiUrl + '/login', body)
|
||||||
|
.pipe((map(user => {
|
||||||
|
localStorage.setItem('user', JSON.stringify(user));
|
||||||
|
this.userSubject.next(user);
|
||||||
|
return user;
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
register(user) {
|
||||||
|
const body = new HttpParams()
|
||||||
|
.set('username', user.username)
|
||||||
|
.set('password', user.password)
|
||||||
|
.set('email', user.email);
|
||||||
|
return this.httpClient.post(environment.apiUrl + '/signup', body);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
src/app/account/auth.guard.spec.ts
Normal file
7
src/app/account/auth.guard.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { AuthGuard } from './auth.guard';
|
||||||
|
|
||||||
|
describe('Auth.Guard', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new AuthGuard()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
21
src/app/account/auth.guard.ts
Normal file
21
src/app/account/auth.guard.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
||||||
|
import { AccountService } from './account.service';
|
||||||
|
|
||||||
|
@Injectable({providedIn: 'root'})
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
constructor(private router: Router,
|
||||||
|
private accountService: AccountService) { }
|
||||||
|
|
||||||
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||||
|
const user = this.accountService.userValue;
|
||||||
|
|
||||||
|
if (user) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.router.navigate(['/login'], {queryParams: {returnURL: state.url}});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
16
src/app/account/jwt.interceptor.spec.ts
Normal file
16
src/app/account/jwt.interceptor.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { JwtInterceptor } from './jwt.interceptor';
|
||||||
|
|
||||||
|
describe('JwtInterceptor', () => {
|
||||||
|
beforeEach(() => TestBed.configureTestingModule({
|
||||||
|
providers: [
|
||||||
|
JwtInterceptor
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
const interceptor: JwtInterceptor = TestBed.inject(JwtInterceptor);
|
||||||
|
expect(interceptor).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
31
src/app/account/jwt.interceptor.ts
Normal file
31
src/app/account/jwt.interceptor.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import {
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpInterceptor
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
import { AccountService } from './account.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtInterceptor implements HttpInterceptor {
|
||||||
|
|
||||||
|
constructor(private accountService: AccountService) {}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
|
const user = this.accountService.userValue;
|
||||||
|
const isLoggedIn = user && user.token;
|
||||||
|
const isApiUrl = request.url.startsWith(environment.apiUrl);
|
||||||
|
if (isLoggedIn && isApiUrl) {
|
||||||
|
request = request.clone({
|
||||||
|
setHeaders: {
|
||||||
|
Authorization: `Bearer ${user.token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/app/account/login/login.component.css
Normal file
22
src/app/account/login/login.component.css
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
.login-card {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
padding: 10px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-button {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
30
src/app/account/login/login.component.html
Normal file
30
src/app/account/login/login.component.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<div class="login-container" fxLayout="row" fxLayoutAlign="center center">
|
||||||
|
<mat-card class="login-card" fxLayout="column" fxLayoutAlign="center center">
|
||||||
|
<mat-card-title>Login</mat-card-title>
|
||||||
|
<mat-card-content>
|
||||||
|
<form class="login-form" [formGroup]="form" (ngSubmit)="onLogin()">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>User name</mat-label>
|
||||||
|
<label>
|
||||||
|
<input matInput formControlName="username" required>
|
||||||
|
</label>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Password</mat-label>
|
||||||
|
<label>
|
||||||
|
<input matInput formControlName="password" type="password" required minlength="15">
|
||||||
|
</label>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div fxLayout="row" fxLayoutAlign="space-evenly center">
|
||||||
|
<button mat-flat-button color="primary" [disabled]="loading">
|
||||||
|
<span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
<a mat-button routerLink="/signup">Sign up</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
25
src/app/account/login/login.component.spec.ts
Normal file
25
src/app/account/login/login.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { LoginComponent } from './login.component';
|
||||||
|
|
||||||
|
describe('LoginComponent', () => {
|
||||||
|
let component: LoginComponent;
|
||||||
|
let fixture: ComponentFixture<LoginComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ LoginComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(LoginComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
43
src/app/account/login/login.component.ts
Normal file
43
src/app/account/login/login.component.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, Validators} from '@angular/forms';
|
||||||
|
import { AccountService } from '../account.service';
|
||||||
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-login',
|
||||||
|
templateUrl: './login.component.html',
|
||||||
|
styleUrls: ['./login.component.css']
|
||||||
|
})
|
||||||
|
export class LoginComponent implements OnInit {
|
||||||
|
form = this.formBuilder.group({
|
||||||
|
username: ['', Validators.required],
|
||||||
|
password: ['', [Validators.required, Validators.minLength(15)]],
|
||||||
|
});
|
||||||
|
loading = false;
|
||||||
|
returnUrl = this.activatedRoute.snapshot.queryParams['returnUrl'] || '/';
|
||||||
|
|
||||||
|
onLogin() {
|
||||||
|
this.loading = true;
|
||||||
|
this.accountService.login(this.form.get('username').value, this.form.get('password').value)
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe(data => {
|
||||||
|
this.router.navigate([this.returnUrl]);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// TODO error handling
|
||||||
|
console.log(error);
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private accountService: AccountService,
|
||||||
|
private activatedRoute: ActivatedRoute,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import { PasswordErrorStateMatcher } from './password-error-state-matcher';
|
||||||
|
|
||||||
|
describe('PasswordErrorStateMatcher', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new PasswordErrorStateMatcher()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
11
src/app/account/signup/password-error-state-matcher.ts
Normal file
11
src/app/account/signup/password-error-state-matcher.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { ErrorStateMatcher } from '@angular/material/core';
|
||||||
|
import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
|
||||||
|
|
||||||
|
export class PasswordErrorStateMatcher implements ErrorStateMatcher {
|
||||||
|
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
|
||||||
|
const invalidControl = !!(control && control.invalid && control.parent.touched);
|
||||||
|
const invalidParent = !!(control && control.parent && control.parent.invalid && control.parent.touched);
|
||||||
|
|
||||||
|
return invalidControl || invalidParent;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/app/account/signup/signup.component.css
Normal file
22
src/app/account/signup/signup.component.css
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
.register-card {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-form {
|
||||||
|
padding: 10px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-button {
|
||||||
|
width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
48
src/app/account/signup/signup.component.html
Normal file
48
src/app/account/signup/signup.component.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<div class="register-container" fxLayout="row" fxLayoutAlign="center center">
|
||||||
|
<mat-card class="register-card" fxLayout="column" fxLayoutAlign="center center">
|
||||||
|
<mat-card-title>Sign up</mat-card-title>
|
||||||
|
<mat-card-content>
|
||||||
|
<form class="register-form" [formGroup]="form" (ngSubmit)="onRegister()">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>User name</mat-label>
|
||||||
|
<label>
|
||||||
|
<input matInput formControlName="username" required>
|
||||||
|
</label>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Email</mat-label>
|
||||||
|
<label>
|
||||||
|
<input matInput formControlName="email" required email>
|
||||||
|
</label>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div formGroupName="password">
|
||||||
|
<mat-form-field appearance="fill" hintLabel="At least 15 characters.">
|
||||||
|
<mat-label>Password</mat-label>
|
||||||
|
<label>
|
||||||
|
<input matInput formControlName="first" type="password" required minlength="15">
|
||||||
|
</label>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Confirm password</mat-label>
|
||||||
|
<label>
|
||||||
|
<input matInput formControlName="second" type="password" required minlength="15"
|
||||||
|
[errorStateMatcher]="passwordErrorStateMatcher">
|
||||||
|
</label>
|
||||||
|
<mat-error *ngIf="form.get('password').hasError('mismatch')">Passwords don't match.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div fxLayout="row" fxLayoutAlign="space-evenly center">
|
||||||
|
<button mat-flat-button color="primary" [disabled]="loading">
|
||||||
|
<span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span>
|
||||||
|
Sign up
|
||||||
|
</button>
|
||||||
|
<a mat-button routerLink="/login">Login</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
25
src/app/account/signup/signup.component.spec.ts
Normal file
25
src/app/account/signup/signup.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SignupComponent } from './signup.component';
|
||||||
|
|
||||||
|
describe('RegisterComponent', () => {
|
||||||
|
let component: SignupComponent;
|
||||||
|
let fixture: ComponentFixture<SignupComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SignupComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SignupComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
65
src/app/account/signup/signup.component.ts
Normal file
65
src/app/account/signup/signup.component.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, ValidationErrors, Validators } from '@angular/forms';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
import { first } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { AccountService } from '../account.service';
|
||||||
|
import { PasswordErrorStateMatcher } from './password-error-state-matcher';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-register',
|
||||||
|
templateUrl: './signup.component.html',
|
||||||
|
styleUrls: ['./signup.component.css']
|
||||||
|
})
|
||||||
|
export class SignupComponent implements OnInit {
|
||||||
|
form = this.formBuilder.group({
|
||||||
|
username: ['', Validators.required],
|
||||||
|
email: ['', [Validators.required, Validators.email]],
|
||||||
|
password: this.formBuilder.group({
|
||||||
|
first: ['', [Validators.required, Validators.minLength(15)]],
|
||||||
|
second: ['', [Validators.required, Validators.minLength(15)]],
|
||||||
|
},
|
||||||
|
{ validators: this.passwordsEqual }),
|
||||||
|
});
|
||||||
|
loading = false;
|
||||||
|
passwordErrorStateMatcher = new PasswordErrorStateMatcher();
|
||||||
|
|
||||||
|
constructor(private accountService: AccountService,
|
||||||
|
private formBuilder: FormBuilder,
|
||||||
|
private router: Router,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
passwordsEqual(passwords: FormGroup): ValidationErrors | null {
|
||||||
|
const password1 = passwords.get('first');
|
||||||
|
const password2 = passwords.get('second');
|
||||||
|
|
||||||
|
return password1 && password2 && password1.value === password2.value ? null : { mismatch: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
readForm() {
|
||||||
|
return {
|
||||||
|
username: this.form.get('username').value,
|
||||||
|
email: this.form.get('email').value,
|
||||||
|
password: this.form.get('password').get('first').value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onRegister() {
|
||||||
|
if (this.form.invalid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
this.accountService.register(this.readForm())
|
||||||
|
.pipe(first())
|
||||||
|
.subscribe(data => {
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
// TODO error handling
|
||||||
|
console.log(error);
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void { }
|
||||||
|
}
|
||||||
7
src/app/account/user.spec.ts
Normal file
7
src/app/account/user.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { User } from './user';
|
||||||
|
|
||||||
|
describe('User', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new User()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
7
src/app/account/user.ts
Normal file
7
src/app/account/user.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export class User {
|
||||||
|
id: number;
|
||||||
|
character: string;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
token: string;
|
||||||
|
}
|
||||||
@@ -1,8 +1,22 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { Routes, RouterModule } from '@angular/router';
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { AuthGuard } from './account/auth.guard';
|
||||||
|
import { LoginComponent } from './account/login/login.component';
|
||||||
|
import { SignupComponent } from './account/signup/signup.component';
|
||||||
|
|
||||||
const routes: Routes = [];
|
|
||||||
|
const gameModule = () => import('./game/game.module').then(x => x.GameModule);
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: '', component: AppComponent, canActivate: [AuthGuard] },
|
||||||
|
// { path: '', redirectTo: '/game', pathMatch: 'prefix', canActivate: [AuthGuard] },
|
||||||
|
{ path: 'login', component: LoginComponent },
|
||||||
|
{ path: 'signup', component: SignupComponent },
|
||||||
|
{ path: 'game', loadChildren: gameModule, canActivate: [AuthGuard] },
|
||||||
|
{ path: '**', redirectTo: '' },
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [RouterModule.forRoot(routes)],
|
imports: [RouterModule.forRoot(routes)],
|
||||||
|
|||||||
@@ -16,69 +16,6 @@ svg.material-icons:not(:last-child) {
|
|||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card svg.material-icons path {
|
|
||||||
fill: #888;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
/* margin-top: 16px; */
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #eee;
|
|
||||||
background-color: #fafafa;
|
|
||||||
height: 40px;
|
|
||||||
width: 200px;
|
|
||||||
margin: 0 8px 16px;
|
|
||||||
padding: 16px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
line-height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(:last-child) {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.card-small {
|
|
||||||
height: 16px;
|
|
||||||
width: 168px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card):hover {
|
|
||||||
transform: translateY(-3px);
|
|
||||||
box-shadow: 0 4px 17px rgba(0, 0, 0, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-container .card:not(.highlight-card):hover .material-icons path {
|
|
||||||
fill: rgb(105, 103, 103);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.highlight-card {
|
|
||||||
background-color: #1976d2;
|
|
||||||
color: white;
|
|
||||||
font-weight: 600;
|
|
||||||
border: none;
|
|
||||||
width: auto;
|
|
||||||
min-width: 30%;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card.card.highlight-card span {
|
|
||||||
margin-left: 60px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive Styles */
|
/* Responsive Styles */
|
||||||
@media screen and (max-width: 767px) {
|
@media screen and (max-width: 767px) {
|
||||||
|
|
||||||
@@ -93,6 +30,10 @@ svg.material-icons:not(:last-child) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.router {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.chat {
|
.chat {
|
||||||
float: right;
|
float: right;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,20 +1,3 @@
|
|||||||
<div class="chat">
|
<div class="router">
|
||||||
<app-chat></app-chat>
|
<router-outlet></router-outlet>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="card-container">
|
|
||||||
<div class="card card-small" (click)="onClickSocket()" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
|
|
||||||
<span>Send test message</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card card-small" (click)="onClickApi()" tabindex="0">
|
|
||||||
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
|
||||||
|
|
||||||
<span>Call rest api</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<router-outlet></router-outlet>
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { SocketService } from './socket/socket.service';
|
import { SocketService } from './socket/socket.service';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { AccountService } from './account/account.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@@ -10,22 +10,14 @@ import { HttpClient } from '@angular/common/http';
|
|||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
title = 'rona-frontend';
|
title = 'rona-frontend';
|
||||||
|
|
||||||
onClickSocket() {
|
|
||||||
this.socketService.send('test', {'user': 'USERNAME', 'payload': 'PAYLOAD TEST'})
|
|
||||||
}
|
|
||||||
|
|
||||||
onClickApi() {
|
|
||||||
this.httpService.get('http://localhost:5005/').subscribe(response => {
|
|
||||||
console.log('REST API call returned: ', response);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
* @param accountService
|
||||||
* @param socketService
|
* @param socketService
|
||||||
* @param httpService
|
|
||||||
*/
|
*/
|
||||||
constructor(private socketService: SocketService,
|
constructor(private accountService: AccountService,
|
||||||
private httpService: HttpClient) { }
|
private socketService: SocketService,
|
||||||
|
) { }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,47 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
||||||
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
|
||||||
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { LoginComponent } from './account/login/login.component';
|
||||||
import { ChatComponent } from './chat/chat.component';
|
import { SignupComponent } from './account/signup/signup.component';
|
||||||
import { EntryComponent } from './chat/entry/entry.component';
|
import { GameModule } from './game/game.module';
|
||||||
import { InputComponent } from './chat/input/input.component';
|
import { JwtInterceptor } from './account/jwt.interceptor';
|
||||||
import {MatCardModule} from '@angular/material/card';
|
|
||||||
import {MatInputModule} from '@angular/material/input';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
ChatComponent,
|
LoginComponent,
|
||||||
EntryComponent,
|
SignupComponent,
|
||||||
InputComponent
|
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
|
||||||
HttpClientModule,
|
HttpClientModule,
|
||||||
AppRoutingModule,
|
BrowserModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatInputModule
|
MatIconModule,
|
||||||
|
MatInputModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
AppRoutingModule,
|
||||||
|
GameModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
|
||||||
|
// fakeBackendProvider,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
],
|
],
|
||||||
providers: [],
|
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#chat-log {
|
#chat-log {
|
||||||
height: calc(99% - 34px);
|
height: calc(99% - 34px);
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
background-color: goldenrod;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
<div id="chat-log">
|
<div id="chat-log">
|
||||||
<app-entry *ngFor="let entry of entries"
|
<div *ngFor="let entry of entries">
|
||||||
[entry]=entry>
|
<div [ngSwitch]="entry.type">
|
||||||
</app-entry>
|
<app-entry *ngSwitchCase="'messages'" [entry]="entry"></app-entry>
|
||||||
|
<app-system-entry *ngSwitchCase="'system'" [entry]="entry"></app-system-entry>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<app-input></app-input>
|
<app-input></app-input>
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Entry } from './entry/entry';
|
|
||||||
import { Message } from './message';
|
import { Message } from './message';
|
||||||
import {SocketService} from '../socket/socket.service';
|
import { SystemMessage } from './entry/entry';
|
||||||
|
import { Messages } from './entry/messages/messages';
|
||||||
|
import { SocketService } from '../socket/socket.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-chat',
|
selector: 'app-chat',
|
||||||
@@ -10,28 +12,39 @@ import {SocketService} from '../socket/socket.service';
|
|||||||
})
|
})
|
||||||
export class ChatComponent implements OnInit {
|
export class ChatComponent implements OnInit {
|
||||||
|
|
||||||
entries = new Array<Entry>();
|
entries = new Array<Messages|SystemMessage>();
|
||||||
|
|
||||||
public addMessage(message: Message): void {
|
public addMessage(message: Message): void {
|
||||||
if ((this.entries.length > 0)
|
if ((this.entries.length > 0)
|
||||||
&& (this.entries[this.entries.length - 1].character == message.sender)) {
|
&& (this.entries[this.entries.length - 1].type === 'messages')) {
|
||||||
this.entries[this.entries.length - 1].messages.push(message.message);
|
let entry = this.entries[this.entries.length - 1] as Messages;
|
||||||
|
if (entry.character == message.character) {
|
||||||
|
entry.messages.push(message);
|
||||||
} else {
|
} else {
|
||||||
this.entries.push(new Entry(message.sender, 'Aangular User', message.message));
|
this.entries.push(new Messages(message));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.entries.push(new Messages(message));
|
||||||
}
|
}
|
||||||
window.setTimeout(ChatComponent.scrollToBottom, 5);
|
window.setTimeout(ChatComponent.scrollToBottom, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public addSystemMessage(message: SystemMessage): void {
|
||||||
|
this.entries.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
static scrollToBottom() {
|
static scrollToBottom() {
|
||||||
const chatLog = document.getElementById('chat-log');
|
const chatLog = document.getElementById('chat-log');
|
||||||
chatLog.scrollTop = chatLog.scrollHeight;
|
chatLog.scrollTop = chatLog.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private socketService: SocketService) {
|
constructor(private socketService: SocketService) {
|
||||||
socketService.onTestMessage().subscribe((message: Message) => {
|
socketService.onPublicMessage().subscribe((message: Message) => {
|
||||||
console.log(message);
|
|
||||||
this.addMessage(message);
|
this.addMessage(message);
|
||||||
});
|
});
|
||||||
|
socketService.onSystemMessage().subscribe((message: SystemMessage) => {
|
||||||
|
this.addSystemMessage(message);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
|||||||
29
src/app/chat/chat.module.ts
Normal file
29
src/app/chat/chat.module.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
|
||||||
|
import { ChatComponent } from './chat.component';
|
||||||
|
import { SystemMessageComponent } from './entry/system-message/system-message.component';
|
||||||
|
import { MessagesComponent } from './entry/messages/messages.component';
|
||||||
|
import { InputComponent } from './input/input.component';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
ChatComponent,
|
||||||
|
InputComponent,
|
||||||
|
MessagesComponent,
|
||||||
|
SystemMessageComponent
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
ChatComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
MatCardModule,
|
||||||
|
MatInputModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ChatModule { }
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import { Component, Input, OnInit } from '@angular/core';
|
|
||||||
import { Entry } from './entry';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-entry',
|
|
||||||
templateUrl: './entry.component.html',
|
|
||||||
styleUrls: ['./entry.component.css']
|
|
||||||
})
|
|
||||||
export class EntryComponent implements OnInit {
|
|
||||||
|
|
||||||
@Input() entry: Entry;
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Entry } from './entry';
|
import { Messages } from './entry';
|
||||||
|
|
||||||
describe('Entry', () => {
|
describe('Entry', () => {
|
||||||
it('should create an instance', () => {
|
it('should create an instance', () => {
|
||||||
expect(new Entry()).toBeTruthy();
|
expect(new Messages()).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,25 @@
|
|||||||
export class Entry {
|
export abstract class Entry {
|
||||||
|
public timestamp;
|
||||||
|
|
||||||
public messages: Array<string> = new Array<string>();
|
protected constructor() {
|
||||||
|
this.timestamp = new Date();
|
||||||
constructor(public character: string,
|
|
||||||
public user: string,
|
|
||||||
message: string) {
|
|
||||||
this.messages.push(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SystemMessage extends Entry {
|
||||||
|
|
||||||
|
constructor(public message: string,
|
||||||
|
public severity: SeverityEnum) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'system'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum SeverityEnum {
|
||||||
|
info = 'info',
|
||||||
|
warning = 'warning',
|
||||||
|
error = 'error',
|
||||||
|
}
|
||||||
|
|||||||
6
src/app/chat/entry/messages/messages.component.css
Normal file
6
src/app/chat/entry/messages/messages.component.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.eye {
|
||||||
|
margin-right: 4px;
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
border: solid 1px;
|
||||||
|
}
|
||||||
@@ -3,13 +3,14 @@
|
|||||||
<div mat-card-avatar class="avatar"></div>
|
<div mat-card-avatar class="avatar"></div>
|
||||||
<mat-card-title class="character">
|
<mat-card-title class="character">
|
||||||
{{entry.character}}
|
{{entry.character}}
|
||||||
<div class="timestamp">23:31</div>
|
<div class="timestamp">{{entry.timestamp | date:'HH:mm' }}</div>
|
||||||
</mat-card-title>
|
</mat-card-title>
|
||||||
<mat-card-subtitle class="user">played by {{entry.user}}</mat-card-subtitle>
|
<mat-card-subtitle class="user">{{entry.user}}</mat-card-subtitle>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<div class="messages" *ngFor="let message of entry.messages">
|
<div class="messages" *ngFor="let message of entry.messages">
|
||||||
{{message}}
|
<div>{{message.message}}</div>
|
||||||
|
<span *ngFor="let eye of message.eyes" class="eye">{{eye}}</span><span *ngIf="message.result">→ {{message.result}}</span>
|
||||||
</div>
|
</div>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { EntryComponent } from './entry.component';
|
import { MessagesComponent } from './messages.component';
|
||||||
|
|
||||||
describe('EntryComponent', () => {
|
describe('EntryComponent', () => {
|
||||||
let component: EntryComponent;
|
let component: MessagesComponent;
|
||||||
let fixture: ComponentFixture<EntryComponent>;
|
let fixture: ComponentFixture<MessagesComponent>;
|
||||||
|
|
||||||
beforeEach(async(() => {
|
beforeEach(async(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
declarations: [ EntryComponent ]
|
declarations: [ MessagesComponent ]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(EntryComponent);
|
fixture = TestBed.createComponent(MessagesComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
18
src/app/chat/entry/messages/messages.component.ts
Normal file
18
src/app/chat/entry/messages/messages.component.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { Messages } from './messages';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-entry',
|
||||||
|
templateUrl: './messages.component.html',
|
||||||
|
styleUrls: ['../entry.component.css', './messages.component.css']
|
||||||
|
})
|
||||||
|
export class MessagesComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() entry: Messages;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
7
src/app/chat/entry/messages/messages.spec.ts
Normal file
7
src/app/chat/entry/messages/messages.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Messages } from './messages';
|
||||||
|
|
||||||
|
describe('Messages', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new Messages()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
23
src/app/chat/entry/messages/messages.ts
Normal file
23
src/app/chat/entry/messages/messages.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { Entry } from '../entry';
|
||||||
|
import { Message } from '../../message';
|
||||||
|
|
||||||
|
export class Messages extends Entry {
|
||||||
|
public messages: Array<Message> = new Array<Message>();
|
||||||
|
|
||||||
|
constructor(message: Message) {
|
||||||
|
super();
|
||||||
|
this.messages.push(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get character(): string {
|
||||||
|
return this.messages[0].character;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get type(): string {
|
||||||
|
return 'messages'
|
||||||
|
}
|
||||||
|
|
||||||
|
public get user(): string {
|
||||||
|
return this.messages[0].user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.system-avatar {
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-header class="header">
|
||||||
|
<div mat-card-avatar class="system-avatar">
|
||||||
|
<img [alt]="entry.severity" src="../../../../assets/build_circle-24px.svg">
|
||||||
|
</div>
|
||||||
|
<mat-card-title class="character">
|
||||||
|
{{entry.severity | titlecase}}
|
||||||
|
<div class="timestamp">{{entry.timestamp | date:'HH:mm' }}</div>
|
||||||
|
</mat-card-title>
|
||||||
|
<mat-card-subtitle class="user">System</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
{{entry.message}}
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { SystemMessageComponent } from './system-message.component';
|
||||||
|
|
||||||
|
describe('SystemEntryComponent', () => {
|
||||||
|
let component: SystemMessageComponent;
|
||||||
|
let fixture: ComponentFixture<SystemMessageComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SystemMessageComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SystemMessageComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Component, Input, OnInit } from '@angular/core';
|
||||||
|
import { SystemMessage } from '../entry';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-system-entry',
|
||||||
|
templateUrl: './system-message.component.html',
|
||||||
|
styleUrls: ['../entry.component.css']
|
||||||
|
})
|
||||||
|
export class SystemMessageComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() entry: SystemMessage;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import {SocketService} from '../../socket/socket.service';
|
|
||||||
import {Message} from '../message';
|
import { Message } from '../message';
|
||||||
import {Events} from '../../socket/events-enum';
|
import { Events } from '../../socket/events-enum';
|
||||||
|
import { SocketService } from '../../socket/socket.service';
|
||||||
|
import { AccountService } from '../../account/account.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-input',
|
selector: 'app-input',
|
||||||
@@ -11,11 +13,15 @@ import {Events} from '../../socket/events-enum';
|
|||||||
export class InputComponent implements OnInit {
|
export class InputComponent implements OnInit {
|
||||||
|
|
||||||
onEnter(value: string): void {
|
onEnter(value: string): void {
|
||||||
const message = new Message('Aangular Frontend', value);
|
if (value.length > 0) {
|
||||||
|
const user = this.accountService.userValue;
|
||||||
|
const message = new Message(user.character, user.username, value);
|
||||||
this.socketService.send(Events.publicMessage, message);
|
this.socketService.send(Events.publicMessage, message);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
constructor(private socketService: SocketService) { }
|
constructor(private accountService: AccountService,
|
||||||
|
private socketService: SocketService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
export class Message {
|
export class Message {
|
||||||
|
|
||||||
constructor(public sender: string,
|
public eyes?: Array<number>;
|
||||||
|
|
||||||
|
public result?: number;
|
||||||
|
|
||||||
|
constructor(public character: string,
|
||||||
|
public user: string,
|
||||||
public message: string) {
|
public message: string) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/app/game/game-routing.module.ts
Normal file
18
src/app/game/game-routing.module.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { Routes, RouterModule } from '@angular/router';
|
||||||
|
import { GameComponent } from './game.component';
|
||||||
|
import { TestComponent } from './test/test.component';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{ path: '', component: GameComponent,
|
||||||
|
children: [
|
||||||
|
{ path: '', redirectTo: 'test', pathMatch: 'prefix' },
|
||||||
|
{ path: 'test', component: TestComponent },
|
||||||
|
]}
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class GameRoutingModule { }
|
||||||
7
src/app/game/game.component.css
Normal file
7
src/app/game/game.component.css
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#game-items {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navbar {
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
11
src/app/game/game.component.html
Normal file
11
src/app/game/game.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div fxLayout="row" id="game-items">
|
||||||
|
<div fxFlex="48px" id="navbar">
|
||||||
|
<app-navbar></app-navbar>
|
||||||
|
</div>
|
||||||
|
<div fxFlex>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</div>
|
||||||
|
<div fxFlex="20%">
|
||||||
|
<app-chat></app-chat>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
25
src/app/game/game.component.spec.ts
Normal file
25
src/app/game/game.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { GameComponent } from './game.component';
|
||||||
|
|
||||||
|
describe('GameComponent', () => {
|
||||||
|
let component: GameComponent;
|
||||||
|
let fixture: ComponentFixture<GameComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ GameComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(GameComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
15
src/app/game/game.component.ts
Normal file
15
src/app/game/game.component.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-game',
|
||||||
|
templateUrl: './game.component.html',
|
||||||
|
styleUrls: ['./game.component.css']
|
||||||
|
})
|
||||||
|
export class GameComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
31
src/app/game/game.module.ts
Normal file
31
src/app/game/game.module.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FlexModule } from '@angular/flex-layout';
|
||||||
|
|
||||||
|
import { MatCardModule } from '@angular/material/card';
|
||||||
|
|
||||||
|
import { GameRoutingModule } from './game-routing.module';
|
||||||
|
import { GameComponent } from './game.component';
|
||||||
|
import { NavbarComponent } from './navbar/navbar.component';
|
||||||
|
import { TestComponent } from './test/test.component';
|
||||||
|
import { ChatModule } from '../chat/chat.module';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
GameComponent,
|
||||||
|
NavbarComponent,
|
||||||
|
TestComponent,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
NavbarComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
GameRoutingModule,
|
||||||
|
FlexModule,
|
||||||
|
MatCardModule,
|
||||||
|
ChatModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class GameModule { }
|
||||||
8
src/app/game/navbar/navbar.component.css
Normal file
8
src/app/game/navbar/navbar.component.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.nav-img {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
margin: 2px 4px;
|
||||||
|
}
|
||||||
11
src/app/game/navbar/navbar.component.html
Normal file
11
src/app/game/navbar/navbar.component.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<div fxLayout="column" fxLayoutAlign=" center">
|
||||||
|
<a routerLink="test">
|
||||||
|
<img class="nav-img" src="assets/build_circle-24px.svg" alt="TODO">
|
||||||
|
</a>
|
||||||
|
<a routerLink="test">
|
||||||
|
<img class="nav-img" src="assets/build_circle-24px.svg" alt="TODO">
|
||||||
|
</a>
|
||||||
|
<a routerLink="test">
|
||||||
|
<img class="nav-img" src="assets/build_circle-24px.svg" alt="TODO">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
25
src/app/game/navbar/navbar.component.spec.ts
Normal file
25
src/app/game/navbar/navbar.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { NavbarComponent } from './navbar.component';
|
||||||
|
|
||||||
|
describe('NavbarComponent', () => {
|
||||||
|
let component: NavbarComponent;
|
||||||
|
let fixture: ComponentFixture<NavbarComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ NavbarComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(NavbarComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
15
src/app/game/navbar/navbar.component.ts
Normal file
15
src/app/game/navbar/navbar.component.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-navbar',
|
||||||
|
templateUrl: './navbar.component.html',
|
||||||
|
styleUrls: ['./navbar.component.css']
|
||||||
|
})
|
||||||
|
export class NavbarComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
0
src/app/game/test/test.component.css
Normal file
0
src/app/game/test/test.component.css
Normal file
15
src/app/game/test/test.component.html
Normal file
15
src/app/game/test/test.component.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card card-small" (click)="onClickSocket()" tabindex="0">
|
||||||
|
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||||
|
|
||||||
|
<span>Send test message</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card card-small" (click)="onClickApi()" tabindex="0">
|
||||||
|
<svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
|
||||||
|
|
||||||
|
<span>Call rest api</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
25
src/app/game/test/test.component.spec.ts
Normal file
25
src/app/game/test/test.component.spec.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { TestComponent } from './test.component';
|
||||||
|
|
||||||
|
describe('TestComponent', () => {
|
||||||
|
let component: TestComponent;
|
||||||
|
let fixture: ComponentFixture<TestComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ TestComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(TestComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
29
src/app/game/test/test.component.ts
Normal file
29
src/app/game/test/test.component.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import {SocketService} from '../../socket/socket.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-test',
|
||||||
|
templateUrl: './test.component.html',
|
||||||
|
styleUrls: ['./test.component.css']
|
||||||
|
})
|
||||||
|
export class TestComponent implements OnInit {
|
||||||
|
|
||||||
|
onClickSocket() {
|
||||||
|
this.socketService.send('test', {'user': 'USERNAME', 'payload': 'PAYLOAD TEST'})
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickApi() {
|
||||||
|
this.httpService.get('http://localhost:5005/').subscribe(response => {
|
||||||
|
console.log('REST API call returned: ', response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private httpService: HttpClient,
|
||||||
|
private socketService: SocketService,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export enum Events {
|
export enum Events {
|
||||||
publicMessage = 'public message'
|
publicMessage = 'public message',
|
||||||
|
systemMessage = 'system message',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { Observable } from 'rxjs';
|
|||||||
import * as socketIo from 'socket.io-client';
|
import * as socketIo from 'socket.io-client';
|
||||||
|
|
||||||
import { Events } from './events-enum';
|
import { Events } from './events-enum';
|
||||||
|
import { SystemMessage } from '../chat/entry/entry';
|
||||||
|
import { Message } from '../chat/message';
|
||||||
|
|
||||||
const SERVER_URL = 'http://localhost:5005'
|
const SERVER_URL = 'http://localhost:5005'
|
||||||
|
|
||||||
@@ -26,12 +28,21 @@ export class SocketService implements OnInit {
|
|||||||
this.socket.emit(event, message);
|
this.socket.emit(event, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onTestMessage(): Observable<any> {
|
public onPublicMessage(): Observable<Message> {
|
||||||
return new Observable<any>(observer => {
|
return new Observable<Message>(observer => {
|
||||||
this.socket.on(Events.publicMessage, (data) => observer.next(data));
|
this.socket.on(Events.publicMessage, (data) => observer.next(data));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onSystemMessage(): Observable<SystemMessage> {
|
||||||
|
return new Observable<SystemMessage>(observer => {
|
||||||
|
this.socket.on(Events.systemMessage, (data: SystemMessage) => {
|
||||||
|
data = Object.assign(SystemMessage, data);
|
||||||
|
observer.next(new SystemMessage(data.message, data.severity));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.initSocket();
|
this.initSocket();
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/app/utils/fake-backend.spec.ts
Normal file
7
src/app/utils/fake-backend.spec.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { FakeBackend } from './fake-backend';
|
||||||
|
|
||||||
|
describe('FakeBackend', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
expect(new FakeBackend()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
75
src/app/utils/fake-backend.ts
Normal file
75
src/app/utils/fake-backend.ts
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||||
|
import { Observable, of, throwError } from 'rxjs';
|
||||||
|
import { delay, mergeMap, materialize, dematerialize } from 'rxjs/operators';
|
||||||
|
|
||||||
|
|
||||||
|
let users = JSON.parse(localStorage.getItem('users')) || [];
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class FakeBackend implements HttpInterceptor {
|
||||||
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
const { url, method, headers, body } = request;
|
||||||
|
|
||||||
|
return of(null)
|
||||||
|
.pipe(mergeMap(handleRoute))
|
||||||
|
.pipe(materialize())
|
||||||
|
.pipe(delay(500))
|
||||||
|
.pipe(dematerialize());
|
||||||
|
|
||||||
|
function handleRoute() {
|
||||||
|
switch (true) {
|
||||||
|
case url.endsWith('fake_login') && method === 'POST':
|
||||||
|
return authenticate();
|
||||||
|
case url.endsWith('fake_registration') && method === 'POST':
|
||||||
|
return register();
|
||||||
|
default:
|
||||||
|
return next.handle(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function authenticate() {
|
||||||
|
const { username, password } = body;
|
||||||
|
const user = users.find(x => x.username === username.value && x.password === password.value);
|
||||||
|
if (!user) {
|
||||||
|
console.log(password);
|
||||||
|
return error('Username or password is incorrect.');
|
||||||
|
}
|
||||||
|
return ok({
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
character: user.character,
|
||||||
|
token: 'fake-jwt-token',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function register() {
|
||||||
|
const user = body;
|
||||||
|
|
||||||
|
if (users.find(x => x.username === user.username)) {
|
||||||
|
return error('Username ' + user.username + ' is already taken.');
|
||||||
|
}
|
||||||
|
|
||||||
|
user.id = users.length ? Math.max(...users.map(x => x.id)) + 1 : 1;
|
||||||
|
user.character = 'placeholder';
|
||||||
|
users.push(user);
|
||||||
|
localStorage.setItem('users', JSON.stringify(users));
|
||||||
|
console.log('Register user: ' + user);
|
||||||
|
return ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ok(body?) {
|
||||||
|
return of(new HttpResponse({ status: 200, body }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function error(message) {
|
||||||
|
return throwError({ error: { message } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fakeBackendProvider = {
|
||||||
|
provide: HTTP_INTERCEPTORS,
|
||||||
|
useClass: FakeBackend,
|
||||||
|
multi: true,
|
||||||
|
}
|
||||||
1
src/assets/build_circle-24px.svg
Normal file
1
src/assets/build_circle-24px.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24" viewBox="0 0 24 24" width="24"><g><rect fill="none" height="24" width="24"/><rect fill="none" height="24" width="24"/></g><g><g><path d="M12,2C6.48,2,2,6.48,2,12c0,5.52,4.48,10,10,10s10-4.48,10-10 C22,6.48,17.52,2,12,2z M16.54,15.85l-0.69,0.69c-0.39,0.39-1.02,0.39-1.41,0l-3.05-3.05c-1.22,0.43-2.64,0.17-3.62-0.81 c-1.11-1.11-1.3-2.79-0.59-4.1l2.35,2.35l1.41-1.41L8.58,7.17c1.32-0.71,2.99-0.52,4.1,0.59c0.98,0.98,1.24,2.4,0.81,3.62 l3.05,3.05C16.93,14.82,16.93,15.46,16.54,15.85z" fill-rule="evenodd"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 602 B |
@@ -3,7 +3,8 @@
|
|||||||
// The list of file replacements can be found in `angular.json`.
|
// The list of file replacements can be found in `angular.json`.
|
||||||
|
|
||||||
export const environment = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
|
apiUrl: 'http://localhost:5005',
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -10,6 +10,6 @@
|
|||||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user