diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4417de9..f55e7f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,6 +21,9 @@ jobs: - name: Install dependencies run: npm ci + - name: Install Playwright browser + run: npx playwright install --with-deps chromium + - name: Lint run: npm run lint -- --max-warnings=0 @@ -30,3 +33,6 @@ jobs: - name: Build run: npm run build + - name: Browser smoke tests + run: npm run test:e2e + diff --git a/package-lock.json b/package-lock.json index 83f53b1..0dcccbc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,6 +51,7 @@ }, "devDependencies": { "@eslint/js": "^9.13.0", + "@playwright/test": "^1.58.2", "@types/firebase": "^2.4.32", "@types/lodash": "^4.17.18", "@types/luxon": "^3.4.2", @@ -72,6 +73,10 @@ "typescript": "~5.6.2", "typescript-eslint": "^8.11.0", "vite": "^5.4.10" + }, + "engines": { + "node": ">=20.0.0", + "npm": ">=10.0.0" } }, "node_modules/@adraffy/ens-normalize": { @@ -164,7 +169,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "license": "MIT", - "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", @@ -568,7 +572,6 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -612,7 +615,6 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -1755,7 +1757,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.2.tgz", "integrity": "sha512-jwtMmJa1BXXDCiDx1vC6SFN/+HfYG53UkfJa6qeN5ogvOunzbFDO3wISZy5n9xgYFUrEP6M7e8EG++riHNTv9w==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/component": "0.6.18", "@firebase/logger": "0.4.4", @@ -1822,7 +1823,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.2.tgz", "integrity": "sha512-LssbyKHlwLeiV8GBATyOyjmHcMpX/tFjzRUCS1jnwGAew1VsBB4fJowyS5Ud5LdFbYpJeS+IQoC+RQxpK7eH3Q==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/app": "0.13.2", "@firebase/component": "0.6.18", @@ -1838,8 +1838,7 @@ "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@ethora/ai-chat-widget/node_modules/@firebase/auth-compat": { "version": "0.5.28", @@ -2290,7 +2289,6 @@ "integrity": "sha512-zGlBn/9Dnya5ta9bX/fgEoNC3Cp8s6h+uYPYaDieZsFOAdHP/ExzQ/eaDgxD3GOROdPkLKpvKY0iIzr9adle0w==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -2309,7 +2307,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.12.tgz", "integrity": "sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -2496,7 +2493,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.13.1.tgz", "integrity": "sha512-0O33PKrXLoIWkoOO5ByFaLjZehBctSYWnb+xJkIdx2SKP/K9l1UPFXPwASyrOIqyY3ws+7orF/1j7wI5EKzPYQ==", - "peer": true, "dependencies": { "@firebase/component": "0.6.17", "@firebase/logger": "0.4.4", @@ -2558,7 +2554,6 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.4.1.tgz", "integrity": "sha512-9VGjnY23Gc1XryoF/ABWtZVJYnaPOnjHM7dsqq9YALgKRtxI1FryvELUVkDaEIUf4In2bfkb9ZENF1S9M273Dw==", - "peer": true, "dependencies": { "@firebase/app": "0.13.1", "@firebase/component": "0.6.17", @@ -2573,8 +2568,7 @@ "node_modules/@ethora/chat-component/node_modules/@firebase/app-types": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.3.tgz", - "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==", - "peer": true + "integrity": "sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==" }, "node_modules/@ethora/chat-component/node_modules/@firebase/auth-compat": { "version": "0.5.27", @@ -2993,7 +2987,6 @@ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.12.0.tgz", "integrity": "sha512-Z4rK23xBCwgKDqmzGVMef+Vb4xso2j5Q8OG0vVL4m4fA5ZjPMYQazu8OJJC3vtQRC3SQ/Pgx/6TPNVsCd70QRw==", "hasInstallScript": true, - "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -3010,7 +3003,6 @@ "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -3219,7 +3211,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.13.tgz", "integrity": "sha512-OZiDAEK/lDB6xy/XzYAyJJkaDqmQ+BCtOEPLqFvxWKUz5JbBmej7IiiRHdtiIOD/twW7O5AxVsfaaGA/V1bNsA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/component": "0.6.9", "@firebase/logger": "0.4.2", @@ -3277,7 +3268,6 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.43.tgz", "integrity": "sha512-HM96ZyIblXjAC7TzE8wIk2QhHlSvksYkQ4Ukh1GmEenzkucSNUmUX4QvoKrqeWsLEQ8hdcojABeCV8ybVyZmeg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@firebase/app": "0.10.13", "@firebase/component": "0.6.9", @@ -3290,8 +3280,7 @@ "version": "0.9.2", "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz", "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/@firebase/auth-compat": { "version": "0.5.14", @@ -3712,7 +3701,6 @@ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.10.0.tgz", "integrity": "sha512-xKtx4A668icQqoANRxyDLBLz51TAbDP9KRfpbKGxiCAW346d0BeJe5vN6/hKxxmWwnZ0mautyv39JxviwwQMOQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } @@ -4191,7 +4179,6 @@ "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.8.tgz", "integrity": "sha512-5S9UTjKZZBd9GfbcYh/nYfD9cv6OXmj5Y7NgKYfk7JcSoshp8/pW5zP4wecRiroBSZX8wcrywSgogpVNO+5W0Q==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.26.0", "@mui/core-downloads-tracker": "^6.4.8", @@ -4750,6 +4737,22 @@ "node": ">=14" } }, + "node_modules/@playwright/test": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz", + "integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -5306,7 +5309,6 @@ "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-4.10.0.tgz", "integrity": "sha512-KrMOL+sH69htCIXCaZ4JluJ35bchuCCznyPyrbN8JXSGQfwBI1SuIEMZNwvy8L8ykj29t6sa5BAAiL7fNoLZ8A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.16" } @@ -5565,7 +5567,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.19.tgz", "integrity": "sha512-fcdJqaHOMDbiAwJnXv6XCzX0jDW77yI3tJqYh1Byn8EL5/S628WRx9b/y3DnNe55zTukUQKrfYxiZls2dHcUMw==", "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -5709,7 +5710,6 @@ "integrity": "sha512-XGwIabPallYipmcOk45DpsBSgLC64A0yvdAkrwEzwZ2viqGqRUJ8eEYoPz0CWnutgAFbNMPdsGGvzjSmcWVlEA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.27.0", "@typescript-eslint/types": "8.27.0", @@ -6245,7 +6245,6 @@ "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6591,7 +6590,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -7429,8 +7427,7 @@ "version": "5.6.0", "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.6.0.tgz", "integrity": "sha512-eJp3QRe79pjwa+duv+n7+5YsNhRcMl812EcFVwrnRvYKoNPoQb5qxU8DG6Bgwji0akHdp6D4Ln6tYLG58MFSow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -7658,7 +7655,6 @@ "integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -13292,7 +13288,6 @@ "version": "4.0.2", "inBundle": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13911,6 +13906,53 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -13938,7 +13980,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -14239,7 +14280,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -14291,7 +14331,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -14639,15 +14678,13 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-persist": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", "license": "MIT", - "peer": true, "peerDependencies": { "redux": ">4.0.0" } @@ -15768,7 +15805,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -15992,7 +16028,6 @@ "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -16369,7 +16404,6 @@ "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", diff --git a/package.json b/package.json index f5444e3..c6aaa6c 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,18 @@ "private": true, "version": "0.0.0", "type": "module", + "engines": { + "node": ">=20.0.0", + "npm": ">=10.0.0" + }, "scripts": { "dev": "vite", "build": "tsc -b && vite build", "typecheck": "tsc -b --noEmit", "lint": "eslint .", "preview": "vite preview", - "prettier": "prettier --write ." + "prettier": "prettier --write .", + "test:e2e": "playwright test" }, "dependencies": { "@emotion/react": "^11.13.3", @@ -55,6 +60,7 @@ }, "devDependencies": { "@eslint/js": "^9.13.0", + "@playwright/test": "^1.58.2", "@types/firebase": "^2.4.32", "@types/lodash": "^4.17.18", "@types/luxon": "^3.4.2", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..d698e80 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,35 @@ +import { defineConfig, devices } from '@playwright/test'; + +const port = Number(process.env.PLAYWRIGHT_PORT || 4173); +const host = process.env.PLAYWRIGHT_HOST || '127.0.0.1'; +const baseURL = process.env.PLAYWRIGHT_BASE_URL || `http://${host}:${port}`; + +export default defineConfig({ + testDir: './tests/e2e', + timeout: 30_000, + fullyParallel: true, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 2 : 0, + reporter: process.env.CI ? [['github'], ['html', { open: 'never' }]] : 'list', + use: { + baseURL, + trace: 'retain-on-failure', + screenshot: 'only-on-failure', + video: 'retain-on-failure', + }, + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + ], + webServer: { + command: `npm run dev -- --host ${host} --port ${port}`, + url: `${baseURL}/login`, + reuseExistingServer: !process.env.CI, + stdout: 'ignore', + stderr: 'pipe', + }, +}); diff --git a/src/actions.ts b/src/actions.ts index e8ec527..81652ca 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -7,6 +7,7 @@ import { httpGetConfig, httpGetUsers, httpPostFile, + httpUploadPushFirebaseServiceAccount, httpResetPasswords, httpTokens, httpUpdateApp, @@ -166,6 +167,13 @@ export async function actionPostFile(file: File) { return httpPostFile(file); } +export async function actionUploadPushFirebaseServiceAccount( + appId: string, + file: File +) { + return httpUploadPushFirebaseServiceAccount(appId, file); +} + export async function actionGetUsers( appId: string, limit: number = 10, diff --git a/src/http.ts b/src/http.ts index 937235d..85469e6 100644 --- a/src/http.ts +++ b/src/http.ts @@ -207,6 +207,12 @@ export function httpPostFile(file: File) { return http.post('/files', fd); } +export function httpUploadPushFirebaseServiceAccount(appId: string, file: File) { + const fd = new FormData(); + fd.append('firebaseServiceAccount', file); + return http.post(`/push/firebase-service-account/${appId}`, fd); +} + export function httpGetUsers( appId: string, limit: number = 10, diff --git a/src/pages/AppSettings/AppSettings.tsx b/src/pages/AppSettings/AppSettings.tsx index a4d5cb1..78a2bbe 100644 --- a/src/pages/AppSettings/AppSettings.tsx +++ b/src/pages/AppSettings/AppSettings.tsx @@ -714,7 +714,6 @@ export default function AppSettings() { diff --git a/src/pages/AppSettings/MobileApp.tsx b/src/pages/AppSettings/MobileApp.tsx index e102a9a..e40db84 100644 --- a/src/pages/AppSettings/MobileApp.tsx +++ b/src/pages/AppSettings/MobileApp.tsx @@ -1,30 +1,35 @@ import { useRef } from 'react'; import { NavLink } from 'react-router-dom'; -import { actionPostFile } from '../../actions'; +import { toast } from 'react-toastify'; +import { actionUploadPushFirebaseServiceAccount } from '../../actions'; import { IconUpload } from '../../components/Icons/IconUpload'; interface Props { appId: string; - setGoogleServicesJson: (s: string) => void; primaryColor: string; } export function MobileApp({ appId, - setGoogleServicesJson, primaryColor, }: Props) { - const googleJsonRef = useRef(null); + const pushServiceAccountRef = useRef(null); - const onGoogleJsonRefChanges = (file: File | null) => { - if (!file) { - return; - } - - actionPostFile(file).then((resp) => { - setGoogleServicesJson(resp.data.results[0].location); - }); + const onPushServiceAccountChanges = (file: File | null) => { + if (!file) return; + actionUploadPushFirebaseServiceAccount(appId, file) + .then(() => { + toast.success('Push service account uploaded'); + }) + .catch((e) => { + const msg = + e?.response?.data?.error || + e?.response?.data?.details || + e?.message || + 'Failed to upload push service account'; + toast.error(String(msg)); + }); }; return ( @@ -108,20 +113,20 @@ export function MobileApp({ Push Notifications

- Follow this manual to set up your Firebase account. Extract and upload your service-account.json. This will enable your users to receive push notifications for chat messages they missed while being offline. + Follow this manual to set up your Firebase account. Upload your service-account.json here to enable push notifications for offline chat messages.

- onGoogleJsonRefChanges(e.target.files && e.target.files[0]) + onPushServiceAccountChanges(e.target.files && e.target.files[0]) } />