From bb08e46b0c85cd384543398326625fdee00eff7a Mon Sep 17 00:00:00 2001 From: benjamin Date: Mon, 9 Mar 2026 22:56:31 +0100 Subject: [PATCH] migrate angular 21 finalize --- .browserslistrc | 17 +- package-lock.json | 804 +++++++++--------- package.json | 6 +- src/app/app.component.ts | 6 +- .../brand/new-user/new-user.component.ts | 8 +- .../modules/guest/guest-show-data.service.ts | 6 +- src/app/modules/guest/guest-show.service.ts | 8 +- src/app/modules/guest/guest.component.ts | 12 +- .../presentation/monitor/monitor.component.ts | 18 +- .../presentation/remote/remote.component.ts | 19 +- .../presentation/select/select.component.ts | 12 +- .../share-dialog/share-dialog.component.ts | 8 +- src/app/modules/shows/edit/edit.component.ts | 15 +- .../shows/list/filter/filter.component.ts | 28 +- src/app/modules/shows/list/list.component.ts | 16 +- src/app/modules/shows/new/new.component.ts | 13 +- .../modules/shows/services/docx.service.ts | 36 +- .../shows/services/show-data.service.ts | 6 +- .../shows/services/show-song-data.service.ts | 6 +- .../shows/services/show-song.service.ts | 12 +- .../modules/shows/services/show.service.ts | 12 +- src/app/modules/shows/show/show.component.ts | 24 +- .../modules/shows/show/song/song.component.ts | 6 +- .../songs/services/file-data.service.ts | 4 +- .../modules/songs/services/file.service.ts | 8 +- .../modules/songs/services/key.helper.spec.ts | 6 +- .../songs/services/song-data.service.ts | 9 +- .../songs/services/song-list.resolver.ts | 4 +- .../modules/songs/services/song.service.ts | 14 +- .../songs/services/text-rendering.service.ts | 6 +- .../songs/services/transpose.service.spec.ts | 20 +- .../modules/songs/services/upload.service.ts | 10 +- .../song-list/filter/filter.component.ts | 13 +- .../songs/song-list/song-list.component.ts | 12 +- .../edit/edit-file/edit-file.component.ts | 12 +- .../edit/edit-file/file/file.component.ts | 6 +- .../edit/edit-song/edit-song.component.ts | 16 +- .../song/edit/history/history.component.ts | 10 +- .../modules/songs/song/file/file.component.ts | 6 +- .../modules/songs/song/new/new.component.ts | 10 +- src/app/modules/songs/song/song.component.ts | 22 +- src/app/modules/user/info/info.component.ts | 6 +- .../user/info/users/user/user.component.ts | 6 +- .../user/info/users/users.component.ts | 8 +- src/app/modules/user/login/login.component.ts | 12 +- .../modules/user/logout/logout.component.ts | 16 +- src/app/modules/user/new/new.component.ts | 10 +- .../user/password/password.component.ts | 10 +- src/app/services/config.service.ts | 6 +- src/app/services/db.service.ts | 4 +- src/app/services/fullscreen.ts | 18 +- src/app/services/global-settings.service.ts | 6 +- src/app/services/user/owner.directive.ts | 14 +- src/app/services/user/role.directive.ts | 16 +- .../user/user-name/user-name.component.ts | 6 +- src/app/services/user/user.service.ts | 16 +- .../components/add-song/add-song.component.ts | 10 +- .../navigation/filter/filter.component.ts | 11 +- .../song-text/song-text.component.ts | 12 +- .../autofocus/autofocus.directive.ts | 5 +- src/app/widget-modules/guards/auth.guard.ts | 8 +- src/app/widget-modules/guards/role.guard.ts | 8 +- .../pipes/sort-by/sort-by.pipe.ts | 27 +- 63 files changed, 738 insertions(+), 783 deletions(-) diff --git a/.browserslistrc b/.browserslistrc index 8084853..fb44d22 100644 --- a/.browserslistrc +++ b/.browserslistrc @@ -1,12 +1,7 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -> 0.5% -last 2 versions +# Angular 21-supported baseline to avoid unsupported-browser build warnings. +last 2 Chrome major versions +last 2 Edge major versions +last 2 Firefox major versions Firefox ESR -not dead -not IE 9-11 # For IE 9-11 support, remove 'not'. \ No newline at end of file +last 2 Safari major versions +last 2 iOS major versions diff --git a/package-lock.json b/package-lock.json index ce2dccd..f1bd032 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@angular/common": "^21.2.2", "@angular/compiler": "^21.2.2", "@angular/core": "^21.2.2", - "@angular/fire": "^21.0.0-rc.0", + "@angular/fire": "21.0.0-rc.0", "@angular/forms": "^21.2.2", "@angular/material": "^21.2.1", "@angular/platform-browser": "^21.2.2", @@ -25,7 +25,6 @@ "@fortawesome/free-solid-svg-icons": "^7.2.0", "docx": "^9.6.0", "firebase": "^12.10.0", - "lodash": "^4.17.21", "ngx-mat-select-search": "^8.0.4", "qrcode": "^1.5.4", "rxjs": "~7.8.1", @@ -46,13 +45,12 @@ "@angular/language-service": "^21.2.2", "@types/jasmine": "~6.0.0", "@types/jasminewd2": "~2.0.13", - "@types/lodash": "^4.17.24", "@typescript-eslint/eslint-plugin": "^8.57.0", "@typescript-eslint/parser": "^8.57.0", "eslint": "^9.39.4", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "firebase-tools": "^14.27.0", + "firebase-tools": "^15.9.1", "jasmine-core": "~6.1.0", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.4", @@ -7215,16 +7213,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", @@ -9935,17 +9923,12 @@ "@types/node": "*" } }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -10050,13 +10033,6 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "devOptional": true }, - "node_modules/@types/lodash": { - "version": "4.17.24", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", - "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -11607,6 +11583,7 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "dev": true, + "license": "MIT", "engines": { "node": "^4.5.0 || >= 5.9" } @@ -11766,24 +11743,24 @@ } }, "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "devOptional": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8", @@ -11799,12 +11776,43 @@ "ms": "2.0.0" } }, + "node_modules/body-parser/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "devOptional": true }, + "node_modules/body-parser/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/bonjour-service": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", @@ -11847,10 +11855,11 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "devOptional": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -12226,16 +12235,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "devOptional": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, "node_modules/chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -12859,10 +12858,11 @@ "dev": true }, "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "devOptional": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -13697,46 +13697,73 @@ } }, "node_modules/engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz", + "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==", "dev": true, + "license": "MIT", "dependencies": { - "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", - "debug": "~4.3.1", + "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" + "ws": "~8.18.3" }, "engines": { "node": ">=10.2.0" } }, "node_modules/engine.io-parser": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", - "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" } }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "node_modules/engine.io/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -14631,16 +14658,6 @@ "node": ">= 12" } }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -14694,22 +14711,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/express/node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "devOptional": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/express/node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -14957,9 +14958,9 @@ } }, "node_modules/firebase-tools": { - "version": "14.27.0", - "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-14.27.0.tgz", - "integrity": "sha512-HrucHJ69mLM9pQhZFO1rb0N/QMpZD4iznoOtKd2lctEELPtbSMN5JHgdgzLlf+EXn5aQy87u5zlPd/0xwwyYTQ==", + "version": "15.9.1", + "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-15.9.1.tgz", + "integrity": "sha512-QaC8uaQgxunLLOiRsUHPTcNEHiUpJt88rIzD8/fTbCr7MM3//SWMOI6KTY08gN5CP061X140vfWk+WARiwm2yg==", "devOptional": true, "license": "MIT", "dependencies": { @@ -14969,8 +14970,8 @@ "@electric-sql/pglite-tools": "^0.2.8", "@google-cloud/cloud-sql-connector": "^1.3.3", "@google-cloud/pubsub": "^5.2.0", - "@inquirer/prompts": "^7.4.0", - "@modelcontextprotocol/sdk": "^1.10.2", + "@inquirer/prompts": "^7.10.1", + "@modelcontextprotocol/sdk": "^1.24.0", "abort-controller": "^3.0.0", "ajv": "^8.17.1", "ajv-formats": "3.0.1", @@ -14996,11 +14997,11 @@ "fs-extra": "^10.1.0", "fuzzy": "^0.1.3", "gaxios": "^6.7.0", - "glob": "^10.4.1", + "glob": "^10.5.0", "google-auth-library": "^9.11.0", "ignore": "^7.0.4", - "js-yaml": "^3.14.1", - "jsonwebtoken": "^9.0.0", + "js-yaml": "^3.14.2", + "jsonwebtoken": "^9.0.2", "leven": "^3.1.0", "libsodium-wrappers": "^0.7.10", "lodash": "^4.17.21", @@ -15026,7 +15027,6 @@ "stream-chain": "^2.2.4", "stream-json": "^1.7.3", "superstatic": "^10.0.0", - "tar": "^6.1.11", "tcp-port-used": "^1.0.2", "tmp": "^0.2.3", "triple-beam": "^1.3.0", @@ -15323,9 +15323,9 @@ "license": "MIT" }, "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true, "funding": [ { @@ -15333,6 +15333,7 @@ "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -18080,9 +18081,11 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "devOptional": true, + "license": "MIT" }, "node_modules/lodash._objecttypes": { "version": "2.4.1", @@ -20921,13 +20924,13 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "devOptional": true, "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -20967,21 +20970,52 @@ } }, "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "devOptional": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -22106,16 +22140,17 @@ } }, "node_modules/socket.io": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz", - "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", "dev": true, + "license": "MIT", "dependencies": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", + "debug": "~4.4.1", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" }, @@ -22124,25 +22159,53 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", "dev": true, + "license": "MIT", "dependencies": { - "ws": "~8.11.0" + "debug": "~4.4.1", + "ws": "~8.18.3" } }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-adapter/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -22166,6 +22229,31 @@ "node": ">=10.0.0" } }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -22739,25 +22827,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "devOptional": true, - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tar-stream": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", @@ -22771,82 +22840,6 @@ "streamx": "^2.15.0" } }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "devOptional": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "devOptional": true, - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tcp-port-used": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tcp-port-used/-/tcp-port-used-1.0.2.tgz", @@ -24450,7 +24443,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true + "dev": true }, "node_modules/yaml": { "version": "2.8.2", @@ -28928,12 +28921,6 @@ "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "devOptional": true }, - "cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "devOptional": true - }, "cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", @@ -30509,16 +30496,10 @@ "@types/node": "*" } }, - "@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, "@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", "dev": true, "requires": { "@types/node": "*" @@ -30616,12 +30597,6 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "devOptional": true }, - "@types/lodash": { - "version": "4.17.24", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", - "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", - "dev": true - }, "@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -31786,23 +31761,23 @@ } }, "body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", "devOptional": true, "requires": { - "bytes": "3.1.2", + "bytes": "~3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", "type-is": "~1.6.18", - "unpipe": "1.0.0" + "unpipe": "~1.0.0" }, "dependencies": { "debug": { @@ -31814,11 +31789,30 @@ "ms": "2.0.0" } }, + "http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "devOptional": true, + "requires": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "devOptional": true + }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "devOptional": true } } }, @@ -31855,9 +31849,9 @@ } }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "devOptional": true, "requires": { "balanced-match": "^1.0.0", @@ -32083,12 +32077,6 @@ "readdirp": "~3.6.0" } }, - "chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "devOptional": true - }, "chrome-trace-event": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", @@ -32525,10 +32513,10 @@ "dev": true }, "cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "devOptional": true }, "cookie-signature": { "version": "1.0.6", @@ -33091,36 +33079,50 @@ } }, "engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "version": "6.6.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.5.tgz", + "integrity": "sha512-2RZdgEbXmp5+dVbRm0P7HQUImZpICccJy7rN7Tv+SFa55pH+lxnuw6/K1ZxxBfHoYpSkHLAO92oa8O4SwFXA2A==", "dev": true, "requires": { - "@types/cookie": "^0.4.1", "@types/cors": "^2.8.12", "@types/node": ">=10.0.0", "accepts": "~1.3.4", "base64id": "2.0.0", - "cookie": "~0.4.1", + "cookie": "~0.7.2", "cors": "~2.8.5", - "debug": "~4.3.1", + "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" + "ws": "~8.18.3" }, "dependencies": { + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "requires": {} } } }, "engine.io-parser": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", - "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", "dev": true }, "enhanced-resolve": { @@ -33701,12 +33703,6 @@ "vary": "~1.1.2" }, "dependencies": { - "cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "devOptional": true - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -33749,15 +33745,6 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "devOptional": true }, - "qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "devOptional": true, - "requires": { - "side-channel": "^1.1.0" - } - }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -33963,9 +33950,9 @@ } }, "firebase-tools": { - "version": "14.27.0", - "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-14.27.0.tgz", - "integrity": "sha512-HrucHJ69mLM9pQhZFO1rb0N/QMpZD4iznoOtKd2lctEELPtbSMN5JHgdgzLlf+EXn5aQy87u5zlPd/0xwwyYTQ==", + "version": "15.9.1", + "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-15.9.1.tgz", + "integrity": "sha512-QaC8uaQgxunLLOiRsUHPTcNEHiUpJt88rIzD8/fTbCr7MM3//SWMOI6KTY08gN5CP061X140vfWk+WARiwm2yg==", "devOptional": true, "requires": { "@apphosting/build": "^0.1.6", @@ -33974,8 +33961,8 @@ "@electric-sql/pglite-tools": "^0.2.8", "@google-cloud/cloud-sql-connector": "^1.3.3", "@google-cloud/pubsub": "^5.2.0", - "@inquirer/prompts": "^7.4.0", - "@modelcontextprotocol/sdk": "^1.10.2", + "@inquirer/prompts": "^7.10.1", + "@modelcontextprotocol/sdk": "^1.24.0", "abort-controller": "^3.0.0", "ajv": "^8.17.1", "ajv-formats": "3.0.1", @@ -34001,11 +33988,11 @@ "fs-extra": "^10.1.0", "fuzzy": "^0.1.3", "gaxios": "^6.7.0", - "glob": "^10.4.1", + "glob": "^10.5.0", "google-auth-library": "^9.11.0", "ignore": "^7.0.4", - "js-yaml": "^3.14.1", - "jsonwebtoken": "^9.0.0", + "js-yaml": "^3.14.2", + "jsonwebtoken": "^9.0.2", "leven": "^3.1.0", "libsodium-wrappers": "^0.7.10", "lodash": "^4.17.21", @@ -34031,7 +34018,6 @@ "stream-chain": "^2.2.4", "stream-json": "^1.7.3", "superstatic": "^10.0.0", - "tar": "^6.1.11", "tcp-port-used": "^1.0.2", "tmp": "^0.2.3", "triple-beam": "^1.3.0", @@ -34232,9 +34218,9 @@ "devOptional": true }, "follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "dev": true }, "foreground-child": { @@ -36144,9 +36130,10 @@ } }, "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "devOptional": true }, "lodash._objecttypes": { "version": "2.4.1", @@ -38091,12 +38078,12 @@ } }, "qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", "devOptional": true, "requires": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" } }, "railroad-diagrams": { @@ -38122,15 +38109,36 @@ "devOptional": true }, "raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "devOptional": true, "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "devOptional": true, + "requires": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + } + }, + "statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "devOptional": true + } } }, "rc": { @@ -38917,33 +38925,66 @@ "devOptional": true }, "socket.io": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz", - "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==", + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz", + "integrity": "sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==", "dev": true, "requires": { "accepts": "~1.3.4", "base64id": "~2.0.0", "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", + "debug": "~4.4.1", + "engine.io": "~6.6.0", "socket.io-adapter": "~2.5.2", "socket.io-parser": "~4.2.4" + }, + "dependencies": { + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } } }, "socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.6.tgz", + "integrity": "sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==", "dev": true, "requires": { - "ws": "~8.11.0" + "debug": "~4.4.1", + "ws": "~8.18.3" }, "dependencies": { + "debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "requires": { + "ms": "^2.1.3" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, "ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, "requires": {} } @@ -39365,75 +39406,6 @@ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true }, - "tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "devOptional": true, - "requires": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "dependencies": { - "fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "devOptional": true, - "requires": { - "minipass": "^3.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "devOptional": true - }, - "minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "devOptional": true, - "requires": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "devOptional": true, - "requires": { - "yallist": "^4.0.0" - } - } - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "devOptional": true - } - } - }, "tar-stream": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", @@ -40498,7 +40470,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true + "dev": true }, "yaml": { "version": "2.8.2", diff --git a/package.json b/package.json index bdf2b6c..2d2e13e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@angular/common": "^21.2.2", "@angular/compiler": "^21.2.2", "@angular/core": "^21.2.2", - "@angular/fire": "^21.0.0-rc.0", + "@angular/fire": "21.0.0-rc.0", "@angular/forms": "^21.2.2", "@angular/material": "^21.2.1", "@angular/platform-browser": "^21.2.2", @@ -28,7 +28,6 @@ "@fortawesome/free-solid-svg-icons": "^7.2.0", "docx": "^9.6.0", "firebase": "^12.10.0", - "lodash": "^4.17.21", "ngx-mat-select-search": "^8.0.4", "qrcode": "^1.5.4", "rxjs": "~7.8.1", @@ -49,13 +48,12 @@ "@angular/language-service": "^21.2.2", "@types/jasmine": "~6.0.0", "@types/jasminewd2": "~2.0.13", - "@types/lodash": "^4.17.24", "@typescript-eslint/eslint-plugin": "^8.57.0", "@typescript-eslint/parser": "^8.57.0", "eslint": "^9.39.4", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", - "firebase-tools": "^14.27.0", + "firebase-tools": "^15.9.1", "jasmine-core": "~6.1.0", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.4", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 0e51f4a..5ba896e 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core'; +import {ChangeDetectionStrategy, Component, OnInit, inject} from '@angular/core'; import {fader} from './animations'; import {ScrollService} from './services/scroll.service'; import {register} from 'swiper/element/bundle'; @@ -14,7 +14,9 @@ import {NavigationComponent} from './widget-modules/components/application-frame imports: [RouterOutlet, NavigationComponent], }) export class AppComponent implements OnInit { - public constructor(private scrollService: ScrollService) { + private scrollService = inject(ScrollService); + + public constructor() { register(); } diff --git a/src/app/modules/brand/new-user/new-user.component.ts b/src/app/modules/brand/new-user/new-user.component.ts index 8b5cff2..fb7fffd 100644 --- a/src/app/modules/brand/new-user/new-user.component.ts +++ b/src/app/modules/brand/new-user/new-user.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, inject} from '@angular/core'; import {UserService} from '../../../services/user/user.service'; import {Observable} from 'rxjs'; import {User} from '../../../services/user/user'; @@ -12,9 +12,13 @@ import {AsyncPipe} from '@angular/common'; imports: [BrandComponent, AsyncPipe], }) export class NewUserComponent { + private userService = inject(UserService); + public user$: Observable | null = null; - public constructor(private userService: UserService) { + public constructor() { + const userService = this.userService; + this.user$ = userService.user$; } } diff --git a/src/app/modules/guest/guest-show-data.service.ts b/src/app/modules/guest/guest-show-data.service.ts index 7777f52..1b11171 100644 --- a/src/app/modules/guest/guest-show-data.service.ts +++ b/src/app/modules/guest/guest-show-data.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Observable} from 'rxjs'; import {shareReplay} from 'rxjs/operators'; import {DbService} from 'src/app/services/db.service'; @@ -8,6 +8,8 @@ import {GuestShow} from './guest-show'; providedIn: 'root', }) export class GuestShowDataService { + private dbService = inject(DbService); + private collection = 'guest'; public list$: Observable = this.dbService.col$(this.collection).pipe( shareReplay({ @@ -16,8 +18,6 @@ export class GuestShowDataService { }) ); - public constructor(private dbService: DbService) {} - public read$: (id: string) => Observable = (id: string): Observable => this.dbService.doc$(`${this.collection}/${id}`); public update$: (id: string, data: Partial) => Promise = async (id: string, data: Partial): Promise => await this.dbService.doc(this.collection + '/' + id).update(data); diff --git a/src/app/modules/guest/guest-show.service.ts b/src/app/modules/guest/guest-show.service.ts index c31981b..6c6a3e9 100644 --- a/src/app/modules/guest/guest-show.service.ts +++ b/src/app/modules/guest/guest-show.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Show} from '../shows/services/show'; import {Song} from '../songs/services/song'; import {GuestShowDataService} from './guest-show-data.service'; @@ -8,10 +8,8 @@ import {ShowService} from '../shows/services/show.service'; providedIn: 'root', }) export class GuestShowService { - public constructor( - private showService: ShowService, - private guestShowDataService: GuestShowDataService - ) {} + private showService = inject(ShowService); + private guestShowDataService = inject(GuestShowDataService); public async share(show: Show, songs: Song[]): Promise { const data = { diff --git a/src/app/modules/guest/guest.component.ts b/src/app/modules/guest/guest.component.ts index d919ae6..02e7651 100644 --- a/src/app/modules/guest/guest.component.ts +++ b/src/app/modules/guest/guest.component.ts @@ -1,4 +1,4 @@ -import {Component, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; +import {Component, CUSTOM_ELEMENTS_SCHEMA, inject} from '@angular/core'; import {GuestShowDataService} from './guest-show-data.service'; import {ActivatedRoute} from '@angular/router'; import {map, switchMap} from 'rxjs/operators'; @@ -16,17 +16,15 @@ import {ShowTypePipe} from '../../widget-modules/pipes/show-type-translater/show imports: [SongTextComponent, AsyncPipe, DatePipe, ShowTypePipe], }) export class GuestComponent { + private currentRoute = inject(ActivatedRoute); + private service = inject(GuestShowDataService); + private configService = inject(ConfigService); + public show$ = this.currentRoute.params.pipe( map(param => param.id as string), switchMap(id => this.service.read$(id)) ); public config$ = this.configService.get$(); - public constructor( - private currentRoute: ActivatedRoute, - private service: GuestShowDataService, - private configService: ConfigService - ) {} - public trackBy = (index: number, show: Song) => show.id; } diff --git a/src/app/modules/presentation/monitor/monitor.component.ts b/src/app/modules/presentation/monitor/monitor.component.ts index 2369a86..142de73 100644 --- a/src/app/modules/presentation/monitor/monitor.component.ts +++ b/src/app/modules/presentation/monitor/monitor.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectorRef, Component, OnDestroy, OnInit} from '@angular/core'; +import {ChangeDetectorRef, Component, OnDestroy, OnInit, inject} from '@angular/core'; import {distinctUntilChanged, filter, map, shareReplay, switchMap, takeUntil, tap} from 'rxjs/operators'; import {ShowService} from '../../shows/services/show.service'; import {Song} from '../../songs/services/song'; @@ -25,6 +25,12 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s imports: [LogoComponent, SongTextComponent, LegalComponent, AsyncPipe, DatePipe, ShowTypePipe], }) export class MonitorComponent implements OnInit, OnDestroy { + private showService = inject(ShowService); + private showSongService = inject(ShowSongService); + private globalSettingsService = inject(GlobalSettingsService); + private configService = inject(ConfigService); + private cRef = inject(ChangeDetectorRef); + public song: Song | null = null; public zoom = 10; public currentShowId: string | null = null; @@ -38,13 +44,9 @@ export class MonitorComponent implements OnInit, OnDestroy { public presentationBackground: PresentationBackground = 'none'; private destroy$ = new Subject(); - public constructor( - private showService: ShowService, - private showSongService: ShowSongService, - private globalSettingsService: GlobalSettingsService, - private configService: ConfigService, - private cRef: ChangeDetectorRef - ) { + public constructor() { + const configService = this.configService; + this.config$ = configService.get$(); } diff --git a/src/app/modules/presentation/remote/remote.component.ts b/src/app/modules/presentation/remote/remote.component.ts index 0c53ea4..e06aa54 100644 --- a/src/app/modules/presentation/remote/remote.component.ts +++ b/src/app/modules/presentation/remote/remote.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy} from '@angular/core'; +import {ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, inject} from '@angular/core'; import {combineLatest, Subject} from 'rxjs'; import {PresentationBackground, Show} from '../../shows/services/show'; import {ShowSongService} from '../../shows/services/show-song.service'; @@ -61,6 +61,12 @@ export interface PresentationSong { ], }) export class RemoteComponent implements OnDestroy { + private showService = inject(ShowService); + private showSongService = inject(ShowSongService); + private songService = inject(SongService); + private textRenderingService = inject(TextRenderingService); + private cRef = inject(ChangeDetectorRef); + public show: Show | null = null; public showSongs: ShowSong[] = []; public songs$ = this.songService.list$(); @@ -73,14 +79,9 @@ export class RemoteComponent implements OnDestroy { public presentationDynamicTextChanged$ = new Subject<{presentationDynamicText: string; showId: string}>(); private destroy$ = new Subject(); - public constructor( - private showService: ShowService, - private showSongService: ShowSongService, - private songService: SongService, - private textRenderingService: TextRenderingService, - globalSettingsService: GlobalSettingsService, - private cRef: ChangeDetectorRef - ) { + public constructor() { + const globalSettingsService = inject(GlobalSettingsService); + const currentShowId$ = globalSettingsService.get$.pipe( filter((settings): settings is NonNullable => !!settings), map(_ => _.currentShow), diff --git a/src/app/modules/presentation/select/select.component.ts b/src/app/modules/presentation/select/select.component.ts index d76cc6a..fa2f204 100644 --- a/src/app/modules/presentation/select/select.component.ts +++ b/src/app/modules/presentation/select/select.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {map} from 'rxjs/operators'; import {ShowService} from '../../shows/services/show.service'; import {Show} from '../../shows/services/show'; @@ -19,17 +19,15 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s imports: [CardComponent, MatButton, UserNameComponent, AsyncPipe, DatePipe, ShowTypePipe], }) export class SelectComponent implements OnInit { + private showService = inject(ShowService); + private globalSettingsService = inject(GlobalSettingsService); + private router = inject(Router); + public visible = false; public shows$ = this.showService .list$(true) .pipe(map(_ => _.filter(_ => _.date.toDate() > new Date(new Date().setMonth(new Date().getMonth() - 1))).sort((a, b) => (b.date < a.date ? -1 : b.date > a.date ? 1 : 0)))); - public constructor( - private showService: ShowService, - private globalSettingsService: GlobalSettingsService, - private router: Router - ) {} - public async selectShow(show: Show) { this.visible = false; diff --git a/src/app/modules/shows/dialog/share-dialog/share-dialog.component.ts b/src/app/modules/shows/dialog/share-dialog/share-dialog.component.ts index 01f6ded..96467ee 100644 --- a/src/app/modules/shows/dialog/share-dialog/share-dialog.component.ts +++ b/src/app/modules/shows/dialog/share-dialog/share-dialog.component.ts @@ -1,4 +1,4 @@ -import {Component, Inject} from '@angular/core'; +import {Component, inject} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogActions, MatDialogClose, MatDialogContent} from '@angular/material/dialog'; import {MatButton} from '@angular/material/button'; import QRCode from 'qrcode'; @@ -17,9 +17,13 @@ export interface ShareDialogData { styleUrl: './share-dialog.component.less', }) export class ShareDialogComponent { + public data = inject(MAT_DIALOG_DATA); + public qrCode: string; - public constructor(@Inject(MAT_DIALOG_DATA) public data: ShareDialogData) { + public constructor() { + const data = this.data; + // eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment QRCode.toDataURL(data.url, { type: 'image/jpeg', diff --git a/src/app/modules/shows/edit/edit.component.ts b/src/app/modules/shows/edit/edit.component.ts index 8f43f70..08c90da 100644 --- a/src/app/modules/shows/edit/edit.component.ts +++ b/src/app/modules/shows/edit/edit.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {ShowDataService} from '../services/show-data.service'; import {Observable, take} from 'rxjs'; import {Show} from '../services/show'; @@ -42,6 +42,10 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s ], }) export class EditComponent implements OnInit { + private showService = inject(ShowService); + private router = inject(Router); + private activatedRoute = inject(ActivatedRoute); + public shows$: Observable; public showTypePublic = ShowService.SHOW_TYPE_PUBLIC; public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE; @@ -52,12 +56,9 @@ export class EditComponent implements OnInit { }); public faSave = faSave; - public constructor( - private showService: ShowService, - showDataService: ShowDataService, - private router: Router, - private activatedRoute: ActivatedRoute - ) { + public constructor() { + const showDataService = inject(ShowDataService); + this.shows$ = showDataService.list$; } diff --git a/src/app/modules/shows/list/filter/filter.component.ts b/src/app/modules/shows/list/filter/filter.component.ts index c0144bb..cd834ad 100644 --- a/src/app/modules/shows/list/filter/filter.component.ts +++ b/src/app/modules/shows/list/filter/filter.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, inject} from '@angular/core'; import {KeyValue} from '@angular/common'; import {ActivatedRoute, Router} from '@angular/router'; import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup} from '@angular/forms'; @@ -9,7 +9,6 @@ import {distinctUntilChanged, map, switchMap} from 'rxjs/operators'; import {combineLatest, Observable, of} from 'rxjs'; import {dynamicSort, onlyUnique} from '../../../../services/filter.helper'; import {UserService} from '../../../../services/user/user.service'; -import {isEqual} from 'lodash'; import {MatFormField, MatLabel} from '@angular/material/form-field'; import {MatSelect} from '@angular/material/select'; import {MatOptgroup, MatOption} from '@angular/material/core'; @@ -22,6 +21,10 @@ import {ShowTypePipe} from '../../../../widget-modules/pipes/show-type-translate imports: [ReactiveFormsModule, MatFormField, MatLabel, MatSelect, MatOption, MatOptgroup, ShowTypePipe], }) export class FilterComponent { + private router = inject(Router); + private showService = inject(ShowService); + private userService = inject(UserService); + @Input() public route = '/shows/'; @Input() public shows: Show[] = []; @@ -38,13 +41,10 @@ export class FilterComponent { public owners: {key: string; value: string}[] = []; - public constructor( - private router: Router, - private showService: ShowService, - private userService: UserService, - activatedRoute: ActivatedRoute, - fb: UntypedFormBuilder - ) { + public constructor() { + const activatedRoute = inject(ActivatedRoute); + const fb = inject(UntypedFormBuilder); + this.filterFormGroup = fb.group({ time: 1, owner: null, @@ -92,7 +92,7 @@ export class FilterComponent { map(owners => { return owners.sort(dynamicSort('value')); }), - distinctUntilChanged(isEqual), + distinctUntilChanged((left, right) => this.sameOwners(left, right)), map(_ => _ as {key: string; value: string}[]) ); }; @@ -104,4 +104,12 @@ export class FilterComponent { }); await this.router.navigateByUrl(route); } + + private sameOwners(left: {key: string; value: string}[], right: {key: string; value: string}[]): boolean { + if (left.length !== right.length) { + return false; + } + + return left.every((owner, index) => owner.key === right[index]?.key && owner.value === right[index]?.value); + } } diff --git a/src/app/modules/shows/list/list.component.ts b/src/app/modules/shows/list/list.component.ts index d248458..4950fe6 100644 --- a/src/app/modules/shows/list/list.component.ts +++ b/src/app/modules/shows/list/list.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, inject} from '@angular/core'; import {combineLatest} from 'rxjs'; import {Show} from '../services/show'; import {fade} from '../../../animations'; @@ -22,8 +22,9 @@ import {SortByPipe} from '../../../widget-modules/pipes/sort-by/sort-by.pipe'; imports: [RoleDirective, ListHeaderComponent, FilterComponent, CardComponent, ListItemComponent, RouterLink, AsyncPipe, SortByPipe], }) export class ListComponent { - public shows$ = this.showService.list$(); - public privateShows$ = this.showService.list$().pipe(map(show => show.filter(_ => !_.published))); + private showService = inject(ShowService); + private activatedRoute = inject(ActivatedRoute); + public lastMonths$ = this.activatedRoute.queryParams.pipe( map(params => { const filterValues = params as FilterValues; @@ -43,8 +44,9 @@ export class ListComponent { return filterValues?.showType; }) ); + public shows$ = this.showService.list$(); + public privateShows$ = this.showService.list$().pipe(map(show => show.filter(_ => !_.published))); public queriedPublicShows$ = this.lastMonths$.pipe(switchMap(lastMonths => this.showService.listPublicSince$(lastMonths))); - public fallbackPublicShows$ = combineLatest([this.shows$, this.lastMonths$]).pipe( map(([shows, lastMonths]) => { const startDate = new Date(); @@ -54,7 +56,6 @@ export class ListComponent { return shows.filter(show => show.published && !show.archived && show.date.toDate() >= startDate); }) ); - public publicShows$ = combineLatest([this.queriedPublicShows$, this.fallbackPublicShows$, this.owner$, this.showType$]).pipe( map(([queriedShows, fallbackShows, owner, showType]) => { const shows = queriedShows.length > 0 || fallbackShows.length === 0 ? queriedShows : fallbackShows; @@ -63,10 +64,5 @@ export class ListComponent { }) ); - public constructor( - private showService: ShowService, - private activatedRoute: ActivatedRoute - ) {} - public trackBy = (index: number, show: unknown) => (show as Show).id; } diff --git a/src/app/modules/shows/new/new.component.ts b/src/app/modules/shows/new/new.component.ts index 58c4eab..40b8b2d 100644 --- a/src/app/modules/shows/new/new.component.ts +++ b/src/app/modules/shows/new/new.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {ShowDataService} from '../services/show-data.service'; import {Observable} from 'rxjs'; import {Show} from '../services/show'; @@ -40,6 +40,9 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s ], }) export class NewComponent implements OnInit { + private showService = inject(ShowService); + private router = inject(Router); + public shows$: Observable; public showTypePublic = ShowService.SHOW_TYPE_PUBLIC; public showTypePrivate = ShowService.SHOW_TYPE_PRIVATE; @@ -49,11 +52,9 @@ export class NewComponent implements OnInit { }); public faSave = faSave; - public constructor( - private showService: ShowService, - showDataService: ShowDataService, - private router: Router - ) { + public constructor() { + const showDataService = inject(ShowDataService); + this.shows$ = showDataService.list$; } diff --git a/src/app/modules/shows/services/docx.service.ts b/src/app/modules/shows/services/docx.service.ts index bec4ebe..65e76c2 100644 --- a/src/app/modules/shows/services/docx.service.ts +++ b/src/app/modules/shows/services/docx.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Document, HeadingLevel, ISectionOptions, Packer, Paragraph} from 'docx'; import {ShowService} from './show.service'; import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/show-type.pipe'; @@ -26,13 +26,11 @@ export interface DownloadOptions { providedIn: 'root', }) export class DocxService { - public constructor( - private showService: ShowService, - private showSongService: ShowSongService, - private textRenderingService: TextRenderingService, - private userService: UserService, - private configService: ConfigService - ) {} + private showService = inject(ShowService); + private showSongService = inject(ShowSongService); + private textRenderingService = inject(TextRenderingService); + private userService = inject(UserService); + private configService = inject(ConfigService); public async create(showId: string, options: DownloadOptions = {}): Promise { const data = await this.prepareData(showId); @@ -194,17 +192,17 @@ export class DocxService { if (!config) return null; const showSongs = await this.showSongService.list(showId); - const songsAsync = showSongs.map(showSong => { - const sections = this.textRenderingService.parse(showSong.text, { - baseKey: showSong.keyOriginal, - targetKey: showSong.key, - }); - return { - showSong, - sections, - }; - }); - const songsLoaded = (await Promise.all(songsAsync)) + const songsLoaded = showSongs + .map(showSong => { + const sections = this.textRenderingService.parse(showSong.text, { + baseKey: showSong.keyOriginal, + targetKey: showSong.key, + }); + return { + showSong, + sections, + }; + }) .filter(_ => !!_) .map( _ => diff --git a/src/app/modules/shows/services/show-data.service.ts b/src/app/modules/shows/services/show-data.service.ts index 6f8afcd..6af5825 100644 --- a/src/app/modules/shows/services/show-data.service.ts +++ b/src/app/modules/shows/services/show-data.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Observable} from 'rxjs'; import {DbService} from '../../../services/db.service'; import {Show} from './show'; @@ -9,6 +9,8 @@ import {orderBy, QueryConstraint, Timestamp, where} from '@angular/fire/firestor providedIn: 'root', }) export class ShowDataService { + private dbService = inject(DbService); + private collection = 'shows'; public list$: Observable = this.dbService.col$(this.collection).pipe( // server-side ordering cuts client work and keeps stable order across subscribers @@ -19,8 +21,6 @@ export class ShowDataService { }) ); - public constructor(private dbService: DbService) {} - public listRaw$ = () => this.dbService.col$(this.collection); public listPublicSince$(lastMonths: number): Observable { diff --git a/src/app/modules/shows/services/show-song-data.service.ts b/src/app/modules/shows/services/show-song-data.service.ts index dc8a415..b73f575 100644 --- a/src/app/modules/shows/services/show-song-data.service.ts +++ b/src/app/modules/shows/services/show-song-data.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {DbService} from '../../../services/db.service'; import {Observable} from 'rxjs'; import {ShowSong} from './show-song'; @@ -9,12 +9,12 @@ import {shareReplay} from 'rxjs/operators'; providedIn: 'root', }) export class ShowSongDataService { + private dbService = inject(DbService); + private collection = 'shows'; private subCollection = 'songs'; private listCache = new Map>(); - public constructor(private dbService: DbService) {} - public list$ = (showId: string, queryConstraints?: QueryConstraint[]): Observable => { if (queryConstraints && queryConstraints.length > 0) { return this.dbService.col$(`${this.collection}/${showId}/${this.subCollection}`, queryConstraints); diff --git a/src/app/modules/shows/services/show-song.service.ts b/src/app/modules/shows/services/show-song.service.ts index e614f28..3fdd330 100644 --- a/src/app/modules/shows/services/show-song.service.ts +++ b/src/app/modules/shows/services/show-song.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {ShowSongDataService} from './show-song-data.service'; import {firstValueFrom, Observable} from 'rxjs'; import {ShowSong} from './show-song'; @@ -10,12 +10,10 @@ import {ShowService} from './show.service'; providedIn: 'root', }) export class ShowSongService { - public constructor( - private showSongDataService: ShowSongDataService, - private songDataService: SongDataService, - private userService: UserService, - private showService: ShowService - ) {} + private showSongDataService = inject(ShowSongDataService); + private songDataService = inject(SongDataService); + private userService = inject(UserService); + private showService = inject(ShowService); public async new$(showId: string, songId: string, addedLive = false): Promise { const song = await firstValueFrom(this.songDataService.read$(songId)); diff --git a/src/app/modules/shows/services/show.service.ts b/src/app/modules/shows/services/show.service.ts index 3f51978..9123d81 100644 --- a/src/app/modules/shows/services/show.service.ts +++ b/src/app/modules/shows/services/show.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {ShowDataService} from './show-data.service'; import {Show} from './show'; import {Observable} from 'rxjs'; @@ -10,15 +10,17 @@ import {User} from '../../../services/user/user'; providedIn: 'root', }) export class ShowService { + private showDataService = inject(ShowDataService); + private userService = inject(UserService); + public static SHOW_TYPE = ['service-worship', 'service-praise', 'home-group-big', 'home-group', 'prayer-group', 'teens-group', 'kids-group', 'misc-public', 'misc-private']; public static SHOW_TYPE_PUBLIC = ['service-worship', 'service-praise', 'home-group-big', 'teens-group', 'kids-group', 'misc-public']; public static SHOW_TYPE_PRIVATE = ['home-group', 'prayer-group', 'misc-private']; private user: User | null = null; - public constructor( - private showDataService: ShowDataService, - private userService: UserService - ) { + public constructor() { + const userService = this.userService; + userService.user$.subscribe(_ => (this.user = _)); } diff --git a/src/app/modules/shows/show/show.component.ts b/src/app/modules/shows/show/show.component.ts index 538220d..6714cd1 100644 --- a/src/app/modules/shows/show/show.component.ts +++ b/src/app/modules/shows/show/show.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, HostListener, OnDestroy, OnInit} from '@angular/core'; +import {ChangeDetectorRef, Component, CUSTOM_ELEMENTS_SCHEMA, HostListener, OnDestroy, OnInit, inject} from '@angular/core'; import {filter, map, shareReplay, switchMap, tap} from 'rxjs/operators'; import {ActivatedRoute, Router} from '@angular/router'; import {ShowService} from '../services/show.service'; @@ -80,6 +80,16 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s ], }) export class ShowComponent implements OnInit, OnDestroy { + private activatedRoute = inject(ActivatedRoute); + private showService = inject(ShowService); + private songService = inject(SongService); + private showSongService = inject(ShowSongService); + private docxService = inject(DocxService); + private router = inject(Router); + private cRef = inject(ChangeDetectorRef); + public dialog = inject(MatDialog); + private guestShowService = inject(GuestShowService); + public show$: Observable | null = null; public songs$: Observable | null = null; public showSongs: ShowSong[] | null = null; @@ -105,18 +115,6 @@ export class ShowComponent implements OnInit, OnDestroy { public currentTime: Date; private subs: Subscription[] = []; - public constructor( - private activatedRoute: ActivatedRoute, - private showService: ShowService, - private songService: SongService, - private showSongService: ShowSongService, - private docxService: DocxService, - private router: Router, - private cRef: ChangeDetectorRef, - public dialog: MatDialog, - private guestShowService: GuestShowService - ) {} - public ngOnInit(): void { this.currentTime = new Date(); setInterval(() => { diff --git a/src/app/modules/shows/show/song/song.component.ts b/src/app/modules/shows/show/song/song.component.ts index 442e39a..db32cd1 100644 --- a/src/app/modules/shows/show/song/song.component.ts +++ b/src/app/modules/shows/show/song/song.component.ts @@ -1,4 +1,4 @@ -import {Component, Input, OnInit, ViewChild} from '@angular/core'; +import {Component, Input, OnInit, ViewChild, inject} from '@angular/core'; import {ShowSongService} from '../../services/show-song.service'; import {ShowSong} from '../../services/show-song'; import {getScale} from '../../../songs/services/key.helper'; @@ -39,6 +39,8 @@ import {ButtonComponent} from '../../../../widget-modules/components/button/butt ], }) export class SongComponent implements OnInit { + private showSongService = inject(ShowSongService); + @Input() public show: Show | null = null; @Input() public showId: string | null = null; @Input() public showText: boolean | null = null; @@ -55,8 +57,6 @@ export class SongComponent implements OnInit { public editSongControl = new UntypedFormControl(); @ViewChild('option') private keyOptions: MatSelect; - public constructor(private showSongService: ShowSongService) {} - @Input() public set showSong(song: ShowSong) { this.iSong = song; diff --git a/src/app/modules/songs/services/file-data.service.ts b/src/app/modules/songs/services/file-data.service.ts index 7c4b2c2..28ae71a 100644 --- a/src/app/modules/songs/services/file-data.service.ts +++ b/src/app/modules/songs/services/file-data.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {File} from './file'; import {Observable} from 'rxjs'; import {FileServer} from './fileServer'; @@ -8,7 +8,7 @@ import {DbService} from '../../../services/db.service'; providedIn: 'root', }) export class FileDataService { - public constructor(private db: DbService) {} + private db = inject(DbService); public async set(songId: string, file: FileServer): Promise { const songRef = this.db.doc('songs/' + songId); diff --git a/src/app/modules/songs/services/file.service.ts b/src/app/modules/songs/services/file.service.ts index fcd7ed2..f26876e 100644 --- a/src/app/modules/songs/services/file.service.ts +++ b/src/app/modules/songs/services/file.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {deleteObject, getDownloadURL, ref, Storage} from '@angular/fire/storage'; import {from, Observable} from 'rxjs'; import {FileDataService} from './file-data.service'; @@ -7,10 +7,8 @@ import {FileDataService} from './file-data.service'; providedIn: 'root', }) export class FileService { - public constructor( - private storage: Storage, - private fileDataService: FileDataService - ) {} + private storage = inject(Storage); + private fileDataService = inject(FileDataService); public getDownloadUrl(path: string): Observable { return from(getDownloadURL(ref(this.storage, path))); diff --git a/src/app/modules/songs/services/key.helper.spec.ts b/src/app/modules/songs/services/key.helper.spec.ts index 8879eaa..c0ff541 100644 --- a/src/app/modules/songs/services/key.helper.spec.ts +++ b/src/app/modules/songs/services/key.helper.spec.ts @@ -2,14 +2,14 @@ import {getScale, scaleMapping} from './key.helper'; describe('key.helper', () => { it('should render Gb correctly', () => { - expect(scaleMapping['Gb']).toBe('G♭'); + void expect(scaleMapping['Gb']).toBe('G♭'); }); it('should expose a sharp-based scale for D', () => { - expect(getScale('D')).toEqual(['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H']); + void expect(getScale('D')).toEqual(['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'H']); }); it('should keep flat-based spelling for Db', () => { - expect(getScale('Db')).toEqual(['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'B', 'H']); + void expect(getScale('Db')).toEqual(['C', 'Db', 'D', 'Eb', 'E', 'F', 'Gb', 'G', 'Ab', 'A', 'B', 'H']); }); }); diff --git a/src/app/modules/songs/services/song-data.service.ts b/src/app/modules/songs/services/song-data.service.ts index 15ecd07..2d25473 100644 --- a/src/app/modules/songs/services/song-data.service.ts +++ b/src/app/modules/songs/services/song-data.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Song} from './song'; import {Observable} from 'rxjs'; import {DbService} from '../../../services/db.service'; @@ -8,6 +8,8 @@ import {shareReplay, startWith} from 'rxjs/operators'; providedIn: 'root', }) export class SongDataService { + private dbService = inject(DbService); + private collection = 'songs'; public list$: Observable = this.dbService.col$(this.collection).pipe( startWith([] as Song[]), // immediate empty emit keeps UI responsive while first snapshot arrives @@ -17,11 +19,6 @@ export class SongDataService { }) ); - public constructor(private dbService: DbService) { - // Warm the shared stream once at startup to avoid first-navigation delay. - // this.list$.subscribe(); - } - public read$ = (songId: string): Observable => this.dbService.doc$(this.collection + '/' + songId); public update$ = async (songId: string, data: Partial): Promise => await this.dbService.doc(this.collection + '/' + songId).update(data); public add = async (data: Partial): Promise => (await this.dbService.col(this.collection).add(data)).id; diff --git a/src/app/modules/songs/services/song-list.resolver.ts b/src/app/modules/songs/services/song-list.resolver.ts index 18d635e..d1d0424 100644 --- a/src/app/modules/songs/services/song-list.resolver.ts +++ b/src/app/modules/songs/services/song-list.resolver.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Observable} from 'rxjs'; import {SongService} from './song.service'; @@ -9,7 +9,7 @@ import {take} from 'rxjs/operators'; providedIn: 'root', }) export class SongListResolver { - public constructor(private songService: SongService) {} + private songService = inject(SongService); public resolve(): Observable { return this.songService.list$().pipe(take(1)); diff --git a/src/app/modules/songs/services/song.service.ts b/src/app/modules/songs/services/song.service.ts index 7eb6821..a80c907 100644 --- a/src/app/modules/songs/services/song.service.ts +++ b/src/app/modules/songs/services/song.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {firstValueFrom, Observable} from 'rxjs'; import {Song} from './song'; import {SongDataService} from './song-data.service'; @@ -16,21 +16,15 @@ export type SongLegalType = 'open' | 'allowed'; providedIn: 'root', }) export class SongService { + private songDataService = inject(SongDataService); + private userService = inject(UserService); + public static TYPES: SongType[] = ['Praise', 'Worship', 'Misc']; public static STATUS: SongStatus[] = ['draft', 'set', 'final']; public static LEGAL_OWNER: SongLegalOwner[] = ['CCLI', 'other']; public static LEGAL_TYPE: SongLegalType[] = ['open', 'allowed']; - // private list: Song[]; - - public constructor( - private songDataService: SongDataService, - private userService: UserService - ) { - // importCCLI = (songs: Song[]) => this.updateFromCLI(songs); - } - public list$ = (): Observable => this.songDataService.list$; //.pipe(tap(_ => (this.list = _))); public read$ = (songId: string): Observable => this.songDataService.read$(songId); public read = (songId: string): Promise => firstValueFrom(this.read$(songId)); diff --git a/src/app/modules/songs/services/text-rendering.service.ts b/src/app/modules/songs/services/text-rendering.service.ts index 16399e2..a598c1d 100644 --- a/src/app/modules/songs/services/text-rendering.service.ts +++ b/src/app/modules/songs/services/text-rendering.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {TransposeService} from './transpose.service'; import {TransposeMode} from './transpose-mode'; import {SectionType} from './section-type'; @@ -11,9 +11,9 @@ import {Line} from './line'; providedIn: 'root', }) export class TextRenderingService { - private regexSection = /(Strophe|Refrain|Bridge)/; + private transposeService = inject(TransposeService); - public constructor(private transposeService: TransposeService) {} + private regexSection = /(Strophe|Refrain|Bridge)/; public parse(text: string, transpose: TransposeMode | null, withComments = true): Section[] { if (!text) { diff --git a/src/app/modules/songs/services/transpose.service.spec.ts b/src/app/modules/songs/services/transpose.service.spec.ts index 1e548ac..9ad1b6d 100644 --- a/src/app/modules/songs/services/transpose.service.spec.ts +++ b/src/app/modules/songs/services/transpose.service.spec.ts @@ -34,18 +34,18 @@ describe('TransposeService', () => { const distance = service.getDistance('C', 'Db'); const map = service.getMap('C', 'Db', distance); - expect(distance).toBe(1); - expect(map?.['C']).toBe('Db'); - expect(map?.['G']).toBe('Ab'); + void expect(distance).toBe(1); + void expect(map?.['C']).toBe('Db'); + void expect(map?.['G']).toBe('Ab'); }); it('should keep german B/H notation consistent', () => { const distance = service.getDistance('H', 'C'); const map = service.getMap('H', 'C', distance); - expect(distance).toBe(1); - expect(map?.['H']).toBe('C'); - expect(map?.['B']).toBe('C#'); + void expect(distance).toBe(1); + void expect(map?.['H']).toBe('C'); + void expect(map?.['B']).toBe('C#'); }); it('should render unknown chords as X', () => { @@ -57,7 +57,7 @@ describe('TransposeService', () => { const rendered = service.renderChords(line); - expect(rendered.text).toBe('Xsus4'); + void expect(rendered.text).toBe('Xsus4'); }); it('should render unknown slash chords as X', () => { @@ -69,7 +69,7 @@ describe('TransposeService', () => { const rendered = service.renderChords(line); - expect(rendered.text).toBe('C/X'); + void expect(rendered.text).toBe('C/X'); }); it('should transpose lines with long chord positions without truncating', () => { @@ -81,7 +81,7 @@ describe('TransposeService', () => { const rendered = service.renderChords(line); - expect(rendered.text.length).toBe(121); - expect(rendered.text.endsWith('C')).toBeTrue(); + void expect(rendered.text.length).toBe(121); + void expect(rendered.text.endsWith('C')).toBeTrue(); }); }); diff --git a/src/app/modules/songs/services/upload.service.ts b/src/app/modules/songs/services/upload.service.ts index 4905f8f..9c59295 100644 --- a/src/app/modules/songs/services/upload.service.ts +++ b/src/app/modules/songs/services/upload.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Upload} from './upload'; import {FileDataService} from './file-data.service'; import {ref, Storage, uploadBytesResumable} from '@angular/fire/storage'; @@ -9,12 +9,8 @@ import {FileServer} from './fileServer'; providedIn: 'root', }) export class UploadService extends FileBase { - public constructor( - private fileDataService: FileDataService, - private storage: Storage - ) { - super(); - } + private fileDataService = inject(FileDataService); + private storage = inject(Storage); public pushUpload(songId: string, upload: Upload): void { const directory = this.directory(songId); diff --git a/src/app/modules/songs/song-list/filter/filter.component.ts b/src/app/modules/songs/song-list/filter/filter.component.ts index ebbac97..abdc6ba 100644 --- a/src/app/modules/songs/song-list/filter/filter.component.ts +++ b/src/app/modules/songs/song-list/filter/filter.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, inject} from '@angular/core'; import {ActivatedRoute, Router} from '@angular/router'; import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormGroup} from '@angular/forms'; import {SongService} from '../../services/song.service'; @@ -21,6 +21,8 @@ import {SongTypePipe} from '../../../../widget-modules/pipes/song-type-translate imports: [ReactiveFormsModule, MatFormField, MatLabel, MatInput, MatSelect, MatOption, LegalTypePipe, KeyPipe, SongTypePipe], }) export class FilterComponent { + private router = inject(Router); + public filterFormGroup: UntypedFormGroup; @Input() public route = '/'; @Input() public songs: Song[] = []; @@ -28,11 +30,10 @@ export class FilterComponent { public legalType = SongService.LEGAL_TYPE; public keys = KEYS; - public constructor( - private router: Router, - activatedRoute: ActivatedRoute, - fb: UntypedFormBuilder - ) { + public constructor() { + const activatedRoute = inject(ActivatedRoute); + const fb = inject(UntypedFormBuilder); + this.filterFormGroup = fb.group({ q: '', type: '', diff --git a/src/app/modules/songs/song-list/song-list.component.ts b/src/app/modules/songs/song-list/song-list.component.ts index f9d6fb0..f4f7129 100644 --- a/src/app/modules/songs/song-list/song-list.component.ts +++ b/src/app/modules/songs/song-list/song-list.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectionStrategy, Component, OnDestroy, OnInit} from '@angular/core'; +import {ChangeDetectionStrategy, Component, OnDestroy, OnInit, inject} from '@angular/core'; import {SongService} from '../services/song.service'; import {Song} from '../services/song'; import {map} from 'rxjs/operators'; @@ -25,6 +25,10 @@ import {FaIconComponent} from '@fortawesome/angular-fontawesome'; imports: [ListHeaderComponent, FilterComponent, CardComponent, RouterLink, RoleDirective, FaIconComponent, AsyncPipe], }) export class SongListComponent implements OnInit, OnDestroy { + private songService = inject(SongService); + private activatedRoute = inject(ActivatedRoute); + private scrollService = inject(ScrollService); + public anyFilterActive = false; public songs$: Observable = combineLatest([ this.activatedRoute.queryParams.pipe(map(_ => _ as FilterValues)), @@ -39,12 +43,6 @@ export class SongListComponent implements OnInit, OnDestroy { public faDraft = faPencilRuler; public faFinal = faCheck; - public constructor( - private songService: SongService, - private activatedRoute: ActivatedRoute, - private scrollService: ScrollService - ) {} - public ngOnInit(): void { setTimeout(() => this.scrollService.restoreScrollPositionFor('songlist'), 100); setTimeout(() => this.scrollService.restoreScrollPositionFor('songlist'), 300); diff --git a/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts b/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts index 5924d8e..8d631ca 100644 --- a/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts +++ b/src/app/modules/songs/song/edit/edit-file/edit-file.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, inject} from '@angular/core'; import {Upload} from '../../../services/upload'; import {UploadService} from '../../../services/upload.service'; import {ActivatedRoute} from '@angular/router'; @@ -19,16 +19,16 @@ import {FileComponent} from './file/file.component'; imports: [CardComponent, NgStyle, MatIconButton, MatIcon, FileComponent, AsyncPipe], }) export class EditFileComponent { + private activatedRoute = inject(ActivatedRoute); + private uploadService = inject(UploadService); + private fileService = inject(FileDataService); + public selectedFiles: FileList | null = null; public currentUpload: Upload | null = null; public songId: string | null = null; public files$: Observable; - public constructor( - private activatedRoute: ActivatedRoute, - private uploadService: UploadService, - private fileService: FileDataService - ) { + public constructor() { this.activatedRoute.params .pipe( map(param => param as {songId: string}), diff --git a/src/app/modules/songs/song/edit/edit-file/file/file.component.ts b/src/app/modules/songs/song/edit/edit-file/file/file.component.ts index b322982..6796578 100644 --- a/src/app/modules/songs/song/edit/edit-file/file/file.component.ts +++ b/src/app/modules/songs/song/edit/edit-file/file/file.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, inject} from '@angular/core'; import {Observable} from 'rxjs'; import {File} from '../../../../services/file'; import {faTrashAlt} from '@fortawesome/free-solid-svg-icons'; @@ -14,6 +14,8 @@ import {AsyncPipe} from '@angular/common'; imports: [MatIconButton, FaIconComponent, AsyncPipe], }) export class FileComponent { + private fileService = inject(FileService); + public url$: Observable | null = null; public name = ''; public faTrash = faTrashAlt; @@ -21,8 +23,6 @@ export class FileComponent { private fileId: string | null = null; private path: string | null = null; - public constructor(private fileService: FileService) {} - @Input() public set file(file: File) { this.url$ = this.fileService.getDownloadUrl(file.path + '/' + file.name); diff --git a/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts b/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts index 30f7776..323d488 100644 --- a/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts +++ b/src/app/modules/songs/song/edit/edit-song/edit-song.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {Song} from '../../../services/song'; import {ReactiveFormsModule, UntypedFormGroup} from '@angular/forms'; import {ActivatedRoute, Router, RouterStateSnapshot} from '@angular/router'; @@ -59,6 +59,12 @@ import {StatusPipe} from '../../../../../widget-modules/pipes/status-translater/ ], }) export class EditSongComponent implements OnInit { + private activatedRoute = inject(ActivatedRoute); + private songService = inject(SongService); + private editService = inject(EditService); + private router = inject(Router); + public dialog = inject(MatDialog); + public song: Song | null = null; public form: UntypedFormGroup = new UntypedFormGroup({}); public keys = KEYS; @@ -73,14 +79,6 @@ export class EditSongComponent implements OnInit { public faLink = faExternalLinkAlt; public songtextFocus = false; - public constructor( - private activatedRoute: ActivatedRoute, - private songService: SongService, - private editService: EditService, - private router: Router, - public dialog: MatDialog - ) {} - public ngOnInit(): void { this.activatedRoute.params .pipe( diff --git a/src/app/modules/songs/song/edit/history/history.component.ts b/src/app/modules/songs/song/edit/history/history.component.ts index 9fabdd9..6c99261 100644 --- a/src/app/modules/songs/song/edit/history/history.component.ts +++ b/src/app/modules/songs/song/edit/history/history.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {first, map, switchMap} from 'rxjs/operators'; import {ActivatedRoute} from '@angular/router'; import {SongService} from '../../../services/song.service'; @@ -13,12 +13,10 @@ import {CardComponent} from '../../../../../widget-modules/components/card/card. imports: [CardComponent, DatePipe], }) export class HistoryComponent implements OnInit { - public song: Song | null = null; + private activatedRoute = inject(ActivatedRoute); + private songService = inject(SongService); - public constructor( - private activatedRoute: ActivatedRoute, - private songService: SongService - ) {} + public song: Song | null = null; public ngOnInit(): void { this.activatedRoute.params diff --git a/src/app/modules/songs/song/file/file.component.ts b/src/app/modules/songs/song/file/file.component.ts index 4ece8f8..9b8cee7 100644 --- a/src/app/modules/songs/song/file/file.component.ts +++ b/src/app/modules/songs/song/file/file.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, inject} from '@angular/core'; import {File} from '../../services/file'; import {getDownloadURL, ref, Storage} from '@angular/fire/storage'; import {from, Observable} from 'rxjs'; @@ -11,11 +11,11 @@ import {AsyncPipe} from '@angular/common'; imports: [AsyncPipe], }) export class FileComponent { + private storage = inject(Storage); + public url$: Observable | null = null; public name = ''; - public constructor(private storage: Storage) {} - @Input() public set file(file: File) { this.url$ = from(getDownloadURL(ref(this.storage, file.path + '/' + file.name))); diff --git a/src/app/modules/songs/song/new/new.component.ts b/src/app/modules/songs/song/new/new.component.ts index 12b48b2..c4303b6 100644 --- a/src/app/modules/songs/song/new/new.component.ts +++ b/src/app/modules/songs/song/new/new.component.ts @@ -1,4 +1,4 @@ -import {Component, OnDestroy, OnInit} from '@angular/core'; +import {Component, OnDestroy, OnInit, inject} from '@angular/core'; import {faSave} from '@fortawesome/free-solid-svg-icons'; import {ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms'; import {SongService} from '../../services/song.service'; @@ -19,6 +19,9 @@ import {ButtonComponent} from '../../../../widget-modules/components/button/butt imports: [CardComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, ButtonRowComponent, ButtonComponent], }) export class NewComponent implements OnInit, OnDestroy { + private songService = inject(SongService); + private router = inject(Router); + public faSave = faSave; public form: UntypedFormGroup = new UntypedFormGroup({ number: new UntypedFormControl(null, Validators.required), @@ -26,11 +29,6 @@ export class NewComponent implements OnInit, OnDestroy { }); private subs: Subscription[] = []; - public constructor( - private songService: SongService, - private router: Router - ) {} - public ngOnInit(): void { this.form.reset(); diff --git a/src/app/modules/songs/song/song.component.ts b/src/app/modules/songs/song/song.component.ts index 32f6ece..5f5a088 100644 --- a/src/app/modules/songs/song/song.component.ts +++ b/src/app/modules/songs/song/song.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {ActivatedRoute, Router, RouterLink} from '@angular/router'; import {SongService} from '../services/song.service'; import {distinctUntilChanged, map, switchMap} from 'rxjs/operators'; @@ -51,6 +51,14 @@ import {ShowTypePipe} from '../../../widget-modules/pipes/show-type-translater/s ], }) export class SongComponent implements OnInit { + private activatedRoute = inject(ActivatedRoute); + private songService = inject(SongService); + private fileService = inject(FileDataService); + private userService = inject(UserService); + private router = inject(Router); + private showService = inject(ShowService); + private showSongService = inject(ShowSongService); + public song$: Observable | null = null; public files$: Observable | null = null; public user$: Observable | null = null; @@ -60,15 +68,9 @@ export class SongComponent implements OnInit { public faFileCirclePlus = faFileCirclePlus; public privateShows$ = this.showService.list$().pipe(map(show => show.filter(_ => !_.published).sort((a, b) => b.date.toMillis() - a.date.toMillis()))); - public constructor( - private activatedRoute: ActivatedRoute, - private songService: SongService, - private fileService: FileDataService, - private userService: UserService, - private router: Router, - private showService: ShowService, - private showSongService: ShowSongService - ) { + public constructor() { + const userService = this.userService; + this.user$ = userService.user$; } diff --git a/src/app/modules/user/info/info.component.ts b/src/app/modules/user/info/info.component.ts index 457bbe1..61d05ef 100644 --- a/src/app/modules/user/info/info.component.ts +++ b/src/app/modules/user/info/info.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {UserService} from '../../../services/user/user.service'; import {Observable} from 'rxjs'; import {User} from '../../../services/user/user'; @@ -40,11 +40,11 @@ import {UsersComponent} from './users/users.component'; ], }) export class InfoComponent implements OnInit { + private userService = inject(UserService); + public user$: Observable | null = null; public faSignOut = faSignOutAlt; - public constructor(private userService: UserService) {} - public ngOnInit(): void { this.user$ = this.userService.user$; } diff --git a/src/app/modules/user/info/users/user/user.component.ts b/src/app/modules/user/info/users/user/user.component.ts index dad181e..723adda 100644 --- a/src/app/modules/user/info/users/user/user.component.ts +++ b/src/app/modules/user/info/users/user/user.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, inject} from '@angular/core'; import {User} from '../../../../../services/user/user'; import {UserService} from '../../../../../services/user/user.service'; import {ROLE_TYPES} from '../../../../../services/user/roles'; @@ -20,6 +20,8 @@ import {RolePipe} from '../../role.pipe'; imports: [MatFormField, MatLabel, MatInput, ReactiveFormsModule, FormsModule, MatSelect, MatOption, MatIconButton, FaIconComponent, RolePipe], }) export class UserComponent { + private userService = inject(UserService); + public id = ''; public name = ''; public roles: string[] = []; @@ -27,8 +29,6 @@ export class UserComponent { public edit = false; public faClose = faTimes; - public constructor(private userService: UserService) {} - @Input() public set user(value: User) { this.id = value.id; diff --git a/src/app/modules/user/info/users/users.component.ts b/src/app/modules/user/info/users/users.component.ts index 6dafa2d..765d88e 100644 --- a/src/app/modules/user/info/users/users.component.ts +++ b/src/app/modules/user/info/users/users.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, inject} from '@angular/core'; import {UserService} from '../../../../services/user/user.service'; import {Observable} from 'rxjs'; import {User} from '../../../../services/user/user'; @@ -14,9 +14,13 @@ import {SortByPipe} from '../../../../widget-modules/pipes/sort-by/sort-by.pipe' imports: [CardComponent, UserComponent, AsyncPipe, SortByPipe], }) export class UsersComponent { + private userService = inject(UserService); + public users$: Observable; - public constructor(private userService: UserService) { + public constructor() { + const userService = this.userService; + this.users$ = userService.list$(); } } diff --git a/src/app/modules/user/login/login.component.ts b/src/app/modules/user/login/login.component.ts index cd9e1b9..062b0eb 100644 --- a/src/app/modules/user/login/login.component.ts +++ b/src/app/modules/user/login/login.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms'; import {Router, RouterLink} from '@angular/router'; import {UserService} from '../../../services/user/user.service'; @@ -17,6 +17,9 @@ import {AuthMessagePipe} from './auth-message.pipe'; imports: [LogoComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, MatButton, RouterLink, AuthMessagePipe], }) export class LoginComponent implements OnInit { + private userService = inject(UserService); + private router = inject(Router); + public form: UntypedFormGroup = new UntypedFormGroup({ user: new UntypedFormControl(null, [Validators.required, Validators.email]), pass: new UntypedFormControl(null, [Validators.required]), @@ -25,13 +28,8 @@ export class LoginComponent implements OnInit { public faSignIn = faSignInAlt; public faNewUser = faUserPlus; - public constructor( - private userService: UserService, - private router: Router - ) {} - public ngOnInit(): void { - this.form.reset; + this.form.reset(); } public async onLogin(): Promise { diff --git a/src/app/modules/user/logout/logout.component.ts b/src/app/modules/user/logout/logout.component.ts index 7fb297b..edee810 100644 --- a/src/app/modules/user/logout/logout.component.ts +++ b/src/app/modules/user/logout/logout.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component} from '@angular/core'; +import {AfterViewInit, Component, inject} from '@angular/core'; import {Router} from '@angular/router'; import {UserService} from '../../../services/user/user.service'; @@ -8,13 +8,13 @@ import {UserService} from '../../../services/user/user.service'; styleUrls: ['./logout.component.less'], }) export class LogoutComponent implements AfterViewInit { - public constructor( - private userService: UserService, - private router: Router - ) {} + private userService = inject(UserService); + private router = inject(Router); - public async ngAfterViewInit(): Promise { - await this.userService.logout(); - await this.router.navigateByUrl('/'); + public ngAfterViewInit(): void { + void (async () => { + await this.userService.logout(); + await this.router.navigateByUrl('/'); + })(); } } diff --git a/src/app/modules/user/new/new.component.ts b/src/app/modules/user/new/new.component.ts index 8a64a60..f551503 100644 --- a/src/app/modules/user/new/new.component.ts +++ b/src/app/modules/user/new/new.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {ReactiveFormsModule, UntypedFormBuilder, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms'; import {UserService} from '../../../services/user/user.service'; import {faUserPlus} from '@fortawesome/free-solid-svg-icons'; @@ -15,6 +15,9 @@ import {ButtonComponent} from '../../../widget-modules/components/button/button. imports: [CardComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, ButtonRowComponent, ButtonComponent], }) export class NewComponent implements OnInit { + private fb = inject(UntypedFormBuilder); + private userService = inject(UserService); + public form: UntypedFormGroup = this.fb.group({ email: new UntypedFormControl(null, [Validators.required, Validators.email]), name: new UntypedFormControl(null, [Validators.required]), @@ -22,11 +25,6 @@ export class NewComponent implements OnInit { }); public faNewUser = faUserPlus; - public constructor( - private fb: UntypedFormBuilder, - private userService: UserService - ) {} - public ngOnInit(): void { this.form.reset(); } diff --git a/src/app/modules/user/password/password.component.ts b/src/app/modules/user/password/password.component.ts index 40e38f2..1ebdfb2 100644 --- a/src/app/modules/user/password/password.component.ts +++ b/src/app/modules/user/password/password.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import {ReactiveFormsModule, UntypedFormControl, UntypedFormGroup, Validators} from '@angular/forms'; import {Router} from '@angular/router'; import {UserService} from '../../../services/user/user.service'; @@ -18,6 +18,9 @@ import {AuthMessagePipe} from '../login/auth-message.pipe'; imports: [CardComponent, ReactiveFormsModule, MatFormField, MatLabel, MatInput, ButtonRowComponent, ButtonComponent, AuthMessagePipe], }) export class PasswordComponent implements OnInit { + public userService = inject(UserService); + private router = inject(Router); + public form: UntypedFormGroup = new UntypedFormGroup({ user: new UntypedFormControl(null, [Validators.required, Validators.email]), }); @@ -25,11 +28,6 @@ export class PasswordComponent implements OnInit { public errorMessage = ''; public faNewPassword = faWindowRestore; - public constructor( - public userService: UserService, - private router: Router - ) {} - public ngOnInit(): void { this.form.reset(); } diff --git a/src/app/services/config.service.ts b/src/app/services/config.service.ts index 2ea68ac..d68cc36 100644 --- a/src/app/services/config.service.ts +++ b/src/app/services/config.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {DbService} from './db.service'; import {firstValueFrom, Observable} from 'rxjs'; import {Config} from './config'; @@ -8,6 +8,8 @@ import {shareReplay} from 'rxjs/operators'; providedIn: 'root', }) export class ConfigService { + private db = inject(DbService); + private readonly config$ = this.db.doc$('global/config').pipe( shareReplay({ bufferSize: 1, @@ -15,8 +17,6 @@ export class ConfigService { }) ); - public constructor(private db: DbService) {} - public get$ = (): Observable => this.config$; public get = (): Promise => firstValueFrom(this.get$()); } diff --git a/src/app/services/db.service.ts b/src/app/services/db.service.ts index 39db2c7..1ed6922 100644 --- a/src/app/services/db.service.ts +++ b/src/app/services/db.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import { addDoc, collection, @@ -76,7 +76,7 @@ export class DbDocument { providedIn: 'root', }) export class DbService { - public constructor(private fs: Firestore) {} + private fs = inject(Firestore); public col(ref: CollectionPredicate): DbCollection { return typeof ref === 'string' ? new DbCollection(this.fs, ref) : ref; diff --git a/src/app/services/fullscreen.ts b/src/app/services/fullscreen.ts index 3620834..b72a018 100644 --- a/src/app/services/fullscreen.ts +++ b/src/app/services/fullscreen.ts @@ -6,12 +6,9 @@ export const openFullscreen = () => { } try { - const promise = elem.requestFullscreen(); - if (promise && typeof promise.catch === 'function') { - void promise.catch(() => { - // Browser may reject when no user gesture is present. Keep app usable. - }); - } + void elem.requestFullscreen().catch(() => { + // Browser may reject when no user gesture is present. Keep app usable. + }); } catch { // Some browsers may throw synchronously if fullscreen is not allowed. } @@ -19,11 +16,8 @@ export const openFullscreen = () => { export const closeFullscreen = () => { if (document.exitFullscreen) { - const promise = document.exitFullscreen(); - if (promise && typeof promise.catch === 'function') { - void promise.catch(() => { - // Ignore; leaving fullscreen is a best-effort action. - }); - } + void document.exitFullscreen().catch(() => { + // Ignore; leaving fullscreen is a best-effort action. + }); } }; diff --git a/src/app/services/global-settings.service.ts b/src/app/services/global-settings.service.ts index 36c507f..e66c536 100644 --- a/src/app/services/global-settings.service.ts +++ b/src/app/services/global-settings.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {DbService} from './db.service'; import {GlobalSettings} from './global-settings'; import {Observable} from 'rxjs'; @@ -8,6 +8,8 @@ import {shareReplay} from 'rxjs/operators'; providedIn: 'root', }) export class GlobalSettingsService { + private db = inject(DbService); + private readonly settings$ = this.db.doc$('global/static').pipe( shareReplay({ bufferSize: 1, @@ -15,8 +17,6 @@ export class GlobalSettingsService { }) ); - public constructor(private db: DbService) {} - public get get$(): Observable { return this.settings$; } diff --git a/src/app/services/user/owner.directive.ts b/src/app/services/user/owner.directive.ts index 3289200..99af7f0 100644 --- a/src/app/services/user/owner.directive.ts +++ b/src/app/services/user/owner.directive.ts @@ -1,18 +1,16 @@ -import {Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core'; +import {Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef, inject} from '@angular/core'; import {UserService} from './user.service'; @Directive({selector: '[appOwner]'}) export class OwnerDirective implements OnInit { + private element = inject(ElementRef); + private templateRef = inject>(TemplateRef); + private viewContainer = inject(ViewContainerRef); + private userService = inject(UserService); + private currentUserId: string | null = null; private iAppOwner: string | null = null; - public constructor( - private element: ElementRef, - private templateRef: TemplateRef, - private viewContainer: ViewContainerRef, - private userService: UserService - ) {} - @Input() public set appOwner(value: string) { this.iAppOwner = value; diff --git a/src/app/services/user/role.directive.ts b/src/app/services/user/role.directive.ts index 28aea85..280c3fc 100644 --- a/src/app/services/user/role.directive.ts +++ b/src/app/services/user/role.directive.ts @@ -1,4 +1,4 @@ -import {ChangeDetectorRef, Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef} from '@angular/core'; +import {ChangeDetectorRef, Directive, ElementRef, Input, OnInit, TemplateRef, ViewContainerRef, inject} from '@angular/core'; import {roles} from './roles'; import {UserService} from './user.service'; import {User} from './user'; @@ -6,19 +6,17 @@ import {combineLatest} from 'rxjs'; @Directive({selector: '[appRole]'}) export class RoleDirective implements OnInit { + private element = inject(ElementRef); + private templateRef = inject>(TemplateRef); + private viewContainer = inject(ViewContainerRef); + private userService = inject(UserService); + private changeDetection = inject(ChangeDetectorRef); + @Input() public appRole: roles[] = []; private currentUser: User | null = null; private loggedIn = false; private currentViewState = false; - public constructor( - private element: ElementRef, - private templateRef: TemplateRef, - private viewContainer: ViewContainerRef, - private userService: UserService, - private changeDetection: ChangeDetectorRef - ) {} - public ngOnInit(): void { combineLatest([this.userService.user$, this.userService.loggedIn$()]).subscribe(_ => { this.currentUser = _[0]; diff --git a/src/app/services/user/user-name/user-name.component.ts b/src/app/services/user/user-name/user-name.component.ts index 548b0eb..2effdd5 100644 --- a/src/app/services/user/user-name/user-name.component.ts +++ b/src/app/services/user/user-name/user-name.component.ts @@ -1,4 +1,4 @@ -import {Component, Input} from '@angular/core'; +import {Component, Input, inject} from '@angular/core'; import {UserService} from '../user.service'; import {map} from 'rxjs/operators'; import {Observable} from 'rxjs'; @@ -11,9 +11,9 @@ import {AsyncPipe} from '@angular/common'; imports: [AsyncPipe], }) export class UserNameComponent { - public name$: Observable | null = null; + private userService = inject(UserService); - public constructor(private userService: UserService) {} + public name$: Observable | null = null; @Input() public set userId(id: string) { diff --git a/src/app/services/user/user.service.ts b/src/app/services/user/user.service.ts index b4c3632..2c8b62f 100644 --- a/src/app/services/user/user.service.ts +++ b/src/app/services/user/user.service.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {Auth, authState, createUserWithEmailAndPassword, sendPasswordResetEmail, signInWithEmailAndPassword, signOut} from '@angular/fire/auth'; import {BehaviorSubject, firstValueFrom, Observable} from 'rxjs'; import {filter, map, shareReplay, switchMap, take, tap} from 'rxjs/operators'; @@ -20,18 +20,18 @@ export interface SongUsageMigrationResult { providedIn: 'root', }) export class UserService { + private auth = inject(Auth); + private db = inject(DbService); + private router = inject(Router); + private showDataService = inject(ShowDataService); + private showSongDataService = inject(ShowSongDataService); + public users$ = this.db.col$('users').pipe(shareReplay({bufferSize: 1, refCount: true})); private iUserId$ = new BehaviorSubject(null); private iUser$ = new BehaviorSubject(null); private userByIdCache = new Map>(); - public constructor( - private auth: Auth, - private db: DbService, - private router: Router, - private showDataService: ShowDataService, - private showSongDataService: ShowSongDataService - ) { + public constructor() { authState(this.auth) .pipe( filter(auth => !!auth), diff --git a/src/app/widget-modules/components/add-song/add-song.component.ts b/src/app/widget-modules/components/add-song/add-song.component.ts index 7addb0f..51a9e1e 100644 --- a/src/app/widget-modules/components/add-song/add-song.component.ts +++ b/src/app/widget-modules/components/add-song/add-song.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectionStrategy, Component, Input} from '@angular/core'; +import {ChangeDetectionStrategy, Component, Input, inject} from '@angular/core'; import {ReactiveFormsModule, UntypedFormControl} from '@angular/forms'; import {filterSong} from '../../../services/filter.helper'; import {MatFormField, MatLabel, MatOption, MatSelect, MatSelectChange} from '@angular/material/select'; @@ -18,17 +18,15 @@ import {NgxMatSelectSearchModule} from 'ngx-mat-select-search'; imports: [MatFormField, MatSelect, MatOption, MatLabel, NgxMatSelectSearchModule, ReactiveFormsModule], }) export class AddSongComponent { + private showSongService = inject(ShowSongService); + private showService = inject(ShowService); + @Input() public songs: Song[] | null = null; @Input() public showSongs: ShowSong[] | null = null; @Input() public show: Show | null = null; @Input() public addedLive = false; public filteredSongsControl = new UntypedFormControl(); - public constructor( - private showSongService: ShowSongService, - private showService: ShowService - ) {} - public filteredSongs(): Song[] { if (!this.songs) return []; const songs = this.songs diff --git a/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts b/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts index 89836a1..df35713 100644 --- a/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts +++ b/src/app/widget-modules/components/application-frame/navigation/filter/filter.component.ts @@ -1,4 +1,4 @@ -import {Component} from '@angular/core'; +import {Component, inject} from '@angular/core'; import {ActivatedRoute, Params, Router} from '@angular/router'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; @@ -9,12 +9,13 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms'; imports: [ReactiveFormsModule, FormsModule], }) export class FilterComponent { + private router = inject(Router); + public value = ''; - public constructor( - private router: Router, - activatedRoute: ActivatedRoute - ) { + public constructor() { + const activatedRoute = inject(ActivatedRoute); + activatedRoute.queryParams.subscribe((params: Params) => { const typedParams = params as {q: string}; if (typedParams.q) this.value = typedParams.q; diff --git a/src/app/widget-modules/components/song-text/song-text.component.ts b/src/app/widget-modules/components/song-text/song-text.component.ts index 96b7b4c..6865ad0 100644 --- a/src/app/widget-modules/components/song-text/song-text.component.ts +++ b/src/app/widget-modules/components/song-text/song-text.component.ts @@ -1,4 +1,4 @@ -import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren} from '@angular/core'; +import {ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnInit, Output, QueryList, ViewChildren, inject} from '@angular/core'; import {TextRenderingService} from '../../../modules/songs/services/text-rendering.service'; import {faGripLines} from '@fortawesome/free-solid-svg-icons'; import {songSwitch} from './animation'; @@ -21,6 +21,10 @@ export type ChordMode = 'show' | 'hide' | 'onlyFirst'; imports: [MatIconButton, FaIconComponent], }) export class SongTextComponent implements OnInit { + private textRenderingService = inject(TextRenderingService); + private elRef = inject>(ElementRef); + private cRef = inject(ChangeDetectorRef); + public sections: Section[] = []; @Input() public header: string | null = null; @Input() public index = -1; @@ -35,12 +39,6 @@ export class SongTextComponent implements OnInit { private iText = ''; private iTranspose: TransposeMode | null = null; - public constructor( - private textRenderingService: TextRenderingService, - private elRef: ElementRef, - private cRef: ChangeDetectorRef - ) {} - @Input() public set chordMode(value: ChordMode) { this.iChordMode = value ?? 'hide'; diff --git a/src/app/widget-modules/directives/autofocus/autofocus.directive.ts b/src/app/widget-modules/directives/autofocus/autofocus.directive.ts index 55c9e84..1741462 100644 --- a/src/app/widget-modules/directives/autofocus/autofocus.directive.ts +++ b/src/app/widget-modules/directives/autofocus/autofocus.directive.ts @@ -1,10 +1,9 @@ -import {Directive, ElementRef, Input, OnInit} from '@angular/core'; +import {Directive, ElementRef, Input, OnInit, inject} from '@angular/core'; @Directive({selector: '[appAutofocus]'}) export class AutofocusDirective implements OnInit { private focus = true; - - public constructor(private el: ElementRef) {} + private el = inject>(ElementRef); @Input() public set autofocus(condition: boolean) { diff --git a/src/app/widget-modules/guards/auth.guard.ts b/src/app/widget-modules/guards/auth.guard.ts index f6c33c3..2ba46fc 100644 --- a/src/app/widget-modules/guards/auth.guard.ts +++ b/src/app/widget-modules/guards/auth.guard.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {CanActivate, Router, UrlTree} from '@angular/router'; import {Auth, authState} from '@angular/fire/auth'; import {Observable} from 'rxjs'; @@ -8,10 +8,8 @@ import {map, take} from 'rxjs/operators'; providedIn: 'root', }) export class AuthGuard implements CanActivate { - public constructor( - private auth: Auth, - private router: Router - ) {} + private auth = inject(Auth); + private router = inject(Router); public canActivate(): Observable { return authState(this.auth).pipe( diff --git a/src/app/widget-modules/guards/role.guard.ts b/src/app/widget-modules/guards/role.guard.ts index 811479d..9ca3bd5 100644 --- a/src/app/widget-modules/guards/role.guard.ts +++ b/src/app/widget-modules/guards/role.guard.ts @@ -1,4 +1,4 @@ -import {Injectable} from '@angular/core'; +import {Injectable, inject} from '@angular/core'; import {ActivatedRouteSnapshot, Router, UrlTree} from '@angular/router'; import {Observable} from 'rxjs'; import {UserService} from '../../services/user/user.service'; @@ -8,10 +8,8 @@ import {map} from 'rxjs/operators'; providedIn: 'root', }) export class RoleGuard { - public constructor( - private userService: UserService, - private router: Router - ) {} + private userService = inject(UserService); + private router = inject(Router); public canActivate(next: ActivatedRouteSnapshot): Observable { const requiredRoles = next.data.requiredRoles as string[]; diff --git a/src/app/widget-modules/pipes/sort-by/sort-by.pipe.ts b/src/app/widget-modules/pipes/sort-by/sort-by.pipe.ts index 74242aa..e3325d0 100644 --- a/src/app/widget-modules/pipes/sort-by/sort-by.pipe.ts +++ b/src/app/widget-modules/pipes/sort-by/sort-by.pipe.ts @@ -1,5 +1,4 @@ import {Pipe, PipeTransform} from '@angular/core'; -import {orderBy} from 'lodash'; @Pipe({ name: 'sortBy', @@ -19,7 +18,29 @@ export class SortByPipe implements PipeTransform { if (value.length <= 1) { return value; } // array with only one item - // eslint-disable-next-line @typescript-eslint/no-unsafe-call - return orderBy(value, [column], [order]); + return [...value].sort((left, right) => { + const leftValue = this.getComparableValue(left, column); + const rightValue = this.getComparableValue(right, column); + const result = leftValue < rightValue ? -1 : leftValue > rightValue ? 1 : 0; + return order === 'asc' ? result : -result; + }); + } + + private getComparableValue(item: unknown, column: string): string | number { + const value = (item as Record)[column]; + if (value instanceof Date) { + return value.getTime(); + } + + switch (typeof value) { + case 'number': + return value; + case 'string': + return value; + case 'boolean': + return value ? 1 : 0; + default: + return ''; + } } }