This commit is contained in:
parent
a72fc72c83
commit
f6168b6b7c
180
package-lock.json
generated
180
package-lock.json
generated
@ -735,6 +735,88 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular/pwa": {
|
||||
"version": "0.1001.7",
|
||||
"resolved": "https://registry.npmjs.org/@angular/pwa/-/pwa-0.1001.7.tgz",
|
||||
"integrity": "sha512-/vGJ/Z6lY8qT3fT1DzJ4D2iz0WYVxJSnmXAhXwltxJQwKHkgJZFnEzOudPpIVMVs9LO68KLnoM6wshU4ZyFgkg==",
|
||||
"requires": {
|
||||
"@angular-devkit/core": "10.1.7",
|
||||
"@angular-devkit/schematics": "10.1.7",
|
||||
"@schematics/angular": "10.1.7",
|
||||
"parse5-html-rewriting-stream": "6.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular-devkit/core": {
|
||||
"version": "10.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.1.7.tgz",
|
||||
"integrity": "sha512-RRyDkN2FByA+nlnRx/MzUMK1FXwj7+SsrzJcvZfWx4yA5rfKmJiJryXQEzL44GL1aoaXSuvOYu3H72wxZADN8Q==",
|
||||
"requires": {
|
||||
"ajv": "6.12.4",
|
||||
"fast-json-stable-stringify": "2.1.0",
|
||||
"magic-string": "0.25.7",
|
||||
"rxjs": "6.6.2",
|
||||
"source-map": "0.7.3"
|
||||
}
|
||||
},
|
||||
"@angular-devkit/schematics": {
|
||||
"version": "10.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-10.1.7.tgz",
|
||||
"integrity": "sha512-nk9RXA09b+7uq59HS/gyztNzUGHH/eQAUQhWHdDYSCG6v1lhJVCKx1HgDPELVxmeq9f+HArkAW7Y7c+ccdNQ7A==",
|
||||
"requires": {
|
||||
"@angular-devkit/core": "10.1.7",
|
||||
"ora": "5.0.0",
|
||||
"rxjs": "6.6.2"
|
||||
}
|
||||
},
|
||||
"@schematics/angular": {
|
||||
"version": "10.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-10.1.7.tgz",
|
||||
"integrity": "sha512-jcyLWDSbpgHvB/BNVSsV4uLJpC2qRx9Z5+rcQpBB1BerqIPS/1cTQg7TViHZtcqnZqWvzHR3jfqzDUSOCZpuJQ==",
|
||||
"requires": {
|
||||
"@angular-devkit/core": "10.1.7",
|
||||
"@angular-devkit/schematics": "10.1.7",
|
||||
"jsonc-parser": "2.3.0"
|
||||
}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.4",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
|
||||
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
|
||||
},
|
||||
"magic-string": {
|
||||
"version": "0.25.7",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
|
||||
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
|
||||
"requires": {
|
||||
"sourcemap-codec": "^1.4.4"
|
||||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
|
||||
"integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@angular/router": {
|
||||
"version": "10.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-10.1.5.tgz",
|
||||
@ -3624,7 +3706,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"restore-cursor": "^3.1.0"
|
||||
}
|
||||
@ -3632,8 +3713,7 @@
|
||||
"cli-spinners": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.5.0.tgz",
|
||||
"integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ=="
|
||||
},
|
||||
"cli-width": {
|
||||
"version": "3.0.0",
|
||||
@ -4651,7 +4731,6 @@
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
|
||||
"integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"clone": "^1.0.2"
|
||||
},
|
||||
@ -4659,8 +4738,7 @@
|
||||
"clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
|
||||
"dev": true
|
||||
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -4813,6 +4891,11 @@
|
||||
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
|
||||
"dev": true
|
||||
},
|
||||
"dexie": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dexie/-/dexie-3.0.2.tgz",
|
||||
"integrity": "sha512-go4FnIoAhcUiCdxutfIZRxnSaSyDgfEq+GH7N0I8nTCJbC2FmeBj+0FrETa3ln5ix+VQMOPsFeYHlgE/8SZWwQ=="
|
||||
},
|
||||
"dezalgo": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
|
||||
@ -7038,8 +7121,7 @@
|
||||
"is-interactive": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
|
||||
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
|
||||
"dev": true
|
||||
"integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="
|
||||
},
|
||||
"is-negative-zero": {
|
||||
"version": "2.0.0",
|
||||
@ -7493,8 +7575,7 @@
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
@ -7520,8 +7601,7 @@
|
||||
"jsonc-parser": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.0.tgz",
|
||||
"integrity": "sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA=="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
@ -8055,7 +8135,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
|
||||
"integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.0.0"
|
||||
},
|
||||
@ -8064,7 +8143,6 @@
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
@ -8073,7 +8151,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
@ -8083,7 +8160,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
@ -8091,20 +8167,17 @@
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
@ -8412,8 +8485,7 @@
|
||||
"mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
|
||||
},
|
||||
"mini-css-extract-plugin": {
|
||||
"version": "0.10.0",
|
||||
@ -8653,8 +8725,7 @@
|
||||
"mute-stream": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
|
||||
},
|
||||
"nanomatch": {
|
||||
"version": "1.2.13",
|
||||
@ -8700,6 +8771,14 @@
|
||||
"integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
|
||||
"dev": true
|
||||
},
|
||||
"ng-connection-service": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/ng-connection-service/-/ng-connection-service-1.0.4.tgz",
|
||||
"integrity": "sha512-WrZfK+hUzrJS77ItxXI08rUN6Av77W3+LsaJEPufyo2wRe7Tn8xG18FHHEbbgqKkJeDT/yGJBH2xOaT+1jb22g==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"ngx-markdown": {
|
||||
"version": "10.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ngx-markdown/-/ngx-markdown-10.1.1.tgz",
|
||||
@ -9239,7 +9318,6 @@
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
|
||||
"integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mimic-fn": "^2.1.0"
|
||||
}
|
||||
@ -9293,7 +9371,6 @@
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-5.0.0.tgz",
|
||||
"integrity": "sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "^4.1.0",
|
||||
"cli-cursor": "^3.1.0",
|
||||
@ -9308,14 +9385,12 @@
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
@ -9324,7 +9399,6 @@
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
@ -9334,7 +9408,6 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
@ -9342,20 +9415,17 @@
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
@ -9364,7 +9434,6 @@
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
@ -9693,8 +9762,16 @@
|
||||
"parse5": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
|
||||
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
|
||||
},
|
||||
"parse5-html-rewriting-stream": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz",
|
||||
"integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==",
|
||||
"requires": {
|
||||
"parse5": "^6.0.1",
|
||||
"parse5-sax-parser": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"parse5-htmlparser2-tree-adapter": {
|
||||
"version": "6.0.1",
|
||||
@ -9705,6 +9782,14 @@
|
||||
"parse5": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"parse5-sax-parser": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz",
|
||||
"integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==",
|
||||
"requires": {
|
||||
"parse5": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"parseqs": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
|
||||
@ -10968,8 +11053,7 @@
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"q": {
|
||||
"version": "1.4.1",
|
||||
@ -11438,7 +11522,6 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
@ -11940,8 +12023,7 @@
|
||||
"signal-exit": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA=="
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
@ -12316,8 +12398,7 @@
|
||||
"source-map": {
|
||||
"version": "0.7.3",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
|
||||
},
|
||||
"source-map-loader": {
|
||||
"version": "1.0.2",
|
||||
@ -12389,8 +12470,7 @@
|
||||
"sourcemap-codec": {
|
||||
"version": "1.4.8",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.1.1",
|
||||
@ -13432,7 +13512,6 @@
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
|
||||
"integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
@ -13886,7 +13965,6 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||
"integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
|
@ -30,7 +30,9 @@
|
||||
"ag-grid-angular": "^22.1.1",
|
||||
"ag-grid-community": "^22.1.1",
|
||||
"core-js": "^2.5.4",
|
||||
"dexie": "^3.0.2",
|
||||
"moment": "^2.24.0",
|
||||
"ng-connection-service": "^1.0.4",
|
||||
"ngx-markdown": "^10.1.1",
|
||||
"ngx-monaco-editor": "^8.1.1",
|
||||
"rxjs": "~6.6.3",
|
||||
|
@ -21,6 +21,7 @@ import {SessionService} from './service/session.service';
|
||||
import {SearchComponent} from './components/search/Search.component';
|
||||
import {NodeTypeIcons} from './structures/node-types';
|
||||
import {NavigationService} from './service/navigation.service';
|
||||
import {DatabaseService} from './service/db/database.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@ -79,6 +80,7 @@ export class AppComponent implements OnInit {
|
||||
protected hasSearchOpen = false;
|
||||
protected versionInterval?: any;
|
||||
protected showedNewVersionAlert = false;
|
||||
protected showedOfflineAlert = false;
|
||||
protected initialized$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor(
|
||||
@ -94,11 +96,14 @@ export class AppComponent implements OnInit {
|
||||
protected loading: LoadingController,
|
||||
protected navService: NavigationService,
|
||||
protected toasts: ToastController,
|
||||
protected db: DatabaseService,
|
||||
) {
|
||||
this.initializeApp();
|
||||
}
|
||||
|
||||
_doInit() {
|
||||
async _doInit() {
|
||||
await this.db.createSchemata();
|
||||
|
||||
this.reloadMenuItems().subscribe(() => {
|
||||
this.ready$.next(true);
|
||||
setTimeout(() => {
|
||||
@ -402,17 +407,16 @@ export class AppComponent implements OnInit {
|
||||
|
||||
reloadMenuItems() {
|
||||
return new Observable(sub => {
|
||||
this.api.get('/menu/items').subscribe(result => {
|
||||
this.nodes = result.data;
|
||||
setTimeout(() => {
|
||||
this.api.getMenuItems().then(nodes => {
|
||||
this.nodes = nodes;
|
||||
sub.next();
|
||||
sub.complete();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async initializeApp() {
|
||||
console.log('app', this);
|
||||
this.loader = await this.loading.create({
|
||||
message: 'Starting up...',
|
||||
cssClass: 'noded-loading-mask',
|
||||
@ -420,8 +424,24 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
|
||||
await this.loader.present();
|
||||
|
||||
await this.platform.ready();
|
||||
|
||||
let toast: any;
|
||||
this.api.offline$.subscribe(async isOffline => {
|
||||
if ( isOffline && !this.showedOfflineAlert ) {
|
||||
toast = await this.toasts.create({
|
||||
cssClass: 'compat-toast-container',
|
||||
message: 'Uh, oh! It looks like you\'re offline. Some features might not work as expected...',
|
||||
});
|
||||
|
||||
this.showedOfflineAlert = true;
|
||||
await toast.present();
|
||||
} else if ( !isOffline && this.showedOfflineAlert ) {
|
||||
await toast.dismiss();
|
||||
this.showedOfflineAlert = false;
|
||||
}
|
||||
});
|
||||
|
||||
const stat: any = await this.session.stat();
|
||||
|
||||
if ( !stat.authenticated_user ) {
|
||||
|
@ -15,6 +15,7 @@ import {AgGridModule} from 'ag-grid-angular';
|
||||
import {MonacoEditorModule} from 'ngx-monaco-editor';
|
||||
import { APP_BASE_HREF, PlatformLocation } from '@angular/common';
|
||||
import { MarkdownModule } from 'ngx-markdown';
|
||||
import {ConnectionServiceModule} from 'ng-connection-service';
|
||||
|
||||
/**
|
||||
* This function is used internal to get a string instance of the `<base href="" />` value from `index.html`.
|
||||
@ -44,6 +45,7 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
|
||||
AgGridModule.withComponents([]),
|
||||
MonacoEditorModule.forRoot(),
|
||||
MarkdownModule.forRoot(),
|
||||
ConnectionServiceModule,
|
||||
],
|
||||
providers: [
|
||||
StatusBar,
|
||||
|
@ -1,8 +1,11 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
import {Injectable} from '@angular/core';
|
||||
import {environment} from '../../environments/environment';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {Observable} from 'rxjs';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import ApiResponse from '../structures/ApiResponse';
|
||||
import {MenuItem} from './db/MenuItem';
|
||||
import {DatabaseService} from './db/database.service';
|
||||
import {ConnectionService} from 'ng-connection-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -11,10 +14,36 @@ export class ApiService {
|
||||
protected baseEndpoint: string = environment.backendBase;
|
||||
protected statUrl: string = environment.statUrl;
|
||||
protected versionUrl: string = environment.versionUrl;
|
||||
protected offline = false;
|
||||
public readonly offline$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
|
||||
|
||||
get isOffline() {
|
||||
return this.offline;
|
||||
}
|
||||
|
||||
constructor(
|
||||
protected http: HttpClient,
|
||||
) { }
|
||||
protected db: DatabaseService,
|
||||
protected connection: ConnectionService,
|
||||
) {
|
||||
connection.monitor().subscribe(isConnected => {
|
||||
if ( !isConnected ) {
|
||||
this.makeOffline();
|
||||
} else {
|
||||
this.makeOnline(); // TODO add checks for server.
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public makeOffline() {
|
||||
this.offline = true;
|
||||
this.offline$.next(true);
|
||||
}
|
||||
|
||||
public makeOnline() {
|
||||
this.offline = false;
|
||||
this.offline$.next(false);
|
||||
}
|
||||
|
||||
public get(endpoint, params = {}): Observable<ApiResponse> {
|
||||
return this.request(endpoint, params, 'get');
|
||||
@ -29,14 +58,55 @@ export class ApiService {
|
||||
}
|
||||
|
||||
public stat(): Observable<ApiResponse> {
|
||||
return this._request(this.statUrl);
|
||||
return new Observable<ApiResponse>(sub => {
|
||||
(async () => {
|
||||
const statKV = await this.db.getKeyValue('host_stat');
|
||||
|
||||
// If offline, look up the last stored stat for information
|
||||
if ( this.isOffline ) {
|
||||
if ( typeof statKV !== 'object' ) {
|
||||
throw new Error('No locally stored host stat found.');
|
||||
}
|
||||
|
||||
sub.next(new ApiResponse(statKV.data));
|
||||
sub.complete();
|
||||
}
|
||||
|
||||
// Otherwise, fetch the stat and cache it locally
|
||||
this._request(this.statUrl).subscribe(apiResponse => {
|
||||
statKV.data = {status: apiResponse.status, message: apiResponse.message, data: apiResponse.data};
|
||||
statKV.save().then(() => {
|
||||
sub.next(statKV.data);
|
||||
sub.complete();
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public version(): Promise<string> {
|
||||
return new Promise((res, rej) => {
|
||||
return new Promise(async (res, rej) => {
|
||||
const versionKV = await this.db.getKeyValue('app_version');
|
||||
|
||||
// If offline, look up the local app version.
|
||||
if ( this.isOffline ) {
|
||||
if ( versionKV ) {
|
||||
return res(versionKV.data);
|
||||
} else {
|
||||
return rej(new Error('No local app version found.'));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, look up the app version and store it locally
|
||||
this._request(this.versionUrl).subscribe({
|
||||
next: result => {
|
||||
res(result.data.text.trim());
|
||||
next: async result => {
|
||||
const version = result.data.text.trim();
|
||||
|
||||
versionKV.data = version;
|
||||
await versionKV.save();
|
||||
|
||||
res(version);
|
||||
},
|
||||
error: rej,
|
||||
});
|
||||
@ -76,4 +146,79 @@ export class ApiService {
|
||||
|
||||
return `${this.baseEndpoint.endsWith('/') ? this.baseEndpoint.slice(0, -1) : this.baseEndpoint}${endpoint}`;
|
||||
}
|
||||
|
||||
public getMenuItems(): Promise<any[]> {
|
||||
return new Promise(async (res, rej) => {
|
||||
await this.db.createSchemata();
|
||||
|
||||
// If offline, fetch the menu from the database
|
||||
if ( this.isOffline ) {
|
||||
const items = await this.db.menuItems.toArray();
|
||||
const nodes = MenuItem.inflateTree(items as MenuItem[]);
|
||||
return res(nodes);
|
||||
}
|
||||
|
||||
// Download the latest menu items
|
||||
const tree: any[] = await new Promise(res2 => {
|
||||
this.get('/menu/items').subscribe({
|
||||
next: async result => {
|
||||
const nodes = result.data as any[];
|
||||
const items = MenuItem.deflateTree(nodes);
|
||||
|
||||
// Update the locally stored nodes
|
||||
await this.db.menuItems.clear();
|
||||
await Promise.all(items.map(item => item.save()));
|
||||
|
||||
res2(nodes);
|
||||
},
|
||||
error: rej,
|
||||
});
|
||||
});
|
||||
|
||||
res(tree);
|
||||
});
|
||||
}
|
||||
|
||||
public getSessionData(): Promise<any> {
|
||||
return new Promise(async (res, rej) => {
|
||||
const sessionKV = await this.db.getKeyValue('session_data');
|
||||
|
||||
// If offline, just return the locally cached session data
|
||||
if ( this.isOffline ) {
|
||||
if ( typeof sessionKV.data !== 'object' ) {
|
||||
return rej(new Error('No locally cached session data found.'));
|
||||
}
|
||||
|
||||
return res(sessionKV.data);
|
||||
}
|
||||
|
||||
// Otherwise, fetch the session data from the server and cache it locally
|
||||
this.get('/session').subscribe(async result => {
|
||||
sessionKV.data = result.data;
|
||||
await sessionKV.save();
|
||||
res(sessionKV.data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public saveSessionData(data: any): Promise<void> {
|
||||
return new Promise(async (res, rej) => {
|
||||
// Update the local session data
|
||||
const sessionKV = await this.db.getKeyValue('session_data');
|
||||
sessionKV.data = data;
|
||||
await sessionKV.save();
|
||||
|
||||
// If we're not offline, then update the data on the server
|
||||
if ( !this.isOffline ) {
|
||||
await new Promise(res2 => {
|
||||
this.post('/session', data || {}).subscribe({
|
||||
next: res2,
|
||||
error: rej,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
res();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
65
src/app/service/db/KeyValue.ts
Normal file
65
src/app/service/db/KeyValue.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import {Model} from './Model';
|
||||
|
||||
export interface IKeyValue {
|
||||
id?: number;
|
||||
key: string;
|
||||
value: string;
|
||||
json: boolean;
|
||||
}
|
||||
|
||||
export class KeyValue extends Model<IKeyValue> implements IKeyValue {
|
||||
id?: number;
|
||||
key: string;
|
||||
value: string;
|
||||
json: boolean;
|
||||
|
||||
public static getTableName() {
|
||||
return 'keyValues';
|
||||
}
|
||||
|
||||
public static getSchema() {
|
||||
return '++id, key, value, json';
|
||||
}
|
||||
|
||||
constructor(key: string, value: string, json: boolean, id?: number) {
|
||||
super();
|
||||
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.json = json;
|
||||
if ( id ) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
get data(): any {
|
||||
if ( this.json ) {
|
||||
return JSON.parse(this.value);
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
set data(val: any) {
|
||||
if ( typeof val === 'string' ) {
|
||||
this.json = false;
|
||||
this.value = val;
|
||||
} else {
|
||||
this.json = true;
|
||||
this.value = JSON.stringify(val);
|
||||
}
|
||||
}
|
||||
|
||||
public getSaveRecord(): any {
|
||||
return {
|
||||
...(this.id ? { id: this.id } : {}),
|
||||
key: this.key,
|
||||
value: this.value,
|
||||
json: this.json,
|
||||
};
|
||||
}
|
||||
|
||||
public getDatabase(): Dexie.Table<IKeyValue, number> {
|
||||
return this.staticClass().dbService.table('keyValues') as Dexie.Table<IKeyValue, number>;
|
||||
}
|
||||
}
|
171
src/app/service/db/MenuItem.ts
Normal file
171
src/app/service/db/MenuItem.ts
Normal file
@ -0,0 +1,171 @@
|
||||
import {Model} from './Model';
|
||||
import Dexie from 'dexie';
|
||||
|
||||
export interface IMenuItem {
|
||||
id?: number;
|
||||
serverId: string;
|
||||
name: string;
|
||||
childIds?: string[];
|
||||
noDelete?: boolean;
|
||||
noChildren?: boolean;
|
||||
virtual?: boolean;
|
||||
type?: string;
|
||||
shared?: boolean;
|
||||
needsServerUpdate?: boolean;
|
||||
}
|
||||
|
||||
export class MenuItem extends Model<IMenuItem> implements IMenuItem {
|
||||
id?: number;
|
||||
serverId: string;
|
||||
name: string;
|
||||
childIds?: string[];
|
||||
noDelete?: boolean;
|
||||
noChildren?: boolean;
|
||||
virtual?: boolean;
|
||||
type?: string;
|
||||
shared?: boolean;
|
||||
needsServerUpdate?: boolean;
|
||||
|
||||
public static getTableName() {
|
||||
return 'menuItems';
|
||||
}
|
||||
|
||||
public static getSchema() {
|
||||
return '++id, serverId, name, childIds, noDelete, noChildren, virtual, type, shared, needsServerUpdate';
|
||||
}
|
||||
|
||||
public static deflateTree(nodes: any[]): MenuItem[] {
|
||||
let items = [];
|
||||
|
||||
for ( const node of nodes ) {
|
||||
const childIds = node.children ? node.children.map(x => x.id) : [];
|
||||
const item = new MenuItem(node.name, node.id, childIds, node.noDelete, node.noChildren, node.virtual, node.type, node.shared);
|
||||
|
||||
items.push(item);
|
||||
if ( node.children ) {
|
||||
items = items.concat(...this.deflateTree(node.children));
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
public static inflateTree(items: MenuItem[]) {
|
||||
const serverIdXItems: { [key: string]: MenuItem[] } = {};
|
||||
|
||||
for ( const item of items ) {
|
||||
if ( !serverIdXItems[item.serverId] ) {
|
||||
serverIdXItems[item.serverId] = [];
|
||||
}
|
||||
|
||||
serverIdXItems[item.serverId].push(item);
|
||||
}
|
||||
|
||||
const inflateNode = (item, alreadyChildren = [], seen = []) => {
|
||||
const node: any = item.getSaveRecord();
|
||||
seen.push(item);
|
||||
node.id = node.serverId;
|
||||
|
||||
node.children = [];
|
||||
if ( item.childIds ) {
|
||||
for ( const childId of item.childIds ) {
|
||||
if ( serverIdXItems[childId] ) {
|
||||
const children = serverIdXItems[childId].filter(x => {
|
||||
if ( x.type !== 'page' && item.serverId !== x.serverId ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return x !== item && !alreadyChildren.includes(x) && !seen.includes(x);
|
||||
});
|
||||
|
||||
node.children = node.children.concat(...children.map(x => inflateNode(x, children, seen)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pageChildren = [];
|
||||
const otherChildren = [];
|
||||
for ( const child of node.children ) {
|
||||
if ( child.type === 'page' ) {
|
||||
pageChildren.push(child);
|
||||
} else {
|
||||
otherChildren.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
node.children = [...otherChildren, ...pageChildren];
|
||||
return node;
|
||||
};
|
||||
|
||||
const topLevelItems = items.filter(x => String(x.serverId) === '0');
|
||||
return topLevelItems.map(x => inflateNode(x));
|
||||
}
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
serverId: string,
|
||||
childIds?: string[],
|
||||
noDelete?: boolean,
|
||||
noChildren?: boolean,
|
||||
virtual?: boolean,
|
||||
type?: string,
|
||||
shared?: boolean,
|
||||
needsServerUpdate?: boolean,
|
||||
id?: number
|
||||
) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
this.serverId = serverId;
|
||||
if ( childIds ) {
|
||||
this.childIds = childIds;
|
||||
}
|
||||
|
||||
if ( typeof noDelete !== 'undefined' ) {
|
||||
this.noDelete = noDelete;
|
||||
}
|
||||
|
||||
if ( typeof noChildren !== 'undefined' ) {
|
||||
this.noChildren = noChildren;
|
||||
}
|
||||
|
||||
if ( typeof virtual !== 'undefined' ) {
|
||||
this.virtual = virtual;
|
||||
}
|
||||
|
||||
if ( type ) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
if ( typeof shared !== 'undefined' ) {
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
if ( typeof needsServerUpdate !== 'undefined' ) {
|
||||
this.needsServerUpdate = needsServerUpdate;
|
||||
}
|
||||
|
||||
if ( id ) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public getDatabase(): Dexie.Table<IMenuItem, number> {
|
||||
return this.staticClass().dbService.table('menuItems') as Dexie.Table<IMenuItem, number>;
|
||||
}
|
||||
|
||||
public getSaveRecord(): any {
|
||||
return {
|
||||
...(this.id ? { id: this.id } : {}),
|
||||
serverId: this.serverId,
|
||||
name: this.name,
|
||||
...(typeof this.childIds !== 'undefined' ? { childIds: this.childIds } : {}),
|
||||
...(typeof this.noDelete !== 'undefined' ? { noDelete: this.noDelete } : {}),
|
||||
...(typeof this.noChildren !== 'undefined' ? { noChildren: this.noChildren } : {}),
|
||||
...(typeof this.virtual !== 'undefined' ? { virtual: this.virtual } : {}),
|
||||
...(typeof this.type !== 'undefined' ? { type: this.type } : {}),
|
||||
...(typeof this.shared !== 'undefined' ? { shared: this.shared } : {}),
|
||||
...(typeof this.needsServerUpdate !== 'undefined' ? { needsServerUpdate: this.needsServerUpdate } : {}),
|
||||
};
|
||||
}
|
||||
}
|
43
src/app/service/db/Migration.ts
Normal file
43
src/app/service/db/Migration.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import {Model} from './Model';
|
||||
|
||||
export interface IMigration {
|
||||
id?: number;
|
||||
uuid: string;
|
||||
applied: boolean;
|
||||
}
|
||||
|
||||
export class Migration extends Model<IMigration> implements IMigration {
|
||||
id?: number;
|
||||
uuid: string;
|
||||
applied: boolean;
|
||||
|
||||
public static getTableName() {
|
||||
return 'migrations';
|
||||
}
|
||||
|
||||
public static getSchema() {
|
||||
return '++id, uuid, applied';
|
||||
}
|
||||
|
||||
constructor(uuid: string, applied: boolean, id?: number) {
|
||||
super();
|
||||
|
||||
this.uuid = uuid;
|
||||
this.applied = applied;
|
||||
if ( id ) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public getSaveRecord(): any {
|
||||
return {
|
||||
...(this.id ? { id: this.id } : {}),
|
||||
uuid: this.uuid,
|
||||
applied: this.applied,
|
||||
};
|
||||
}
|
||||
|
||||
public getDatabase(): Dexie.Table<IMigration, number> {
|
||||
return this.staticClass().dbService.table('migrations') as Dexie.Table<IMigration, number>;
|
||||
}
|
||||
}
|
31
src/app/service/db/Model.ts
Normal file
31
src/app/service/db/Model.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import Dexie from 'dexie';
|
||||
import {DatabaseService} from './database.service';
|
||||
|
||||
export abstract class Model<InterfaceType> {
|
||||
public static dbService?: DatabaseService;
|
||||
|
||||
public id?: number;
|
||||
|
||||
public static getSchema(): string {
|
||||
throw new TypeError('Child class must implement.');
|
||||
}
|
||||
|
||||
public static getTableName(): string {
|
||||
throw new TypeError('Child class must implement.');
|
||||
}
|
||||
|
||||
public abstract getDatabase(): Dexie.Table<InterfaceType, number>;
|
||||
public abstract getSaveRecord(): any;
|
||||
|
||||
public staticClass() {
|
||||
return (this.constructor as typeof Model);
|
||||
}
|
||||
|
||||
public exists() {
|
||||
return !!this.id;
|
||||
}
|
||||
|
||||
public async save() {
|
||||
this.id = await this.getDatabase().put(this.getSaveRecord());
|
||||
}
|
||||
}
|
67
src/app/service/db/database.service.ts
Normal file
67
src/app/service/db/database.service.ts
Normal file
@ -0,0 +1,67 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import Dexie from 'dexie';
|
||||
import {IMigration, Migration} from './Migration';
|
||||
import {IMenuItem, MenuItem} from './MenuItem';
|
||||
import {KeyValue, IKeyValue} from './KeyValue';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class DatabaseService extends Dexie {
|
||||
protected static registeredModels = [Migration, MenuItem, KeyValue];
|
||||
protected initialized = false;
|
||||
|
||||
migrations!: Dexie.Table<IMigration, number>;
|
||||
menuItems!: Dexie.Table<IMenuItem, number>;
|
||||
keyValues!: Dexie.Table<IKeyValue, number>;
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super('NodedLocalDatabase');
|
||||
}
|
||||
|
||||
public async getKeyValue(key: string): Promise<KeyValue> {
|
||||
const matches = await this.keyValues.where({ key }).toArray();
|
||||
|
||||
if ( matches.length > 0 ) {
|
||||
return matches[0] as KeyValue;
|
||||
}
|
||||
|
||||
return new KeyValue(key, '', false);
|
||||
}
|
||||
|
||||
public async createSchemata() {
|
||||
if ( this.initialized ) {
|
||||
return;
|
||||
}
|
||||
this.initialized = true;
|
||||
|
||||
console.log('db', this);
|
||||
|
||||
const staticClass = this.constructor as typeof DatabaseService;
|
||||
const schema: any = {};
|
||||
|
||||
for ( const ModelClass of staticClass.registeredModels ) {
|
||||
ModelClass.dbService = this;
|
||||
schema[ModelClass.getTableName()] = ModelClass.getSchema();
|
||||
}
|
||||
|
||||
await this.version(3).stores(schema);
|
||||
await this.open();
|
||||
|
||||
this.migrations = this.table('migrations');
|
||||
this.migrations.mapToClass(Migration);
|
||||
|
||||
this.menuItems = this.table('menuItems');
|
||||
this.menuItems.mapToClass(MenuItem);
|
||||
|
||||
this.keyValues = this.table('keyValues');
|
||||
this.keyValues.mapToClass(KeyValue);
|
||||
|
||||
// await new Promise(res => {
|
||||
// setTimeout(() => {
|
||||
// res();
|
||||
// }, 1000);
|
||||
// });
|
||||
}
|
||||
}
|
@ -50,28 +50,13 @@ export class SessionService {
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
return new Promise((res, rej) => {
|
||||
this.api.get('/session').subscribe(response => {
|
||||
this.data = response.data;
|
||||
res();
|
||||
});
|
||||
});
|
||||
this.data = await this.api.getSessionData();
|
||||
}
|
||||
|
||||
async save() {
|
||||
this.saving = true;
|
||||
return new Promise((res, rej) => {
|
||||
this.api.post('/session', this.data || {}).subscribe({
|
||||
next: result => {
|
||||
res();
|
||||
await this.api.saveSessionData(this.data);
|
||||
this.saving = false;
|
||||
},
|
||||
error: (e) => {
|
||||
this.saving = false;
|
||||
rej(e);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
buildAppUrl(...parts: string[]): string {
|
||||
|
Loading…
Reference in New Issue
Block a user