diff --git a/.github/workflows/frontend-dev.yml b/.github/workflows/frontend-dev.yml index 39eadac8a..c82985558 100644 --- a/.github/workflows/frontend-dev.yml +++ b/.github/workflows/frontend-dev.yml @@ -14,43 +14,46 @@ on: defaults: run: working-directory: frontend - + permissions: - pull-requests: write - checks: write + pull-requests: write + checks: write jobs: - build: + build: runs-on: ubuntu-latest steps: - name: Set up Repository uses: actions/checkout@v3 - - name : Set up Node 18.16.0 + - name: Set up Node 18.16.0 uses: actions/setup-node@v3 with: node-version: 18.16.0 - + - name: Cache node_modules id: cache uses: actions/cache@v3 with: - path: '**/node_modules' - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - + path: '**/node_modules' + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - name: npm clean install run: npm ci if: steps.cache.outputs.cache-hit != 'true' + - name: submodule update + run: git submodule update --remote --merge + - name: npm build dev run: npm run build-dev - + - name: storyBook build run: npm run build-storybook if: always() - + - name: notificate on slack if build failed uses: 8398a7/action-slack@v3 with: diff --git a/frontend/cypress/e2e/main.cy.ts b/frontend/cypress/e2e/main.cy.ts index 2888f68ed..68f1eee47 100644 --- a/frontend/cypress/e2e/main.cy.ts +++ b/frontend/cypress/e2e/main.cy.ts @@ -96,6 +96,10 @@ describe('검색창', () => { .type('아카시') .wait(500) + .get('ul > li') + .contains('아카시') + .should('be.visible') + .get('button[aria-label="이동하기"]') .click() @@ -110,6 +114,13 @@ describe('검색창', () => { cy.get('input') .type('참') .wait(500) + + .get('ul > li') + .should('be.visible') + + .get('input') + .focus() + .type('{enter}') .location('pathname') diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4f192ae67..895d281a1 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@pium/stack-component": "^1.0.0", "@tanstack/react-query": "^5.0.0-beta.20", + "firebase": "^10.4.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.1", @@ -2987,6 +2988,624 @@ "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "dev": true }, + "node_modules/@firebase/analytics": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.0.tgz", + "integrity": "sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/analytics-compat": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz", + "integrity": "sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-types": "0.8.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/analytics-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.0.tgz", + "integrity": "sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==" + }, + "node_modules/@firebase/app": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.19.tgz", + "integrity": "sha512-t/SHyZ3xWkR77ZU9VMoobDNFLdDKQ5xqoCAn4o16gTsA1C8sJ6ZOMZ02neMOPxNHuQXVE4tA8ukilnDbnK7uJA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-check": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.0.tgz", + "integrity": "sha512-dRDnhkcaC2FspMiRK/Vbp+PfsOAEP6ZElGm9iGFJ9fDqHoPs0HOPn7dwpJ51lCFi1+2/7n5pRPGhqF/F03I97g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/app-check-compat": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.7.tgz", + "integrity": "sha512-cW682AxsyP1G+Z0/P7pO/WT2CzYlNxoNe5QejVarW2o5ZxeWSSPAiVEwpEpQR/bUlUmdeWThYTMvBWaopdBsqw==", + "dependencies": { + "@firebase/app-check": "0.8.0", + "@firebase/app-check-types": "0.5.0", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" + }, + "node_modules/@firebase/app-check-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.0.tgz", + "integrity": "sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==" + }, + "node_modules/@firebase/app-compat": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.19.tgz", + "integrity": "sha512-QkJDqYqjhvs4fTMcRVXQkP9hbo5yfoJXDWkhU4VA5Vzs8Qsp76VPzYbqx5SD5OmBy+bz/Ot1UV8qySPGI4aKuw==", + "dependencies": { + "@firebase/app": "0.9.19", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/app-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" + }, + "node_modules/@firebase/auth": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.3.0.tgz", + "integrity": "sha512-vjK4CHbY9aWdiVOrKi6mpa8z6uxeaf7LB/MZTHuZOiGHMcUoTGB6TeMbRShyqk1uaMrxhhZ5Ar/dR0965E1qyA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x", + "@react-native-async-storage/async-storage": "^1.18.1" + }, + "peerDependenciesMeta": { + "@react-native-async-storage/async-storage": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-compat": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.4.6.tgz", + "integrity": "sha512-pKp1d4fSf+yoy1EBjTx9ISxlunqhW0vTICk0ByZ3e+Lp6ZIXThfUy4F1hAJlEafD/arM0oepRiAh7LXS1xn/BA==", + "dependencies": { + "@firebase/auth": "1.3.0", + "@firebase/auth-types": "0.12.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/auth-compat/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/auth-interop-types": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" + }, + "node_modules/@firebase/auth-types": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", + "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/auth/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/component": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", + "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", + "dependencies": { + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.1.tgz", + "integrity": "sha512-VAhF7gYwunW4Lw/+RQZvW8dlsf2r0YYqV9W0Gi2Mz8+0TGg1mBJWoUtsHfOr8kPJXhcLsC4eP/z3x6L/Fvjk/A==", + "dependencies": { + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-compat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.1.tgz", + "integrity": "sha512-ky82yLIboLxtAIWyW/52a6HLMVTzD2kpZlEilVDok73pNPLjkJYowj8iaIWK5nTy7+6Gxt7d00zfjL6zckGdXQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/database": "1.0.1", + "@firebase/database-types": "1.0.0", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/database-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", + "dependencies": { + "@firebase/app-types": "0.9.0", + "@firebase/util": "1.9.3" + } + }, + "node_modules/@firebase/firestore": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.2.0.tgz", + "integrity": "sha512-iKZqIdOBJpJUcwY5airLX0W04TLrQSJuActOP1HG5WoIY5oyGTQE4Ml7hl5GW7mBqFieT4ojtUuDXj6MLrn1lA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "@firebase/webchannel-wrapper": "0.10.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10.10.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/firestore-compat": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.18.tgz", + "integrity": "sha512-hkqv4mb1oScKbEtzfcK8Go8c0VpDWmbAvbD6B6XnphLqi27pkXgo9Rp+aSKlD7cBL29VMEekP5bEm9lSVfZpNw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/firestore": "4.2.0", + "@firebase/firestore-types": "3.0.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/firestore-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.0.tgz", + "integrity": "sha512-Meg4cIezHo9zLamw0ymFYBD4SMjLb+ZXIbuN7T7ddXN6MGoICmOTq3/ltdCGoDCS2u+H1XJs2u/cYp75jsX9Qw==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/firestore/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/functions": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.10.0.tgz", + "integrity": "sha512-2U+fMNxTYhtwSpkkR6WbBcuNMOVaI7MaH3cZ6UAeNfj7AgEwHwMIFLPpC13YNZhno219F0lfxzTAA0N62ndWzA==", + "dependencies": { + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/functions-compat": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.5.tgz", + "integrity": "sha512-uD4jwgwVqdWf6uc3NRKF8cSZ0JwGqSlyhPgackyUPe+GAtnERpS4+Vr66g0b3Gge0ezG4iyHo/EXW/Hjx7QhHw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/functions": "0.10.0", + "@firebase/functions-types": "0.6.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/functions-types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.0.tgz", + "integrity": "sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==" + }, + "node_modules/@firebase/functions/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/installations": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.4.tgz", + "integrity": "sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/installations-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.4.tgz", + "integrity": "sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/installations-types": "0.5.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/installations-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.0.tgz", + "integrity": "sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==", + "peerDependencies": { + "@firebase/app-types": "0.x" + } + }, + "node_modules/@firebase/installations/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + }, + "node_modules/@firebase/logger": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", + "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/messaging": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.4.tgz", + "integrity": "sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/messaging-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.4.tgz", + "integrity": "sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/messaging": "0.12.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/messaging-interop-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz", + "integrity": "sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==" + }, + "node_modules/@firebase/messaging/node_modules/idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + }, + "node_modules/@firebase/performance": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.4.tgz", + "integrity": "sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/performance-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.4.tgz", + "integrity": "sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/performance": "0.6.4", + "@firebase/performance-types": "0.2.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/performance-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.0.tgz", + "integrity": "sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==" + }, + "node_modules/@firebase/remote-config": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.4.tgz", + "integrity": "sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/remote-config-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz", + "integrity": "sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-types": "0.3.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/remote-config-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz", + "integrity": "sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==" + }, + "node_modules/@firebase/storage": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.11.2.tgz", + "integrity": "sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app": "0.x" + } + }, + "node_modules/@firebase/storage-compat": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.2.tgz", + "integrity": "sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==", + "dependencies": { + "@firebase/component": "0.6.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-types": "0.8.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "@firebase/app-compat": "0.x" + } + }, + "node_modules/@firebase/storage-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.0.tgz", + "integrity": "sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==", + "peerDependencies": { + "@firebase/app-types": "0.x", + "@firebase/util": "1.x" + } + }, + "node_modules/@firebase/storage/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@firebase/util": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", + "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@firebase/webchannel-wrapper": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.3.tgz", + "integrity": "sha512-+ZplYUN3HOpgCfgInqgdDAbkGGVzES1cs32JJpeqoh87SkRobGXElJx+1GZSaDqzFL+bYiX18qEcBK76mYs8uA==" + }, + "node_modules/@grpc/grpc-js": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.5.tgz", + "integrity": "sha512-iouYNlPxRAwZ2XboDT+OfRKHuaKHiqjB5VFYZ0NFrHkbEF+AV3muIUY9olQsp8uxU4VvRCMiRk9ftzFDGb61aw==", + "dependencies": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + }, + "engines": { + "node": "^8.13.0 || >=10.10.0" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -3605,6 +4224,60 @@ "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==", "dev": true }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "node_modules/@remix-run/router": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.1.tgz", @@ -7531,8 +8204,7 @@ "node_modules/@types/node": { "version": "20.3.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==", - "dev": true + "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" }, "node_modules/@types/node-fetch": { "version": "2.6.4", @@ -8598,7 +9270,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -9933,7 +10604,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -11527,8 +12197,7 @@ "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -12787,7 +13456,6 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, "dependencies": { "websocket-driver": ">=0.5.1" }, @@ -12961,6 +13629,39 @@ "node": ">=8" } }, + "node_modules/firebase": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.4.0.tgz", + "integrity": "sha512-3Z8WsNwA7kbcKGZ+nrTZ/ES518pk0K440ZJYD8nUNKN5hV6ll+unhUw30t1msedN6yIFjhsC/9OwT4Z0ohwO2w==", + "dependencies": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-compat": "0.2.6", + "@firebase/app": "0.9.19", + "@firebase/app-check": "0.8.0", + "@firebase/app-check-compat": "0.3.7", + "@firebase/app-compat": "0.2.19", + "@firebase/app-types": "0.9.0", + "@firebase/auth": "1.3.0", + "@firebase/auth-compat": "0.4.6", + "@firebase/database": "1.0.1", + "@firebase/database-compat": "1.0.1", + "@firebase/firestore": "4.2.0", + "@firebase/firestore-compat": "0.3.18", + "@firebase/functions": "0.10.0", + "@firebase/functions-compat": "0.3.5", + "@firebase/installations": "0.6.4", + "@firebase/installations-compat": "0.2.4", + "@firebase/messaging": "0.12.4", + "@firebase/messaging-compat": "0.2.4", + "@firebase/performance": "0.6.4", + "@firebase/performance-compat": "0.2.4", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-compat": "0.2.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-compat": "0.3.2", + "@firebase/util": "1.9.3" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -13358,7 +14059,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -14021,8 +14721,7 @@ "node_modules/http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" }, "node_modules/http-proxy": { "version": "1.18.1", @@ -14145,6 +14844,11 @@ "postcss": "^8.1.0" } }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -14559,7 +15263,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -16029,6 +16732,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -16224,6 +16932,11 @@ "node": ">=8" } }, + "node_modules/long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -18043,6 +18756,29 @@ "react-is": "^16.13.1" } }, + "node_modules/protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "hasInstallScript": true, + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -18826,7 +19562,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -18981,7 +19716,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -19747,7 +20481,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -19825,7 +20558,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -20720,8 +21452,7 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/trim-newlines": { "version": "4.1.1", @@ -21492,8 +22223,7 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { "version": "5.88.1", @@ -21918,7 +22648,6 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, "dependencies": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -21932,7 +22661,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -21941,7 +22669,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -22038,7 +22765,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -22055,7 +22781,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -22070,7 +22795,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -22081,8 +22805,7 @@ "node_modules/wrap-ansi/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrappy": { "version": "1.0.2", @@ -22136,7 +22859,6 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, "engines": { "node": ">=10" } @@ -22159,7 +22881,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -22177,7 +22898,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, "engines": { "node": ">=12" } @@ -24076,6 +24796,494 @@ "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "dev": true }, + "@firebase/analytics": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.0.tgz", + "integrity": "sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/analytics-compat": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.6.tgz", + "integrity": "sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==", + "requires": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-types": "0.8.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/analytics-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.0.tgz", + "integrity": "sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==" + }, + "@firebase/app": { + "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.9.19.tgz", + "integrity": "sha512-t/SHyZ3xWkR77ZU9VMoobDNFLdDKQ5xqoCAn4o16gTsA1C8sJ6ZOMZ02neMOPxNHuQXVE4tA8ukilnDbnK7uJA==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "idb": "7.1.1", + "tslib": "^2.1.0" + } + }, + "@firebase/app-check": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.0.tgz", + "integrity": "sha512-dRDnhkcaC2FspMiRK/Vbp+PfsOAEP6ZElGm9iGFJ9fDqHoPs0HOPn7dwpJ51lCFi1+2/7n5pRPGhqF/F03I97g==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/app-check-compat": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.7.tgz", + "integrity": "sha512-cW682AxsyP1G+Z0/P7pO/WT2CzYlNxoNe5QejVarW2o5ZxeWSSPAiVEwpEpQR/bUlUmdeWThYTMvBWaopdBsqw==", + "requires": { + "@firebase/app-check": "0.8.0", + "@firebase/app-check-types": "0.5.0", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/app-check-interop-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.0.tgz", + "integrity": "sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==" + }, + "@firebase/app-check-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.0.tgz", + "integrity": "sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==" + }, + "@firebase/app-compat": { + "version": "0.2.19", + "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.19.tgz", + "integrity": "sha512-QkJDqYqjhvs4fTMcRVXQkP9hbo5yfoJXDWkhU4VA5Vzs8Qsp76VPzYbqx5SD5OmBy+bz/Ot1UV8qySPGI4aKuw==", + "requires": { + "@firebase/app": "0.9.19", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/app-types": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.0.tgz", + "integrity": "sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==" + }, + "@firebase/auth": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.3.0.tgz", + "integrity": "sha512-vjK4CHbY9aWdiVOrKi6mpa8z6uxeaf7LB/MZTHuZOiGHMcUoTGB6TeMbRShyqk1uaMrxhhZ5Ar/dR0965E1qyA==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "@firebase/auth-compat": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.4.6.tgz", + "integrity": "sha512-pKp1d4fSf+yoy1EBjTx9ISxlunqhW0vTICk0ByZ3e+Lp6ZIXThfUy4F1hAJlEafD/arM0oepRiAh7LXS1xn/BA==", + "requires": { + "@firebase/auth": "1.3.0", + "@firebase/auth-types": "0.12.0", + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "@firebase/auth-interop-types": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.1.tgz", + "integrity": "sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==" + }, + "@firebase/auth-types": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.0.tgz", + "integrity": "sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==", + "requires": {} + }, + "@firebase/component": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.4.tgz", + "integrity": "sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==", + "requires": { + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/database": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.1.tgz", + "integrity": "sha512-VAhF7gYwunW4Lw/+RQZvW8dlsf2r0YYqV9W0Gi2Mz8+0TGg1mBJWoUtsHfOr8kPJXhcLsC4eP/z3x6L/Fvjk/A==", + "requires": { + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "faye-websocket": "0.11.4", + "tslib": "^2.1.0" + } + }, + "@firebase/database-compat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.1.tgz", + "integrity": "sha512-ky82yLIboLxtAIWyW/52a6HLMVTzD2kpZlEilVDok73pNPLjkJYowj8iaIWK5nTy7+6Gxt7d00zfjL6zckGdXQ==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/database": "1.0.1", + "@firebase/database-types": "1.0.0", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/database-types": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.0.tgz", + "integrity": "sha512-SjnXStoE0Q56HcFgNQ+9SsmJc0c8TqGARdI/T44KXy+Ets3r6x/ivhQozT66bMnCEjJRywYoxNurRTMlZF8VNg==", + "requires": { + "@firebase/app-types": "0.9.0", + "@firebase/util": "1.9.3" + } + }, + "@firebase/firestore": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.2.0.tgz", + "integrity": "sha512-iKZqIdOBJpJUcwY5airLX0W04TLrQSJuActOP1HG5WoIY5oyGTQE4Ml7hl5GW7mBqFieT4ojtUuDXj6MLrn1lA==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "@firebase/webchannel-wrapper": "0.10.3", + "@grpc/grpc-js": "~1.9.0", + "@grpc/proto-loader": "^0.7.8", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "@firebase/firestore-compat": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.18.tgz", + "integrity": "sha512-hkqv4mb1oScKbEtzfcK8Go8c0VpDWmbAvbD6B6XnphLqi27pkXgo9Rp+aSKlD7cBL29VMEekP5bEm9lSVfZpNw==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/firestore": "4.2.0", + "@firebase/firestore-types": "3.0.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/firestore-types": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.0.tgz", + "integrity": "sha512-Meg4cIezHo9zLamw0ymFYBD4SMjLb+ZXIbuN7T7ddXN6MGoICmOTq3/ltdCGoDCS2u+H1XJs2u/cYp75jsX9Qw==", + "requires": {} + }, + "@firebase/functions": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.10.0.tgz", + "integrity": "sha512-2U+fMNxTYhtwSpkkR6WbBcuNMOVaI7MaH3cZ6UAeNfj7AgEwHwMIFLPpC13YNZhno219F0lfxzTAA0N62ndWzA==", + "requires": { + "@firebase/app-check-interop-types": "0.3.0", + "@firebase/auth-interop-types": "0.2.1", + "@firebase/component": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "@firebase/functions-compat": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.5.tgz", + "integrity": "sha512-uD4jwgwVqdWf6uc3NRKF8cSZ0JwGqSlyhPgackyUPe+GAtnERpS4+Vr66g0b3Gge0ezG4iyHo/EXW/Hjx7QhHw==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/functions": "0.10.0", + "@firebase/functions-types": "0.6.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/functions-types": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.0.tgz", + "integrity": "sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==" + }, + "@firebase/installations": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.4.tgz", + "integrity": "sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "dependencies": { + "idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + } + } + }, + "@firebase/installations-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.4.tgz", + "integrity": "sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/installations-types": "0.5.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/installations-types": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.0.tgz", + "integrity": "sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==", + "requires": {} + }, + "@firebase/logger": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.0.tgz", + "integrity": "sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@firebase/messaging": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.4.tgz", + "integrity": "sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/messaging-interop-types": "0.2.0", + "@firebase/util": "1.9.3", + "idb": "7.0.1", + "tslib": "^2.1.0" + }, + "dependencies": { + "idb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.0.1.tgz", + "integrity": "sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==" + } + } + }, + "@firebase/messaging-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.4.tgz", + "integrity": "sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/messaging": "0.12.4", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/messaging-interop-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.0.tgz", + "integrity": "sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==" + }, + "@firebase/performance": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.4.tgz", + "integrity": "sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/performance-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.4.tgz", + "integrity": "sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/performance": "0.6.4", + "@firebase/performance-types": "0.2.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/performance-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.0.tgz", + "integrity": "sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==" + }, + "@firebase/remote-config": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.4.tgz", + "integrity": "sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/installations": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/remote-config-compat": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.4.tgz", + "integrity": "sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/logger": "0.4.0", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-types": "0.3.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/remote-config-types": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.0.tgz", + "integrity": "sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==" + }, + "@firebase/storage": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.11.2.tgz", + "integrity": "sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/util": "1.9.3", + "node-fetch": "2.6.7", + "tslib": "^2.1.0" + }, + "dependencies": { + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + } + } + }, + "@firebase/storage-compat": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.2.tgz", + "integrity": "sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==", + "requires": { + "@firebase/component": "0.6.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-types": "0.8.0", + "@firebase/util": "1.9.3", + "tslib": "^2.1.0" + } + }, + "@firebase/storage-types": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.0.tgz", + "integrity": "sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==", + "requires": {} + }, + "@firebase/util": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.3.tgz", + "integrity": "sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@firebase/webchannel-wrapper": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.3.tgz", + "integrity": "sha512-+ZplYUN3HOpgCfgInqgdDAbkGGVzES1cs32JJpeqoh87SkRobGXElJx+1GZSaDqzFL+bYiX18qEcBK76mYs8uA==" + }, + "@grpc/grpc-js": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.5.tgz", + "integrity": "sha512-iouYNlPxRAwZ2XboDT+OfRKHuaKHiqjB5VFYZ0NFrHkbEF+AV3muIUY9olQsp8uxU4VvRCMiRk9ftzFDGb61aw==", + "requires": { + "@grpc/proto-loader": "^0.7.8", + "@types/node": ">=12.12.47" + } + }, + "@grpc/proto-loader": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.10.tgz", + "integrity": "sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==", + "requires": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.4", + "yargs": "^17.7.2" + } + }, "@humanwhocodes/config-array": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", @@ -24535,6 +25743,60 @@ "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==", "dev": true }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + }, "@remix-run/router": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.7.1.tgz", @@ -27375,8 +28637,7 @@ "@types/node": { "version": "20.3.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==", - "dev": true + "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" }, "@types/node-fetch": { "version": "2.6.4", @@ -28205,8 +29466,7 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", @@ -29169,7 +30429,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -30397,8 +31656,7 @@ "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "emojis-list": { "version": "3.0.0", @@ -31386,7 +32644,6 @@ "version": "0.11.4", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, "requires": { "websocket-driver": ">=0.5.1" } @@ -31528,6 +32785,39 @@ "path-exists": "^4.0.0" } }, + "firebase": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.4.0.tgz", + "integrity": "sha512-3Z8WsNwA7kbcKGZ+nrTZ/ES518pk0K440ZJYD8nUNKN5hV6ll+unhUw30t1msedN6yIFjhsC/9OwT4Z0ohwO2w==", + "requires": { + "@firebase/analytics": "0.10.0", + "@firebase/analytics-compat": "0.2.6", + "@firebase/app": "0.9.19", + "@firebase/app-check": "0.8.0", + "@firebase/app-check-compat": "0.3.7", + "@firebase/app-compat": "0.2.19", + "@firebase/app-types": "0.9.0", + "@firebase/auth": "1.3.0", + "@firebase/auth-compat": "0.4.6", + "@firebase/database": "1.0.1", + "@firebase/database-compat": "1.0.1", + "@firebase/firestore": "4.2.0", + "@firebase/firestore-compat": "0.3.18", + "@firebase/functions": "0.10.0", + "@firebase/functions-compat": "0.3.5", + "@firebase/installations": "0.6.4", + "@firebase/installations-compat": "0.2.4", + "@firebase/messaging": "0.12.4", + "@firebase/messaging-compat": "0.2.4", + "@firebase/performance": "0.6.4", + "@firebase/performance-compat": "0.2.4", + "@firebase/remote-config": "0.4.4", + "@firebase/remote-config-compat": "0.2.4", + "@firebase/storage": "0.11.2", + "@firebase/storage-compat": "0.3.2", + "@firebase/util": "1.9.3" + } + }, "flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -31818,8 +33108,7 @@ "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { "version": "1.2.1", @@ -32311,8 +33600,7 @@ "http-parser-js": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" }, "http-proxy": { "version": "1.18.1", @@ -32398,6 +33686,11 @@ "dev": true, "requires": {} }, + "idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==" + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -32687,8 +33980,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-generator-function": { "version": "1.0.10", @@ -33761,6 +35053,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -33918,6 +35215,11 @@ } } }, + "long": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", + "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -35244,6 +36546,25 @@ "react-is": "^16.13.1" } }, + "protobufjs": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.5.tgz", + "integrity": "sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -35837,8 +37158,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "require-from-string": { "version": "2.0.2", @@ -35941,8 +37261,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safe-regex-test": { "version": "1.0.0", @@ -36544,7 +37863,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -36604,7 +37922,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -37286,8 +38603,7 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "trim-newlines": { "version": "4.1.1", @@ -37861,8 +39177,7 @@ "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { "version": "5.88.1", @@ -38155,7 +39470,6 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, "requires": { "http-parser-js": ">=0.5.1", "safe-buffer": ">=5.1.0", @@ -38165,14 +39479,12 @@ "websocket-extensions": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -38251,7 +39563,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -38262,7 +39573,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -38271,7 +39581,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -38279,8 +39588,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, @@ -38315,8 +39623,7 @@ "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" }, "yallist": { "version": "3.1.1", @@ -38333,7 +39640,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, "requires": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -38347,8 +39653,7 @@ "yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" }, "yauzl": { "version": "2.10.0", diff --git a/frontend/package.json b/frontend/package.json index 7f335a959..46181db11 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -19,6 +19,7 @@ "dependencies": { "@pium/stack-component": "^1.0.0", "@tanstack/react-query": "^5.0.0-beta.20", + "firebase": "^10.4.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.14.1", diff --git a/frontend/public/assets/android-icon-144x144.png b/frontend/public/assets/android-icon-144x144.png index 349129863..183d8b64e 100644 Binary files a/frontend/public/assets/android-icon-144x144.png and b/frontend/public/assets/android-icon-144x144.png differ diff --git a/frontend/public/assets/android-icon-192x192.png b/frontend/public/assets/android-icon-192x192.png index 0b6243288..b6b320770 100644 Binary files a/frontend/public/assets/android-icon-192x192.png and b/frontend/public/assets/android-icon-192x192.png differ diff --git a/frontend/public/assets/android-icon-48x48.png b/frontend/public/assets/android-icon-48x48.png index bac6abfbe..20af524da 100644 Binary files a/frontend/public/assets/android-icon-48x48.png and b/frontend/public/assets/android-icon-48x48.png differ diff --git a/frontend/public/assets/android-icon-512x512.png b/frontend/public/assets/android-icon-512x512.png index a68af4884..41d702e48 100644 Binary files a/frontend/public/assets/android-icon-512x512.png and b/frontend/public/assets/android-icon-512x512.png differ diff --git a/frontend/public/assets/android-icon-72x72.png b/frontend/public/assets/android-icon-72x72.png index d7f4cef5f..00dd12c90 100644 Binary files a/frontend/public/assets/android-icon-72x72.png and b/frontend/public/assets/android-icon-72x72.png differ diff --git a/frontend/public/assets/android-icon-96x96.png b/frontend/public/assets/android-icon-96x96.png index 1de52cea2..ad68d9707 100644 Binary files a/frontend/public/assets/android-icon-96x96.png and b/frontend/public/assets/android-icon-96x96.png differ diff --git a/frontend/public/assets/apple-icon-114x114.png b/frontend/public/assets/apple-icon-114x114.png index e796761dc..5bcd7a74f 100644 Binary files a/frontend/public/assets/apple-icon-114x114.png and b/frontend/public/assets/apple-icon-114x114.png differ diff --git a/frontend/public/assets/apple-icon-120x120.png b/frontend/public/assets/apple-icon-120x120.png index ab14547eb..340055e5e 100644 Binary files a/frontend/public/assets/apple-icon-120x120.png and b/frontend/public/assets/apple-icon-120x120.png differ diff --git a/frontend/public/assets/apple-icon-144x144.png b/frontend/public/assets/apple-icon-144x144.png index 349129863..cc59fdb26 100644 Binary files a/frontend/public/assets/apple-icon-144x144.png and b/frontend/public/assets/apple-icon-144x144.png differ diff --git a/frontend/public/assets/apple-icon-152x152.png b/frontend/public/assets/apple-icon-152x152.png index 7e3b1bc19..c68c36b30 100644 Binary files a/frontend/public/assets/apple-icon-152x152.png and b/frontend/public/assets/apple-icon-152x152.png differ diff --git a/frontend/public/assets/apple-icon-180x180.png b/frontend/public/assets/apple-icon-180x180.png index 5461e7aa3..414f59bca 100644 Binary files a/frontend/public/assets/apple-icon-180x180.png and b/frontend/public/assets/apple-icon-180x180.png differ diff --git a/frontend/public/assets/apple-icon-192x192.png b/frontend/public/assets/apple-icon-192x192.png deleted file mode 100644 index 0b6243288..000000000 Binary files a/frontend/public/assets/apple-icon-192x192.png and /dev/null differ diff --git a/frontend/public/assets/apple-icon-512x512.png b/frontend/public/assets/apple-icon-512x512.png deleted file mode 100644 index a68af4884..000000000 Binary files a/frontend/public/assets/apple-icon-512x512.png and /dev/null differ diff --git a/frontend/public/assets/apple-icon-57x57.png b/frontend/public/assets/apple-icon-57x57.png index 19625fee2..88bbf3f0f 100644 Binary files a/frontend/public/assets/apple-icon-57x57.png and b/frontend/public/assets/apple-icon-57x57.png differ diff --git a/frontend/public/assets/apple-icon-60x60.png b/frontend/public/assets/apple-icon-60x60.png index 56a26f6b9..14944e51a 100644 Binary files a/frontend/public/assets/apple-icon-60x60.png and b/frontend/public/assets/apple-icon-60x60.png differ diff --git a/frontend/public/assets/apple-icon-72x72.png b/frontend/public/assets/apple-icon-72x72.png index d7f4cef5f..9e1c0a71b 100644 Binary files a/frontend/public/assets/apple-icon-72x72.png and b/frontend/public/assets/apple-icon-72x72.png differ diff --git a/frontend/public/assets/apple-icon-76x76.png b/frontend/public/assets/apple-icon-76x76.png index 8b450e4da..bb613fb19 100644 Binary files a/frontend/public/assets/apple-icon-76x76.png and b/frontend/public/assets/apple-icon-76x76.png differ diff --git a/frontend/public/assets/apple-icon-precomposed.png b/frontend/public/assets/apple-icon-precomposed.png index acd529094..6a36dd0ff 100644 Binary files a/frontend/public/assets/apple-icon-precomposed.png and b/frontend/public/assets/apple-icon-precomposed.png differ diff --git a/frontend/public/assets/apple-icon.png b/frontend/public/assets/apple-icon.png index 0b6243288..6a36dd0ff 100644 Binary files a/frontend/public/assets/apple-icon.png and b/frontend/public/assets/apple-icon.png differ diff --git a/frontend/public/assets/favicon-16x16.png b/frontend/public/assets/favicon-16x16.png index 960c91a62..4b31ce258 100644 Binary files a/frontend/public/assets/favicon-16x16.png and b/frontend/public/assets/favicon-16x16.png differ diff --git a/frontend/public/assets/favicon-32x32.png b/frontend/public/assets/favicon-32x32.png index d293e10fc..e839645ea 100644 Binary files a/frontend/public/assets/favicon-32x32.png and b/frontend/public/assets/favicon-32x32.png differ diff --git a/frontend/public/assets/favicon-96x96.png b/frontend/public/assets/favicon-96x96.png index a897a1650..2eec46cc2 100644 Binary files a/frontend/public/assets/favicon-96x96.png and b/frontend/public/assets/favicon-96x96.png differ diff --git a/frontend/public/assets/favicon.ico b/frontend/public/assets/favicon.ico index db4f185a0..bf5a2e213 100644 Binary files a/frontend/public/assets/favicon.ico and b/frontend/public/assets/favicon.ico differ diff --git a/frontend/public/assets/manifest.json b/frontend/public/assets/manifest.json index b3659bb12..45ca8b0cb 100644 --- a/frontend/public/assets/manifest.json +++ b/frontend/public/assets/manifest.json @@ -35,7 +35,7 @@ "sizes": "192x192", "type": "image/png", "density": "4.0", - "purpose": "any" + "purpose": "maskable" }, { "src": "/assets/android-icon-512x512.png", @@ -56,5 +56,5 @@ "orientation": "portrait", "scope": "/", "categories": ["lifestyle", "plant", "indoor-garden"], - "display_override": ["window-controls-overlay", "standalone", "browser"] + "display_override": ["window-controls-overlay"] } diff --git a/frontend/public/assets/ms-icon-144x144.png b/frontend/public/assets/ms-icon-144x144.png index f668cbc0b..cc59fdb26 100644 Binary files a/frontend/public/assets/ms-icon-144x144.png and b/frontend/public/assets/ms-icon-144x144.png differ diff --git a/frontend/public/assets/ms-icon-150x150.png b/frontend/public/assets/ms-icon-150x150.png index 702c92369..a0c9c0b3c 100644 Binary files a/frontend/public/assets/ms-icon-150x150.png and b/frontend/public/assets/ms-icon-150x150.png differ diff --git a/frontend/public/assets/ms-icon-310x310.png b/frontend/public/assets/ms-icon-310x310.png index a4dd775f6..6a07a1228 100644 Binary files a/frontend/public/assets/ms-icon-310x310.png and b/frontend/public/assets/ms-icon-310x310.png differ diff --git a/frontend/public/assets/ms-icon-70x70.png b/frontend/public/assets/ms-icon-70x70.png index fc6af9fe4..1fe2f89c3 100644 Binary files a/frontend/public/assets/ms-icon-70x70.png and b/frontend/public/assets/ms-icon-70x70.png differ diff --git a/frontend/public/devLocalServiceWorker.js b/frontend/public/devLocalServiceWorker.js new file mode 100644 index 000000000..5e4980e7d --- /dev/null +++ b/frontend/public/devLocalServiceWorker.js @@ -0,0 +1,3 @@ +/* eslint-disable no-undef */ +importScripts('/firebase-messaging-sw.js'); +importScripts('/mockServiceWorker.js'); diff --git a/frontend/public/firebase-messaging-sw.js b/frontend/public/firebase-messaging-sw.js new file mode 100644 index 000000000..b876acfb0 --- /dev/null +++ b/frontend/public/firebase-messaging-sw.js @@ -0,0 +1,117 @@ +/* eslint-disable no-undef */ +// Give the service worker access to Firebase Messaging. +// Note that you can only use Firebase Messaging here. Other Firebase libraries +// are not available in the service worker. +importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js'); +importScripts('https://www.gstatic.com/firebasejs/8.10.1/firebase-messaging.js'); + +// Initialize the Firebase app in the service worker by passing in +// your app's Firebase config object. +// https://firebase.google.com/docs/web/setup#config-object +firebase.initializeApp({ + apiKey: 'AIzaSyCNVyJ1qLPvMiuZDK97O-rmB3mz48UqC1g', + authDomain: 'pium-7445f.firebaseapp.com', + databaseURL: 'https://pium-7445f.firebaseio.com', + projectId: 'pium-7445f', + storageBucket: 'pium-7445f.appspot.com', + messagingSenderId: '14284052337', + appId: '1:14284052337:web:4ccb34224d907e73fa48d0', + measurementId: 'G-Z5F62MDJ8N', +}); + +// Retrieve an instance of Firebase Messaging so that it can handle background +// messages. +const messaging = firebase.messaging(); + +const version = 1; +const assetCacheName = `assets-${version}`; + +/** + * cache 설정 + * TODO: 처음 파일들을 캐싱하는 것인데 파일 명이 달라져서 매번 갈아끼워 줘야 하는 문제가 있는것 같습니다. + * 근데, 버저닝 생각하면 괜찮을지도? + * + * 파일명이 firebase-messaging-sw인 이유는, 해당 파일명으로 serviceWorker를 하지 않으면 firebase에서 인식이 되지 않더라구여;; + * + * https://web.dev/service-worker-mindset/ 참조 + * https://web.dev/learn/pwa/caching/ + */ + +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(assetCacheName).then((cache) => { + // 캐시로 저장할 파일을 지정. 어떤 파일을 넣을지는 차후 얘기해 보며 좋을 듯. + cache.addAll([ + '/main.43e3611a6c9133f38206.bundle.js', + '/vendors-node_modules_react-dom_client_js-node_modules_react-router-dom_dist_index_js-node_mod-cccd04.261f3e9fe64cb376e09d.bundle.js', + '/assets/logo.webp', + '/index.html', + '/', + ]); + }) + ); + + // 제어중인 서비스 워커가 존재해도 대기 상태 건너 뜀 + self.skipWaiting(); +}); + +// 기존에 있던 cache 삭제 +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => { + if (cacheName !== assetCacheName) { + return caches.delete(cacheName); + } + }) + ); + }) + ); + + // 활성화 즉시 클라이언트를 제어한다.(새로고침 불필요) + self.clients.claim(); +}); + +// 오프라인 상태라면, 캐시에 저장되어 있는 파일들 fetch로 받아옴 +self.addEventListener('fetch', (event) => { + if (!navigator.onLine) { + event.respondWith(caches.match(event.request).then((result) => result && result)); + } +}); + +/** + * self.addEventListener('push'.)를 하지 않아도 되는 이유는 messaging이라는 메서드 안에 이미 선언이 되어 있기 때문 + * https://github.dev/firebase/firebase-js-sdk + * + * + */ + +// TODO: 사용자가 forward ground인 경우에 알림을 받을 수 있도록 설정하기. + +messaging.onBackgroundMessage((payload) => { + const { + notification: { title, body }, + } = payload; + + // Customize notification here + const notificationTitle = title; + + const notificationOptions = { + body: body, + icon: './assets/favicon-32x32.png', + badge: './assets/favicon-16x16.png', + data: '/reminder', + tag: 'reminder-alert', + vibrate: [200], // 짝수 인덱스는 진동 시간, 홀수 인덱스는 휴식 시간 + }; + + self.registration.showNotification(notificationTitle, notificationOptions); +}); + +self.addEventListener('notificationclick', (event) => { + event.notification.close(); + const movePath = self.location.origin + event.notification.data; // 알림 클릭시에 리마인더로 이동 가능 + + event.waitUntil(self.clients.openWindow(movePath)); +}); diff --git a/frontend/public/seo/sitemap.xml b/frontend/public/seo/sitemap.xml index 1855bea6c..14b75d8ab 100644 --- a/frontend/public/seo/sitemap.xml +++ b/frontend/public/seo/sitemap.xml @@ -6,31 +6,31 @@ > https://pium.life/ - 2023-10-01T11:26:30.665Z + 2023-10-12T00:52:27.934Z https://pium.life/login - 2023-10-01T11:26:30.665Z + 2023-10-12T00:52:27.934Z https://pium.life/garden - 2023-10-01T11:26:30.665Z + 2023-10-12T00:52:27.934Z https://pium.life/dict - 2023-10-01T11:26:30.665Z + 2023-10-12T00:52:27.934Z https://pium.life/dict/:id - 2023-10-01T11:26:30.665Z + 2023-10-12T00:52:27.934Z https://pium.life/dict/new-plant-request - 2023-10-01T11:26:30.665Z + 2023-10-12T00:52:27.934Z diff --git a/frontend/public/serviceWorker.js b/frontend/public/serviceWorker.js deleted file mode 100644 index 84a866f60..000000000 --- a/frontend/public/serviceWorker.js +++ /dev/null @@ -1,53 +0,0 @@ -const version = 1; -const assetCacheName = `assets-${version}`; - -/** - * cache 설정 - * TODO: 처음 파일들을 캐싱하는 것인데 파일 명이 달라져서 매번 갈아끼워 줘야 하는 문제가 있는것 같습니다. - * 근데, 버저닝 생각하면 괜찮을지도? - * https://web.dev/service-worker-mindset/ 참조 - * https://web.dev/learn/pwa/caching/ - */ - -self.addEventListener('install', (event) => { - event.waitUntil( - caches.open(assetCacheName).then((cache) => { - // 캐시로 저장할 파일을 지정. 어떤 파일을 넣을지는 차후 얘기해 보며 좋을 듯. - cache.addAll([ - '/main.43e3611a6c9133f38206.bundle.js', - '/vendors-node_modules_react-dom_client_js-node_modules_react-router-dom_dist_index_js-node_mod-cccd04.261f3e9fe64cb376e09d.bundle.js', - '/assets/logo.webp', - '/index.html', - '/', - ]); - }) - ); - - // 제어중인 서비스 워커가 존재해도 대기 상태 건너 뜀 - self.skipWaiting(); -}); - -// 기존에 있던 cache 삭제 -self.addEventListener('activate', (event) => { - event.waitUntil( - caches.keys().then((cacheNames) => { - return Promise.all( - cacheNames.map((cacheName) => { - if (cacheName !== assetCacheName) { - return caches.delete(cacheName); - } - }) - ); - }) - ); - - // 활성화 즉시 클라이언트를 제어한다.(새로고침 불필요) - self.clients.claim(); -}); - -// 오프라인 상태라면, 캐시에 저장되어 있는 파일들 fetch로 받아옴 -self.addEventListener('fetch', (event) => { - if (!navigator.onLine) { - event.respondWith(caches.match(event.request).then((result) => result && result)); - } -}); diff --git a/frontend/src/apis/webPush.ts b/frontend/src/apis/webPush.ts new file mode 100644 index 000000000..dea4f487a --- /dev/null +++ b/frontend/src/apis/webPush.ts @@ -0,0 +1,40 @@ +import { BASE_URL } from 'constants/index'; + +export const SUBSCRIBE_URL = `${BASE_URL}/members/notification`; + +const headers = { + 'Content-Type': 'application/json', +}; + +const subscribe = (token: string) => { + return fetch(SUBSCRIBE_URL, { + method: 'POST', + headers, + credentials: 'include', + body: JSON.stringify({ token }), + }); +}; + +const unSubscribe = () => { + return fetch(SUBSCRIBE_URL, { + method: 'DELETE', + credentials: 'include', + headers, + }); +}; + +const currentSubscribe = () => { + return fetch(SUBSCRIBE_URL, { + method: 'GET', + credentials: 'include', + headers, + }); +}; + +const WebPushAPI = { + subscribe, + unSubscribe, + currentSubscribe, +}; + +export default WebPushAPI; diff --git a/frontend/src/components/@common/InstallPrompt/InstallPrompt.style.ts b/frontend/src/components/@common/InstallPrompt/InstallPrompt.style.ts index 56b76dae6..323a07b85 100644 --- a/frontend/src/components/@common/InstallPrompt/InstallPrompt.style.ts +++ b/frontend/src/components/@common/InstallPrompt/InstallPrompt.style.ts @@ -13,9 +13,10 @@ const slideDown = keyframes` export const Wrapper = styled.div` position: absolute; + z-index: ${({ theme }) => theme.zIndex.popover}; top: 0; - display: none; + display: flex; flex-direction: column; justify-content: space-evenly; diff --git a/frontend/src/components/@common/InstallPrompt/index.tsx b/frontend/src/components/@common/InstallPrompt/index.tsx index 93aecc8d9..9ba93885b 100644 --- a/frontend/src/components/@common/InstallPrompt/index.tsx +++ b/frontend/src/components/@common/InstallPrompt/index.tsx @@ -11,40 +11,44 @@ import theme from 'style/theme.style'; import SvgFill from '../SvgIcons/SvgFill'; const InstallPrompt = () => { - const { installApp, ignoreInstallApp, installAppRef } = useInstallApp(); + const { installApp, ignoreInstallApp, showPrompt, closePrompt } = useInstallApp(); const isIos = /iPhone|iPod|iPad/i.test(navigator.userAgent); return ( - - - - - 바로가기를 추가하시면 앱처럼 사용이 가능합니다. 추가하시겠습니까? - - - {isIos ? ( - - 하단 탭에 있는 아이콘에서 - "홈 화면에 추가"를 통해 바로가기를 추가 할 수 있습니다. - - ) : ( - - - - + <> + {showPrompt && ( + + + + + 바로가기를 추가하시면 앱처럼 사용이 가능합니다. 추가하시겠습니까? + + + {isIos ? ( + + 하단 탭에 있는 {' '} + 아이콘에서 "홈 화면에 추가"를 통해 바로가기를 추가 할 수 있습니다. + + ) : ( + + + + + )} + )} - + ); }; diff --git a/frontend/src/components/@common/Toggle/Toggle.stories.tsx b/frontend/src/components/@common/Toggle/Toggle.stories.tsx new file mode 100644 index 000000000..a18e3938e --- /dev/null +++ b/frontend/src/components/@common/Toggle/Toggle.stories.tsx @@ -0,0 +1,29 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { number } from 'prop-types'; +import Toggle from '.'; + +const meta: Meta = { + component: Toggle, + argTypes: { + width: { + control: { type: number }, + }, + height: { + control: { type: number }, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + width: 130, + height: 50, + }, + render: (args) => { + return ; + }, +}; diff --git a/frontend/src/components/@common/Toggle/Toggle.style.ts b/frontend/src/components/@common/Toggle/Toggle.style.ts new file mode 100644 index 000000000..fb983b809 --- /dev/null +++ b/frontend/src/components/@common/Toggle/Toggle.style.ts @@ -0,0 +1,54 @@ +import { HTMLProps } from 'react'; +import styled, { css } from 'styled-components'; + +interface ToggleParams extends HTMLProps { + $on: boolean; + width: number; + height: number; +} + +export const Wrapper = styled.div``; + +export const ToggleBtn = styled.button` + cursor: pointer; + + position: relative; + + display: flex; + align-items: center; + justify-content: center; + + width: ${({ width }) => width}px; + height: ${({ height }) => height}px; + + opacity: ${({ disabled }) => (disabled ? 0.3 : 1)}; + background-color: ${({ $on, theme }) => (!$on ? theme.color.gray : theme.color.primary)}; + border: none; + border-radius: 30px; + + transition: all 0.5s ease-in-out; + + :disabled { + cursor: not-allowed; + } +`; +export const Circle = styled.div` + position: absolute; + left: 5%; + + width: ${({ height }) => height * 0.8}px; + height: ${({ height }) => height * 0.8}px; + + ${({ $on, width, height }) => + $on && + css` + transform: translate(${width - height}px, 0); + transition: all 0.5s ease-in-out; + `} + + background-color: white; + border-radius: ${({ height }) => height}px; + box-shadow: 0 2px 2px rgba(0, 0, 0, 0.3); /* 그림자 효과 추가 */ + + transition: all 0.5s ease-in-out; +`; diff --git a/frontend/src/components/@common/Toggle/index.tsx b/frontend/src/components/@common/Toggle/index.tsx new file mode 100644 index 000000000..a9618d52e --- /dev/null +++ b/frontend/src/components/@common/Toggle/index.tsx @@ -0,0 +1,44 @@ +import { useCallback } from 'react'; +import { Circle, ToggleBtn, Wrapper } from './Toggle.style'; + +interface ToggleProps { + width?: number; + height?: number; + state: boolean; + toggleOnCallback?: () => void; + toggleOffCallback?: () => void; + disabled?: boolean; +} + +const Toggle = ({ + width = 130, + height = 50, + state, + toggleOnCallback, + toggleOffCallback, + disabled = false, +}: ToggleProps) => { + const toggleHandler = useCallback(() => { + if (state) { + toggleOffCallback && toggleOffCallback(); + } else { + toggleOnCallback && toggleOnCallback(); + } + }, [state, toggleOffCallback, toggleOnCallback]); + + return ( + + + + + + ); +}; + +export default Toggle; diff --git a/frontend/src/hooks/@common/useInstallApp.ts b/frontend/src/hooks/@common/useInstallApp.ts index d45c7999c..c36bd28e2 100644 --- a/frontend/src/hooks/@common/useInstallApp.ts +++ b/frontend/src/hooks/@common/useInstallApp.ts @@ -1,4 +1,5 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useState } from 'react'; +import useCheckSessionId from 'hooks/queries/auth/useCheckSessionId'; import { getCookie, setCookie } from 'utils/cookie'; declare global { @@ -16,45 +17,39 @@ interface BeforeInstallPromptEvent extends Event { prompt(): Promise; } -let deferredPrompt: BeforeInstallPromptEvent | null = null; - const useInstallApp = () => { - const installAppRef = useRef(null); + const { isSuccess: isLoggedIn } = useCheckSessionId(false); + + const [deferredPrompt, setDeferredPrompt] = useState(null); - const installApp = () => { + const installApp = async () => { if (deferredPrompt) { - deferredPrompt.prompt(); + await deferredPrompt.prompt(); deferredPrompt.userChoice.then(() => { - deferredPrompt = null; - if (installAppRef.current) { - installAppRef.current.style.display = 'none'; - } + setDeferredPrompt(null); }); } }; const ignoreInstallApp = () => { setCookie({ key: 'PromptVisible', value: 'false' }); + setDeferredPrompt(null); + }; - deferredPrompt = null; - if (installAppRef.current) { - installAppRef.current.style.display = 'none'; - } + const closePrompt = () => { + setDeferredPrompt(null); }; const beforeInstallPromptHandler = (event: BeforeInstallPromptEvent) => { event.preventDefault(); - const showPrompt = JSON.parse(getCookie('PromptVisible') || 'true'); - + const showPrompt = JSON.parse(getCookie('PromptVisible') ?? 'true'); if (!showPrompt) return; - deferredPrompt = event; - if (installAppRef.current) { - installAppRef.current.style.display = 'flex'; - } + setDeferredPrompt(event); }; + // TODO: 왜 '/'에서만 beforeinstallprompt가 이벤트 추가가 되나? prompt가 나오고 나머지는 나오지 않는가? useEffect(() => { window.addEventListener('beforeinstallprompt', beforeInstallPromptHandler); @@ -63,7 +58,7 @@ const useInstallApp = () => { }; }, []); - return { installApp, ignoreInstallApp, installAppRef }; + return { showPrompt: deferredPrompt && isLoggedIn, installApp, ignoreInstallApp, closePrompt }; }; export default useInstallApp; diff --git a/frontend/src/hooks/@common/usePushAlert.ts b/frontend/src/hooks/@common/usePushAlert.ts new file mode 100644 index 000000000..1e50ef814 --- /dev/null +++ b/frontend/src/hooks/@common/usePushAlert.ts @@ -0,0 +1,42 @@ +import useWebPush from 'hooks/queries/auth/useWebPush'; +import { getCurrentToken } from 'utils/firebase'; +import { pushStatus } from 'utils/pushStatus'; +import useAddToast from './useAddToast'; + +const usePushAlert = () => { + const addToast = useAddToast(); + const { subscribe, unSubscribe, currentSubscribe } = useWebPush(); + + const subscribeAlert = async () => { + if (!pushStatus.pushSupport) { + addToast({ type: 'warning', message: '지원하지 않는 브라우저입니다', time: 3000 }); + return; + } + + // subscribe를 하지 않으려면 해당 토큰을 제거해야 한다. + const permission = await Notification.requestPermission(); + pushStatus.notificationPermission = permission; + + if (permission !== 'granted') { + addToast({ type: 'info', message: '알림을 거부했습니다', time: 3000 }); + return; + } + + const currentToken = await getCurrentToken(); // 여기서 새로운 토큰을 전달하면 됨. + subscribe(currentToken); + }; + + const unSubscribeAlert = () => { + unSubscribe(); + }; + + return { + currentSubscribe, // 현재 FCM을 구독하고 있는지 아닌지 + pushSupport: pushStatus.pushSupport, // 푸시 서비스를 지원하는지 여부 + notificationDenied: pushStatus.notificationPermission, // 알림 구독 여부 + subscribeAlert, + unSubscribeAlert, + }; +}; + +export default usePushAlert; diff --git a/frontend/src/hooks/queries/auth/useLogin.ts b/frontend/src/hooks/queries/auth/useLogin.ts index 69e2c0575..9272e3f7c 100644 --- a/frontend/src/hooks/queries/auth/useLogin.ts +++ b/frontend/src/hooks/queries/auth/useLogin.ts @@ -1,12 +1,12 @@ -import { useSuspenseQuery } from '@tanstack/react-query'; +import { useMutation } from '@tanstack/react-query'; import AuthAPI from 'apis/auth'; import throwOnInvalidStatus from 'utils/throwOnInvalidStatus'; -const useLogin = (code: string) => - useSuspenseQuery({ - queryKey: ['getSession', code], - queryFn: async () => { +const useLogin = () => + useMutation({ + mutationFn: async (code: string) => { const response = await AuthAPI.getSessionId(code); + throwOnInvalidStatus(response); return null; }, diff --git a/frontend/src/hooks/queries/auth/useWebPush.ts b/frontend/src/hooks/queries/auth/useWebPush.ts new file mode 100644 index 000000000..03794e39b --- /dev/null +++ b/frontend/src/hooks/queries/auth/useWebPush.ts @@ -0,0 +1,91 @@ +import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'; +import useAddToast from 'hooks/@common/useAddToast'; +import WebPushSubscribeAPI, { SUBSCRIBE_URL } from 'apis/webPush'; +import noRetryIfUnauthorized from 'utils/noRetryIfUnauthorized'; +import throwOnInvalidStatus from 'utils/throwOnInvalidStatus'; + +interface CurrentSubscribe { + isSubscribe: boolean; +} + +const useWebPush = () => { + const addToast = useAddToast(); + const queryClient = useQueryClient(); + + const subscribe = useMutation({ + mutationFn: async (token: string) => { + const response = await WebPushSubscribeAPI.subscribe(token); + throwOnInvalidStatus(response); + + return null; + }, + onMutate: async () => { + await queryClient.cancelQueries({ queryKey: [SUBSCRIBE_URL] }); + + const prevData = queryClient.getQueryState([SUBSCRIBE_URL]); + + return { prevData }; + }, + onSuccess: () => { + addToast({ type: 'success', message: '알림을 등록했습니다', time: 3000 }); + }, + onError: (_, __, context) => { + queryClient.setQueryData([SUBSCRIBE_URL], context?.prevData); + addToast({ type: 'error', message: '알림 등록에 실패했습니다', time: 3000 }); + }, + onSettled: () => { + queryClient.invalidateQueries({ queryKey: [SUBSCRIBE_URL] }); + }, + }); + + const unSubscribe = useMutation({ + mutationFn: async () => { + const response = await WebPushSubscribeAPI.unSubscribe(); + throwOnInvalidStatus(response); + if (response.status === 407) { + throw new Error('랜덤 실패'); + } + + return null; + }, + onMutate: async () => { + await queryClient.cancelQueries({ queryKey: [SUBSCRIBE_URL] }); + + const prevData = queryClient.getQueryState([SUBSCRIBE_URL]); + + return { prevData }; + }, + onSuccess: () => { + addToast({ type: 'success', message: '알림을 해제했습니다', time: 3000 }); + }, + onError: (_, __, context) => { + // 업데이트에 실패한 경우 이전 값으로 반환 + queryClient.setQueryData([SUBSCRIBE_URL], context?.prevData); + addToast({ type: 'error', message: '알림 해제에 실패했습니다', time: 3000 }); + }, + + onSettled: () => { + queryClient.invalidateQueries({ queryKey: [SUBSCRIBE_URL] }); + }, + }); + + const currentSubscribe = useSuspenseQuery({ + queryKey: [SUBSCRIBE_URL], + queryFn: async () => { + const response = await WebPushSubscribeAPI.currentSubscribe(); + throwOnInvalidStatus(response); + + const data = await response.json(); + return data; + }, + retry: noRetryIfUnauthorized, + }); + + return { + subscribe: subscribe.mutate, + unSubscribe: unSubscribe.mutate, + currentSubscribe: currentSubscribe.data.isSubscribe, + }; +}; + +export default useWebPush; diff --git a/frontend/src/hooks/queries/petPlant/useRegisterPetPlant.ts b/frontend/src/hooks/queries/petPlant/useRegisterPetPlant.ts index 237328a65..b9d32ecae 100644 --- a/frontend/src/hooks/queries/petPlant/useRegisterPetPlant.ts +++ b/frontend/src/hooks/queries/petPlant/useRegisterPetPlant.ts @@ -18,6 +18,7 @@ const useRegisterPetPlant = () => { throwOnInvalidStatus(response); }, + // TODO: 첫 식물 등록시에 navigate로 반환하고 알림 설정하겠냐고 물어보기 onSuccess: () => { addToast({ type: 'success', message: '반려 식물 등록에 성공했어요' }); navigate(URL_PATH.petList, { replace: true }); diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 1a1754b2a..d1c78ff07 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,20 +1,24 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; import App from './App'; -import swDev from './swDev'; +import registerServiceWork from './registerServiceWork'; if (process.env.NODE_ENV === 'development') { // eslint-disable-next-line @typescript-eslint/no-var-requires const { worker } = require('./mocks/browser'); + registerServiceWork('/devLocalServiceWorker.js'); worker.start({ serviceWorker: { - url: 'http://localhost:8282/mockServiceWorker.js', + url: 'http://localhost:8282/devLocalServiceWorker.js', }, }); -} else { - swDev(); } + +if (process.env.NODE_ENV === 'production') { + registerServiceWork('/firebase-messaging-sw.js'); +} + const root = createRoot(document.getElementById('root') as HTMLElement); root.render( diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index d255b0b60..b2fac8e9d 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -241,5 +241,41 @@ export const makeHandler = (delay = 0, failRate = 0) => { // 쿠키 갱신 return res(ctx.delay(delay), ctx.cookie('JSESSION', JSESSION)); }), + + rest.get('/members/notification', (req, res, ctx) => { + const { JSESSION } = req.cookies; + + if (JSESSION === undefined) { + return res(ctx.delay(delay), ctx.status(401), ctx.json({ message: '만료된 세션입니다.' })); + } + + const token = JSON.parse(sessionStorage.getItem('FCM_TOKEN') ?? 'false'); + let isSubscribe = false; + if (token) isSubscribe = true; + + return res(ctx.delay(delay), ctx.json({ isSubscribe })); + }), + + rest.post('/members/notification', async (req, res, ctx) => { + const { token } = await req.json(); + + if (Math.random() < failRate) { + return res(ctx.delay(delay), ctx.status(407)); + } + + sessionStorage.setItem('FCM_TOKEN', JSON.stringify(token)); + + return res(ctx.delay(delay), ctx.status(204)); + }), + + rest.delete('/members/notification', (req, res, ctx) => { + if (Math.random() < failRate) { + return res(ctx.delay(delay), ctx.status(407)); + } + + sessionStorage.removeItem('FCM_TOKEN'); + + return res(ctx.delay(delay), ctx.status(204)); + }), ]; }; diff --git a/frontend/src/pages/@common/Main/index.tsx b/frontend/src/pages/@common/Main/index.tsx index a4075ca86..a3ee79517 100644 --- a/frontend/src/pages/@common/Main/index.tsx +++ b/frontend/src/pages/@common/Main/index.tsx @@ -14,7 +14,6 @@ const Main = () => { return ( <> - 식물을 쉽게 diff --git a/frontend/src/pages/auth/Login/Authorization.tsx b/frontend/src/pages/auth/Login/Authorization.tsx index 935ef34a5..2ce0665ab 100644 --- a/frontend/src/pages/auth/Login/Authorization.tsx +++ b/frontend/src/pages/auth/Login/Authorization.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react'; import { Navigate, useSearchParams } from 'react-router-dom'; import useLogin from 'hooks/queries/auth/useLogin'; import { URL_PATH } from 'constants/index'; @@ -5,8 +6,11 @@ import { URL_PATH } from 'constants/index'; const Authorization = () => { const [searchParams] = useSearchParams(); const code = searchParams.get('code'); + const { mutate: login } = useLogin(); - useLogin(code ?? ''); + useEffect(() => { + login(code ?? ''); + }, [code, login]); return ; }; diff --git a/frontend/src/pages/auth/MyPage/MyPage.style.ts b/frontend/src/pages/auth/MyPage/MyPage.style.ts index 04f1f41c3..c68b0a32f 100644 --- a/frontend/src/pages/auth/MyPage/MyPage.style.ts +++ b/frontend/src/pages/auth/MyPage/MyPage.style.ts @@ -3,9 +3,12 @@ import styled from 'styled-components'; export const Wrapper = styled.main` position: relative; + display: flex; flex-direction: column; + height: calc(100% - 68px); + padding: 16px 0; `; export const ButtonBox = styled.section` @@ -24,6 +27,32 @@ export const ButtonBox = styled.section` } `; +export const PushAlertWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 8px; + justify-content: space-around; + + width: 80%; + margin: 0 auto; +`; + +export const WarnParagraph = styled.p` + font: 400 1.2rem/1.6rem NanumSquareRound; + color: ${({ theme }) => theme.color.accent}; + text-align: right; +`; + +export const PushAlertContent = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + p { + font: 600 1.6rem/2rem NanumSquareRound; + } +`; + export const Button = styled.button` height: 45px; color: ${({ theme: { color } }) => color.gray}; diff --git a/frontend/src/pages/auth/MyPage/index.tsx b/frontend/src/pages/auth/MyPage/index.tsx index cd0fb4dfc..75b8cd092 100644 --- a/frontend/src/pages/auth/MyPage/index.tsx +++ b/frontend/src/pages/auth/MyPage/index.tsx @@ -2,9 +2,19 @@ import { FixedButtonArea } from 'pages/garden/GardenPostList/GardenPostList.styl import ContentHeader from 'components/@common/ContentHeader'; import Navbar from 'components/@common/Navbar'; import SvgFill from 'components/@common/SvgIcons/SvgFill'; +import Toggle from 'components/@common/Toggle'; import VerticalDivider from 'components/@common/VerticalDivider/VerticalDivider.style'; -import { BottomSheet, Button, ButtonBox, Wrapper } from './MyPage.style'; +import { + BottomSheet, + Button, + ButtonBox, + PushAlertContent, + PushAlertWrapper, + WarnParagraph, + Wrapper, +} from './MyPage.style'; import useConfirm from 'hooks/@common/useConfirm'; +import usePushAlert from 'hooks/@common/usePushAlert'; import useCheckSessionId from 'hooks/queries/auth/useCheckSessionId'; import useLogout from 'hooks/queries/auth/useLogout'; import useWithdraw from 'hooks/queries/auth/useWithdraw'; @@ -14,6 +24,10 @@ const MyPage = () => { useCheckSessionId(); const { mutate: logoutMutate } = useLogout(); const { mutate: withdrawMutate } = useWithdraw(); + + const { currentSubscribe, pushSupport, notificationDenied, subscribeAlert, unSubscribeAlert } = + usePushAlert(); + const confirm = useConfirm(); const handleLogout: React.MouseEventHandler = () => { @@ -32,6 +46,25 @@ const MyPage = () => { <> + + +

리마인더 알림 받기

+ +
+ {!pushSupport && 지원하지 않는 브라우저 또는 os입니다.} + {notificationDenied === 'denied' && ( + + 브라우저 알림을 허용하지 않았습니다. 허용하기 위해서는 설정 {'>'} 알림 허용을 해주세요 + + )} +