From 63ad1a49c56eca805468e2c80862304a290c03c2 Mon Sep 17 00:00:00 2001 From: TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com> Date: Fri, 7 Oct 2022 00:56:19 -0400 Subject: [PATCH 01/40] add custom user fields - Allow custom user fields to be added and saved in the user table - Data json column handles the data dynamically - Includes migration - Dynamically creates TextField inputs on the profile page for any number of fields you add to your config - Can be formatted as an array of strings or array of objects - If you use a string, the string is used as the name that is displayed and saved in the db json - If you use objects, you have extra customization: - You can have different locales displayed, use the locales as the keys and the display name as the value - Use the `name` key as the fallback for any missing locales - Use the `database` key as the key that will be saved in the database json --- package.json | 2 +- server/src/configs/default.json | 3 +- .../20221006032139_add_data_column.cjs | 22 ++++++++ server/src/graphql/resolvers.js | 15 ++++++ server/src/graphql/typeDefs.js | 2 +- server/src/routes/rootRouter.js | 1 + src/components/layout/dialogs/UserProfile.jsx | 50 +++++++++++++++++++ src/hooks/useConfig.js | 13 +++-- src/hooks/useStore.js | 2 + src/services/queries/user.js | 6 +++ 10 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 server/src/db/migrations/20221006032139_add_data_column.cjs diff --git a/package.json b/package.json index d5a98eca7..9fd85002f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "reactmap", - "version": "1.4.1", + "version": "1.5.0", "description": "React based frontend map.", "main": "ReactMap.mjs", "author": "TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com>", diff --git a/server/src/configs/default.json b/server/src/configs/default.json index 2aceb87ae..680b5d3bb 100644 --- a/server/src/configs/default.json +++ b/server/src/configs/default.json @@ -455,7 +455,8 @@ "gymBadgeTableName": "gymBadges", "sessionTableName": "session", "migrationTableName": "knex_migrations", - "maxConnections": 10 + "maxConnections": 10, + "extraUserFields": [] }, "schemas": [] }, diff --git a/server/src/db/migrations/20221006032139_add_data_column.cjs b/server/src/db/migrations/20221006032139_add_data_column.cjs new file mode 100644 index 000000000..49e4cceb8 --- /dev/null +++ b/server/src/db/migrations/20221006032139_add_data_column.cjs @@ -0,0 +1,22 @@ +/* eslint-disable no-unused-vars */ +const { database: { settings: { userTableName: tableName } } } = require('../../services/config') + +/** + * @typedef {import("knex")} Knex + */ + +/** + * @param {Knex} knex + */ +exports.up = async (knex) => knex.schema + .table(tableName, (table) => { + table.json('data') + }) + +/** + * @param {Knex} knex + */ +exports.down = async (knex) => knex.schema + .table(tableName, (table) => { + table.dropColumn('data') + }) diff --git a/server/src/graphql/resolvers.js b/server/src/graphql/resolvers.js index 41f1392f3..0af2c6265 100644 --- a/server/src/graphql/resolvers.js +++ b/server/src/graphql/resolvers.js @@ -388,6 +388,21 @@ module.exports = { const results = await User.query().where('username', args.username) return Boolean(results.length) }, + setExtraFields: async (_, { key, value }, { req }) => { + if (req.user?.id) { + const user = await User.query().findById(req.user.id) + if (user) { + const data = + typeof user.data === 'string' + ? JSON.parse(user.data) + : user.data || {} + data[key] = value + await user.$query().update({ data: JSON.stringify(data) }) + } + return true + } + return false + }, setGymBadge: async (_, args, { req }) => { const perms = req.user ? req.user.perms : false if (perms?.gymBadges && req?.user?.id) { diff --git a/server/src/graphql/typeDefs.js b/server/src/graphql/typeDefs.js index 851c22ff0..0eed5b958 100644 --- a/server/src/graphql/typeDefs.js +++ b/server/src/graphql/typeDefs.js @@ -81,7 +81,6 @@ module.exports = gql` webhookName: String ts: Int midnight: Int - onlyAreas: [String] ): [Search] searchQuest( @@ -123,5 +122,6 @@ module.exports = gql` strategy(strategy: String): Boolean checkUsername(username: String): Boolean setGymBadge(gymId: String, badge: Int): Boolean + setExtraFields(key: String, value: String): Boolean } ` diff --git a/server/src/routes/rootRouter.js b/server/src/routes/rootRouter.js index 1e14ca7ee..6b474ab7f 100644 --- a/server/src/routes/rootRouter.js +++ b/server/src/routes/rootRouter.js @@ -178,6 +178,7 @@ rootRouter.get('/settings', async (req, res) => { gymValidDataLimit: Date.now() / 1000 - config.api.gymValidDataLimit * 86400, }, + extraUserFields: config.database.settings.extraUserFields, available: { pokemon: [], pokestops: [], gyms: [], nests: [] }, } diff --git a/src/components/layout/dialogs/UserProfile.jsx b/src/components/layout/dialogs/UserProfile.jsx index 9ea21ae54..eaeed1a10 100644 --- a/src/components/layout/dialogs/UserProfile.jsx +++ b/src/components/layout/dialogs/UserProfile.jsx @@ -15,6 +15,7 @@ import { CardContent, IconButton, Dialog, + TextField, } from '@material-ui/core' import { Edit } from '@material-ui/icons' import { useTranslation } from 'react-i18next' @@ -72,6 +73,7 @@ export default function UserProfile({ setUserProfile, isMobile, isTablet }) {
+ { ) } +const ExtraFields = ({ auth }) => { + const extraUserFields = useStatic((state) => state.extraUserFields) + const setAuth = useStatic((state) => state.setAuth) + + const [setField] = useMutation(Query.user('setExtraFields')) + + return ( + + {extraUserFields.map((field) => { + const locale = localStorage.getItem('i18nextLng') || 'en' + const label = + typeof field === 'string' ? field : field[locale] || field.name + const key = typeof field === 'string' ? field : field.database + if (!key || !label) return null + return ( + + { + setAuth({ + ...auth, + data: { + ...auth.data, + [key]: value, + }, + }) + setField({ + variables: { + key, + value, + }, + }) + }} + /> + + ) + })} + + ) +} + const ProfilePermissions = ({ perms, excludeList, t }) => { const { map: { permImageDir, permArrayImages }, diff --git a/src/hooks/useConfig.js b/src/hooks/useConfig.js index ef0fd4a10..4262be0ec 100644 --- a/src/hooks/useConfig.js +++ b/src/hooks/useConfig.js @@ -38,6 +38,7 @@ export default function useConfig(serverSettings, params) { const setStaticFilters = useStatic((state) => state.setFilters) const setWebhookData = useStatic((state) => state.setWebhookData) const setIsNight = useStatic((state) => state.setIsNight) + const setExtraUserFields = useStatic((state) => state.setExtraUserFields) const localState = JSON.parse(localStorage.getItem('local-state')) @@ -58,14 +59,15 @@ export default function useConfig(serverSettings, params) { } setAuth({ - strategy: serverSettings.user.strategy, - discordId: serverSettings.user.discordId, - telegramId: serverSettings.user.telegramId, - webhookStrategy: serverSettings.user.webhookStrategy, + strategy: serverSettings.user?.strategy || '', + discordId: serverSettings.user?.discordId || '', + telegramId: serverSettings.user?.telegramId || '', + webhookStrategy: serverSettings.user?.webhookStrategy || '', loggedIn: serverSettings.loggedIn, perms: serverSettings.user ? serverSettings.user.perms : {}, methods: serverSettings.authMethods, - username: serverSettings.user.username, + username: serverSettings.user?.username || '', + data: serverSettings.user?.data || {}, }) Sentry.setUser({ username: serverSettings.user.username, @@ -86,6 +88,7 @@ export default function useConfig(serverSettings, params) { setAvailable(serverSettings.available) setMenus(updateObjState(serverSettings.menus, 'menus')) setStaticMenus(serverSettings.menus) + setExtraUserFields(serverSettings.extraUserFields) if (localState?.state?.filters?.pokemon?.standard) { delete localState.state.filters.pokemon.standard diff --git a/src/hooks/useStore.js b/src/hooks/useStore.js index 7e214920a..e59253a8b 100644 --- a/src/hooks/useStore.js +++ b/src/hooks/useStore.js @@ -138,4 +138,6 @@ export const useStatic = create((set) => ({ setFeedback: (feedback) => set({ feedback }), resetFilters: false, setResetFilters: (resetFilters) => set({ resetFilters }), + extraUserFields: [], + setExtraUserFields: (extraUserFields) => set({ extraUserFields }), })) diff --git a/src/services/queries/user.js b/src/services/queries/user.js index 47f71ea4d..c31cd43c7 100644 --- a/src/services/queries/user.js +++ b/src/services/queries/user.js @@ -23,3 +23,9 @@ export const setGymBadge = gql` setGymBadge(gymId: $gymId, badge: $badge) } ` + +export const setExtraFields = gql` + mutation SetExtraFields($key: String, $value: String) { + setExtraFields(key: $key, value: $value) + } +` \ No newline at end of file From 8c7f875e997854a9cda83675cdf42ac670ec967e Mon Sep 17 00:00:00 2001 From: TurtIeSocks Date: Fri, 7 Oct 2022 04:56:41 +0000 Subject: [PATCH 02/40] Synced docker env vars with latest config Files changed:\nM server/src/configs/custom-environment-variables.json --- server/src/configs/custom-environment-variables.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/configs/custom-environment-variables.json b/server/src/configs/custom-environment-variables.json index f234bdc9e..cb4fbd113 100644 --- a/server/src/configs/custom-environment-variables.json +++ b/server/src/configs/custom-environment-variables.json @@ -938,6 +938,10 @@ "maxConnections": { "__name": "DATABASE_SETTINGS_MAX_CONNECTIONS", "__format": "number" + }, + "extraUserFields": { + "__name": "DATABASE_SETTINGS_EXTRA_USER_FIELDS", + "__format": "json" } }, "schemas": { From 595fb36dced9e024872258798c00a7882da57a3b Mon Sep 17 00:00:00 2001 From: TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:33:45 -0400 Subject: [PATCH 03/40] house keeping - Cleanup some eslint rules - Lint some various jsons - Convert all `.mjs` files back to standard `.js` files for less PM2 headaches in the long run - Keep `ReactMap.mjs` as a polyfill with a deprecation warning after version 1.8.0 - Set all Location icons to the crosshairs style - Cleanup old vscode extensions - Format on save --- .eslintignore | 3 +- .eslintrc | 1 + .prettierignore | 3 +- .vscode/extensions.json | 5 +- .vscode/settings.json | 7 +- ReactMap.js | 11 ++ ReactMap.mjs | 15 +- esbuild.config.js | 175 +++++++++++++++++ esbuild.config.mjs | 180 ------------------ jsconfig.json | 8 +- package.json | 12 +- server/src/data/defaultRarity.json | 49 ++++- server/src/index.js | 1 - server/src/routes/api/apiIndex.js | 1 - server/src/routes/authRouter.js | 1 - server/src/services/DiscordClient.js | 1 - server/src/services/config.js | 1 - src/components/Map.jsx | 2 +- .../layout/dialogs/tutorial/data.json | 127 +++--------- .../layout/dialogs/webhooks/Location.jsx | 4 +- src/services/queries/user.js | 2 +- 21 files changed, 284 insertions(+), 325 deletions(-) create mode 100644 ReactMap.js create mode 100644 esbuild.config.js delete mode 100644 esbuild.config.mjs diff --git a/.eslintignore b/.eslintignore index 8baa9f15c..e15880cb6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,5 @@ dist node_modules public/missing-locales public/images/custom -public/images/uicons \ No newline at end of file +public/images/uicons +server/src/configs/ diff --git a/.eslintrc b/.eslintrc index 63aa8e312..1e8023715 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,6 +19,7 @@ "inject": true }, "rules": { + "global-require": 0, "camelcase": 0, "react/jsx-props-no-spreading": 0, "linebreak-style": 0, diff --git a/.prettierignore b/.prettierignore index 8baa9f15c..e15880cb6 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,5 @@ dist node_modules public/missing-locales public/images/custom -public/images/uicons \ No newline at end of file +public/images/uicons +server/src/configs/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 89f244ad1..69466d568 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -4,8 +4,7 @@ "dbaeumer.vscode-eslint", "lokalise.i18n-ally", "esbenp.prettier-vscode", - "leizongmin.node-module-intellisense", - "eg2.vscode-npm-script", + "christian-kohler.npm-intellisense", "graphql.vscode-graphql" ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 4bb8cedef..e71a380c8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,5 @@ { - "i18n-ally.localesPaths": [ - "public/base-locales" - ], + "i18n-ally.localesPaths": ["public/base-locales"], "i18n-ally.keystyle": "flat", "[javascript]": { "editor.autoClosingBrackets": "always", @@ -11,4 +9,5 @@ "editor.autoClosingBrackets": "always", "editor.defaultFormatter": "esbenp.prettier-vscode" }, -} \ No newline at end of file + "editor.formatOnSave": true +} diff --git a/ReactMap.js b/ReactMap.js new file mode 100644 index 000000000..7ee053f1d --- /dev/null +++ b/ReactMap.js @@ -0,0 +1,11 @@ +const { generate } = require('./server/scripts/generateMasterfile') +const { locales } = require('./server/scripts/createLocales') +const { connection } = require('./server/knexfile.cjs') + +connection.migrate.latest().then(() => + generate(true).then(() => + locales() + .then(() => require('./esbuild.config.js')) + .then(() => require('./server/src/index')), + ), +) diff --git a/ReactMap.mjs b/ReactMap.mjs index be829f56f..e096b4004 100644 --- a/ReactMap.mjs +++ b/ReactMap.mjs @@ -1,11 +1,6 @@ -/* eslint-disable import/extensions */ -import { generate } from './server/scripts/generateMasterfile.js' -import { locales } from './server/scripts/createLocales.js' -import { connection } from './server/knexfile.cjs' +/* eslint-disable no-console */ +import './ReactMap.js' -connection.migrate.latest().then(() => ( - generate(true) - .then(() => locales() - .then(() => import('./esbuild.config.mjs')) - .then(() => import('./server/src/index.js'))) -)) +console.warn( + '[WARN] ReactMap.mjs is being deprecated and support will be removed in version 1.8.0. Please change your PM2 script to use ReactMap.js instead.', +) diff --git a/esbuild.config.js b/esbuild.config.js new file mode 100644 index 000000000..b19a4bde2 --- /dev/null +++ b/esbuild.config.js @@ -0,0 +1,175 @@ +/* eslint-disable no-continue */ +/* eslint-disable import/no-extraneous-dependencies */ +/* eslint-disable no-console */ +const { resolve, extname } = require('path') +const fs = require('fs') +const dotenv = require('dotenv') +const { build: compile } = require('esbuild') +const { createServer } = require('esbuild-server') +const { htmlPlugin } = require('@craftamap/esbuild-plugin-html') +const esbuildMxnCopy = require('esbuild-plugin-mxn-copy') +const aliasPlugin = require('esbuild-plugin-path-alias') +const { eslintPlugin } = require('esbuild-plugin-eslinter') + +const env = fs.existsSync(resolve(__dirname, '.env')) + ? dotenv.config() + : { parsed: process.env } +const { version } = JSON.parse( + fs.readFileSync(resolve(__dirname, 'package.json')), +) +const isDevelopment = Boolean(process.argv.includes('--dev')) +const isRelease = Boolean(process.argv.includes('--release')) +const isServing = Boolean(process.argv.includes('--serve')) + +const hasCustom = (function checkFolders(folder, isCustom = false) { + const files = fs.readdirSync(folder) + for (let i = 0; i < files.length; i += 1) { + if (isCustom) return true + if (files[i].startsWith('.')) continue + if (!files[i].includes('.')) + isCustom = checkFolders(`${folder}/${files[i]}`, isCustom) + if (/\.custom.(jsx?|css)$/.test(files[i])) return true + } + return isCustom +})(resolve(__dirname, 'src')) + +if (fs.existsSync(resolve(__dirname, 'dist'))) { + console.log('[BUILD] Cleaning up old build') + fs.rm(resolve(__dirname, 'dist'), { recursive: true }, (err) => { + if (err) console.log(err) + }) +} + +const plugins = [ + htmlPlugin({ + files: [ + { + entryPoints: ['src/index.jsx'], + filename: 'index.html', + htmlTemplate: fs.readFileSync(resolve(__dirname, 'public/index.html')), + scriptLoading: 'defer', + favicon: fs.existsSync(resolve(__dirname, 'public/favicon/favicon.ico')) + ? resolve(__dirname, 'public/favicon/favicon.ico') + : resolve(__dirname, 'public/favicon/fallback.ico'), + extraScripts: isServing + ? [{ src: '/esbuild-livereload.js', attrs: { async: true } }] + : undefined, + }, + ], + }), + esbuildMxnCopy({ + copy: [ + { from: resolve(__dirname, './public/images'), to: 'dist/' }, + { from: resolve(__dirname, './public/locales'), to: 'dist/' }, + ], + }), + aliasPlugin({ + '@components': resolve(__dirname, './src/components'), + '@assets': resolve(__dirname, './src/assets'), + '@hooks': resolve(__dirname, './src/hooks'), + '@services': resolve(__dirname, './src/services'), + }), +] + +if (isDevelopment) { + plugins.push(eslintPlugin()) +} else { + if (hasCustom) { + plugins.push({ + name: 'Custom Loader', + setup(build) { + const customPaths = [] + build.onLoad({ filter: /\.(jsx?|css)$/ }, async (args) => { + const isNodeModule = /node_modules/.test(args.path) + if (!isNodeModule) { + const ext = extname(args.path) + const newPath = args.path.replace(ext, `.custom${ext}`) + // console.log(ext, newPath) + if (fs.existsSync(newPath)) { + customPaths.push(newPath) + return { + contents: fs.readFileSync(newPath, 'utf8'), + loader: ext.replace('.', ''), + watchFiles: isDevelopment ? [newPath] : undefined, + } + } + } + }) + build.onEnd(() => { + if (customPaths.length && !isDevelopment) { + console.log(` +====================================================== + WARNING: + Custom files aren't officially supported + Be sure to watch for breaking changes! + +${customPaths.map((x, i) => ` ${i + 1}. src/${x.split('src/')[1]}`).join('\n')} + +====================================================== +`) + } + }) + }, + }) + } + console.log(`[BUILD] Building production version: ${version}`) +} + +const esbuild = { + entryPoints: ['src/index.jsx'], + legalComments: 'none', + bundle: true, + outdir: 'dist/', + publicPath: '/', + entryNames: isDevelopment ? undefined : `[name]-${version}-[hash]`, + metafile: true, + minify: env.parsed.NO_MINIFIED ? false : isRelease || !isDevelopment, + logLevel: isDevelopment ? 'info' : 'error', + target: ['safari11.1', 'chrome64', 'firefox66', 'edge88'], + watch: isDevelopment + ? { + onRebuild(error) { + if (error) console.error('Recompiling failed:', error) + else console.log('Recompiled successfully') + }, + } + : false, + sourcemap: isRelease || isDevelopment, + define: { + inject: JSON.stringify({ + GOOGLE_ANALYTICS_ID: env.parsed.GOOGLE_ANALYTICS_ID || '', + ANALYTICS_DEBUG_MODE: env.parsed.ANALYTICS_DEBUG_MODE || false, + TITLE: env.parsed.TITLE || env.parsed.MAP_GENERAL_TITLE || '', + SENTRY_DSN: env.parsed.SENTRY_DSN || '', + SENTRY_TRACES_SAMPLE_RATE: env.parsed.SENTRY_TRACES_SAMPLE_RATE || 0.1, + SENTRY_DEBUG: env.parsed.SENTRY_DEBUG || false, + VERSION: version, + DEVELOPMENT: isDevelopment, + CUSTOM: hasCustom, + LOCALES: fs.readdirSync(resolve(__dirname, 'public/locales')), + }), + }, + plugins, +} + +try { + if (isServing) { + if (!env.parsed.DEV_PORT) + throw new Error( + 'DEV_PORT is not set, in .env file, it should match the port you set in your config', + ) + createServer(esbuild, { + port: +env.parsed.DEV_PORT + 1, + static: 'public', + open: true, + proxy: { + '/': `http://localhost:${env.parsed.DEV_PORT}`, + }, + }).start() + } else { + compile(esbuild).then(() => console.log('[BUILD] React Map Compiled')) + } +} catch (e) { + console.error(e) + process.exit(1) +} diff --git a/esbuild.config.mjs b/esbuild.config.mjs deleted file mode 100644 index 257a8cda4..000000000 --- a/esbuild.config.mjs +++ /dev/null @@ -1,180 +0,0 @@ -/* eslint-disable no-await-in-loop */ -/* eslint-disable no-continue */ -/* eslint-disable no-restricted-syntax */ -/* eslint-disable import/no-extraneous-dependencies */ -/* eslint-disable no-console */ -import path from 'path' -import fs from 'fs' -import { fileURLToPath } from 'url' -import dotenv from 'dotenv' -import { build as compile } from 'esbuild' -import { createServer } from 'esbuild-server' -import { htmlPlugin } from '@craftamap/esbuild-plugin-html' -import esbuildMxnCopy from 'esbuild-plugin-mxn-copy' -import aliasPlugin from 'esbuild-plugin-path-alias' -import { eslintPlugin } from 'esbuild-plugin-eslinter' - -const __dirname = path.dirname(fileURLToPath(import.meta.url)) -const env = fs.existsSync(`${__dirname}/.env`) ? dotenv.config() : { parsed: process.env } -const { version } = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'package.json'))) -const isDevelopment = Boolean(process.argv.includes('--dev')) -const isRelease = Boolean(process.argv.includes('--release')) -const isServing = Boolean(process.argv.includes('--serve')) - -const hasCustom = await (async function checkFolders(folder, isCustom = false) { - for (const file of await fs.promises.readdir(folder)) { - if (isCustom) return true - if (file.startsWith('.')) continue - if (!file.includes('.')) isCustom = await checkFolders(`${folder}/${file}`, isCustom) - if (/\.custom.(jsx?|css)$/.test(file)) return true - } - return isCustom -}(path.resolve(__dirname, 'src'))) - -if (fs.existsSync(path.resolve(__dirname, 'dist'))) { - console.log('[BUILD] Cleaning up old build') - fs.rm(path.resolve(__dirname, 'dist'), { recursive: true }, (err) => { - if (err) console.log(err) - }) -} - -const plugins = [ - htmlPlugin({ - files: [ - { - entryPoints: ['src/index.jsx'], - filename: 'index.html', - htmlTemplate: fs.readFileSync(path.resolve(__dirname, './public/index.html')), - scriptLoading: 'defer', - favicon: fs.existsSync(path.resolve(__dirname, './public/favicon/favicon.ico')) - ? path.resolve(__dirname, './public/favicon/favicon.ico') - : path.resolve(__dirname, './public/favicon/fallback.ico'), - extraScripts: isServing ? [ - { src: '/esbuild-livereload.js', attrs: { async: true } }, - ] : undefined, - }, - ], - }), - esbuildMxnCopy({ - copy: [ - { from: path.resolve(__dirname, './public/images'), to: 'dist/' }, - { from: path.resolve(__dirname, './public/locales'), to: 'dist/' }, - ], - }), - aliasPlugin({ - '@components': path.resolve(__dirname, './src/components'), - '@assets': path.resolve(__dirname, './src/assets'), - '@hooks': path.resolve(__dirname, './src/hooks'), - '@services': path.resolve(__dirname, './src/services'), - }), -] - -if (isDevelopment) { - plugins.push( - eslintPlugin(), - ) -} else { - if (hasCustom) { - plugins.push( - { - name: 'Custom Loader', - setup(build) { - const customPaths = [] - build.onLoad({ filter: /\.(jsx?|css)$/ }, async (args) => { - const isNodeModule = /node_modules/.test(args.path) - if (!isNodeModule) { - const [base, suffix] = args.path.split('.') - const newPath = `${base}.custom.${suffix}` - if (fs.existsSync(newPath)) { - customPaths.push(newPath) - return { - contents: fs.readFileSync(newPath, 'utf8'), - loader: suffix, - watchFiles: isDevelopment ? [newPath] : undefined, - } - } - } - }) - build.onEnd(() => { - if (customPaths.length && !isDevelopment) { - console.log(` -====================================================== - WARNING: - Custom files aren't officially supported - Be sure to watch for breaking changes! - -${customPaths.map((x, i) => ` ${i + 1}. src/${x.split('src/')[1]}`).join('\n')} - -====================================================== -`) - } - }) - }, - }, - ) - } - console.log(`[BUILD] Building production version: ${version}`) -} - -const esbuild = { - entryPoints: ['src/index.jsx'], - legalComments: 'none', - bundle: true, - outdir: 'dist/', - publicPath: '/', - entryNames: isDevelopment ? undefined : `[name]-${version}-[hash]`, - metafile: true, - minify: env.parsed.NO_MINIFIED - ? false - : isRelease || !isDevelopment, - logLevel: isDevelopment ? 'info' : 'error', - target: ['safari11.1', 'chrome64', 'firefox66', 'edge88'], - watch: isDevelopment - ? { - onRebuild(error) { - if (error) console.error('Recompiling failed:', error) - else console.log('Recompiled successfully') - }, - } - : false, - sourcemap: isRelease || isDevelopment, - define: { - inject: JSON.stringify({ - GOOGLE_ANALYTICS_ID: env.parsed.GOOGLE_ANALYTICS_ID || '', - ANALYTICS_DEBUG_MODE: env.parsed.ANALYTICS_DEBUG_MODE || false, - TITLE: env.parsed.TITLE || env.parsed.MAP_GENERAL_TITLE || '', - SENTRY_DSN: env.parsed.SENTRY_DSN || '', - SENTRY_TRACES_SAMPLE_RATE: env.parsed.SENTRY_TRACES_SAMPLE_RATE || 0.1, - SENTRY_DEBUG: env.parsed.SENTRY_DEBUG || false, - VERSION: version, - DEVELOPMENT: isDevelopment, - CUSTOM: hasCustom, - LOCALES: await fs.promises.readdir(`${__dirname}/public/locales`), - }), - }, - plugins, -} - -try { - if (isServing) { - if (!env.parsed.DEV_PORT) throw new Error('DEV_PORT is not set, in .env file, it should match the port you set in your config') - await createServer( - esbuild, - { - port: +env.parsed.DEV_PORT + 1, - static: 'public', - open: true, - proxy: { - '/': `http://localhost:${env.parsed.DEV_PORT}`, - }, - }, - ).start() - } else { - await compile(esbuild) - } -} catch (e) { - console.error(e) - process.exit(1) -} finally { - console.log('[BUILD] React Map Compiled') -} diff --git a/jsconfig.json b/jsconfig.json index ba0810aba..c608dbdd8 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,15 +1,15 @@ { "compilerOptions": { "target": "es2017", + "module": "commonjs", "allowSyntheticDefaultImports": false, "baseUrl": "./src", "paths": { "@assets/*": ["./assets/*"], "@components/*": ["./components/*"], "@services/*": ["./services/*"], - "@hooks/*": ["./hooks/*"], - "@classes/*": ["./classes/*"], + "@hooks/*": ["./hooks/*"] } }, - "exclude": ["node_modules", "dist"], -} \ No newline at end of file + "exclude": ["node_modules", "**/node_modules/*", "dist"] +} diff --git a/package.json b/package.json index 9fd85002f..cce78bfff 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ { "name": "reactmap", - "version": "1.5.0", + "version": "1.5.1", "description": "React based frontend map.", - "main": "ReactMap.mjs", + "main": "ReactMap.js", "author": "TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com>", "license": "MIT", "private": true, "scripts": { "start": "node .", - "build": "yarn create-locales && node esbuild.config.mjs", + "build": "yarn create-locales && node esbuild.config.js", "server": "yarn create-area && yarn generate && yarn migrate:latest && node server/src/index.js", - "watch": "node esbuild.config.mjs --dev", - "serve": "node esbuild.config.mjs --dev --serve", + "watch": "node esbuild.config.js --dev", + "serve": "node esbuild.config.js --dev --serve", "dev": "nodemon server/src/index.js --watch server", "generate": "node server/scripts/generateMasterfile.js", "gen-env-config": "node server/scripts/genEnvConfig.js", @@ -115,4 +115,4 @@ "suncalc": "^1.8.0", "zustand": "^4.0.0-rc.1" } -} +} \ No newline at end of file diff --git a/server/src/data/defaultRarity.json b/server/src/data/defaultRarity.json index 9fa61caf1..d6b018502 100644 --- a/server/src/data/defaultRarity.json +++ b/server/src/data/defaultRarity.json @@ -1,21 +1,58 @@ { "common": [ - 1,4,7,10,13,16,19,21,23,25,27,29,32,35,37,39,41,43,46,48,50,52,54,56,58,60,63,66,69,72,74,77,79,81,84,86,88,90,92,95,96,98,100,102,104,109,111,116,118,120,123,129,133,138,140,152,155,158,161,163,165,167,170,177,179,183,187,190,191,193,194,198,200,204,207,209,215,216,218,220,223,228,231,252,255,258,261,263,265,270,273,276,278,280,283,285,287,293,296,299,300,304,307,309,311,312,315,316,318,320,322,325,328,331,333,339,341,343,345,347,349,351,353,355,361,363,366,370,387,390,393,396,399,401,412,415,418,420,421,425,427,431,434,436,449,451,453,456,459,495,498,501,504,506,509,517,519,527,529,535,540,543,546,548,551,557,562,568,570,572,577,580,582,585,588,590,592,602,605,613,616,619,624,629,650,653,656,659,661,667 + 1, 4, 7, 10, 13, 16, 19, 21, 23, 25, 27, 29, 32, 35, 37, 39, 41, 43, 46, 48, + 50, 52, 54, 56, 58, 60, 63, 66, 69, 72, 74, 77, 79, 81, 84, 86, 88, 90, 92, + 95, 96, 98, 100, 102, 104, 109, 111, 116, 118, 120, 123, 129, 133, 138, 140, + 152, 155, 158, 161, 163, 165, 167, 170, 177, 179, 183, 187, 190, 191, 193, + 194, 198, 200, 204, 207, 209, 215, 216, 218, 220, 223, 228, 231, 252, 255, + 258, 261, 263, 265, 270, 273, 276, 278, 280, 283, 285, 287, 293, 296, 299, + 300, 304, 307, 309, 311, 312, 315, 316, 318, 320, 322, 325, 328, 331, 333, + 339, 341, 343, 345, 347, 349, 351, 353, 355, 361, 363, 366, 370, 387, 390, + 393, 396, 399, 401, 412, 415, 418, 420, 421, 425, 427, 431, 434, 436, 449, + 451, 453, 456, 459, 495, 498, 501, 504, 506, 509, 517, 519, 527, 529, 535, + 540, 543, 546, 548, 551, 557, 562, 568, 570, 572, 577, 580, 582, 585, 588, + 590, 592, 602, 605, 613, 616, 619, 624, 629, 650, 653, 656, 659, 661, 667 ], "uncommon": [ - 2,5,8,11,14,17,20,22,24,26,30,33,36,38,40,42,44,47,49,51,53,55,57,59,61,64,67,70,73,75,78,80,82,85,87,89,91,93,97,99,101,103,105,110,112,117,119,121,124,125,126,127,139,141,153,156,159,162,164,166,168,171,178,180,184,185,188,195,202,203,205,206,210,211,213,217,219,221,224,226,227,229,234,253,256,259,262,264,266,268,271,274,277,279,281,284,286,288,294,297,301,302,305,308,310,317,319,323,326,329,332,340,342,344,346,348,354,356,362,364,388,391,394,397,400,402,419,426,428,432,435,437,450,452,454,457,460,496,499,502,505,507,510,520,522,524,528,536,541,544,547,549,552,558,563,569,573,578,581,583,586,587,591,595,603,606,614,615,620,651,654,657,660,662,668 + 2, 5, 8, 11, 14, 17, 20, 22, 24, 26, 30, 33, 36, 38, 40, 42, 44, 47, 49, 51, + 53, 55, 57, 59, 61, 64, 67, 70, 73, 75, 78, 80, 82, 85, 87, 89, 91, 93, 97, + 99, 101, 103, 105, 110, 112, 117, 119, 121, 124, 125, 126, 127, 139, 141, + 153, 156, 159, 162, 164, 166, 168, 171, 178, 180, 184, 185, 188, 195, 202, + 203, 205, 206, 210, 211, 213, 217, 219, 221, 224, 226, 227, 229, 234, 253, + 256, 259, 262, 264, 266, 268, 271, 274, 277, 279, 281, 284, 286, 288, 294, + 297, 301, 302, 305, 308, 310, 317, 319, 323, 326, 329, 332, 340, 342, 344, + 346, 348, 354, 356, 362, 364, 388, 391, 394, 397, 400, 402, 419, 426, 428, + 432, 435, 437, 450, 452, 454, 457, 460, 496, 499, 502, 505, 507, 510, 520, + 522, 524, 528, 536, 541, 544, 547, 549, 552, 558, 563, 569, 573, 578, 581, + 583, 586, 587, 591, 595, 603, 606, 614, 615, 620, 651, 654, 657, 660, 662, + 668 ], "rare": [ - 3,6,9,12,15,18,28,31,34,45,62,65,68,71,76,94,106,107,108,113,114,130,131,132,134,135,136,137,142,143,144,154,157,160,169,176,181,189,232,235,237,241,242,254,257,260,267,269,272,275,282,289,295,306,330,358,365,389,392,395,398,408,409,410,411,413,414,497,500,503,508,518,521,523,525,526,530,531,537,542,545,553,554,555,564,565,566,567,571,574,575,576,579,584,589,593,594,596,597,598,604,617,618,621,622,623,625,630,636,637,652,655,658 + 3, 6, 9, 12, 15, 18, 28, 31, 34, 45, 62, 65, 68, 71, 76, 94, 106, 107, 108, + 113, 114, 130, 131, 132, 134, 135, 136, 137, 142, 143, 144, 154, 157, 160, + 169, 176, 181, 189, 232, 235, 237, 241, 242, 254, 257, 260, 267, 269, 272, + 275, 282, 289, 295, 306, 330, 358, 365, 389, 392, 395, 398, 408, 409, 410, + 411, 413, 414, 497, 500, 503, 508, 518, 521, 523, 525, 526, 530, 531, 537, + 542, 545, 553, 554, 555, 564, 565, 566, 567, 571, 574, 575, 576, 579, 584, + 589, 593, 594, 596, 597, 598, 604, 617, 618, 621, 622, 623, 625, 630, 636, + 637, 652, 655, 658 ], "ultraRare": [ - 147,148,149,201,246,247,248,371,372,373,374,375,376,443,444,445,607,608,609,610,611,612,633,634,635,714,715 + 147, 148, 149, 201, 246, 247, 248, 371, 372, 373, 374, 375, 376, 443, 444, + 445, 607, 608, 609, 610, 611, 612, 633, 634, 635, 714, 715 ], "regional": [ - 83,115,122,128,214,222,313,314,324,335,336,337,338,357,369,417,422,423,439,441,455,480,481,482,511,512,513,514,515,516,538,539,550,556,561,626,631,632,707 + 83, 115, 122, 128, 214, 222, 313, 314, 324, 335, 336, 337, 338, 357, 369, + 417, 422, 423, 439, 441, 455, 480, 481, 482, 511, 512, 513, 514, 515, 516, + 538, 539, 550, 556, 561, 626, 631, 632, 707 ], "never": [ - 172,173,174,175,182,186,192,196,197,199,208,212,225,230,233,236,238,239,240,290,291,292,298,303,321,327,334,350,352,359,360,367,368,403,404,405,406,407,416,424,429,430,433,438,439,440,442,446,447,448,458,461,462,463,464,465,466,467,468,469,470,471,472,473,474,475,476,477,478,479,532,533,534,559,560,599,600,601,627,628,677,678 + 172, 173, 174, 175, 182, 186, 192, 196, 197, 199, 208, 212, 225, 230, 233, + 236, 238, 239, 240, 290, 291, 292, 298, 303, 321, 327, 334, 350, 352, 359, + 360, 367, 368, 403, 404, 405, 406, 407, 416, 424, 429, 430, 433, 438, 439, + 440, 442, 446, 447, 448, 458, 461, 462, 463, 464, 465, 466, 467, 468, 469, + 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 532, 533, 534, 559, 560, + 599, 600, 601, 627, 628, 677, 678 ], "event": [] } diff --git a/server/src/index.js b/server/src/index.js index 6bdab66d9..41cbe38e2 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -1,6 +1,5 @@ /* eslint-disable no-console */ /* eslint-disable import/no-dynamic-require */ -/* eslint-disable global-require */ process.title = 'ReactMap' const path = require('path') diff --git a/server/src/routes/api/apiIndex.js b/server/src/routes/api/apiIndex.js index d5a2e8108..5cda2ef13 100644 --- a/server/src/routes/api/apiIndex.js +++ b/server/src/routes/api/apiIndex.js @@ -1,6 +1,5 @@ /* eslint-disable no-console */ /* eslint-disable import/no-dynamic-require */ -/* eslint-disable global-require */ const router = require('express').Router() const fs = require('fs') diff --git a/server/src/routes/authRouter.js b/server/src/routes/authRouter.js index f9f78a474..c46aac2bd 100644 --- a/server/src/routes/authRouter.js +++ b/server/src/routes/authRouter.js @@ -1,4 +1,3 @@ -/* eslint-disable global-require */ /* eslint-disable import/no-dynamic-require */ /* eslint-disable no-console */ const router = require('express').Router() diff --git a/server/src/services/DiscordClient.js b/server/src/services/DiscordClient.js index 94822e012..180034191 100644 --- a/server/src/services/DiscordClient.js +++ b/server/src/services/DiscordClient.js @@ -2,7 +2,6 @@ /* eslint-disable class-methods-use-this */ /* eslint-disable no-await-in-loop */ /* eslint-disable no-restricted-syntax */ -/* eslint-disable global-require */ /* eslint-disable import/no-dynamic-require */ /* global BigInt */ const fs = require('fs') diff --git a/server/src/services/config.js b/server/src/services/config.js index df563eecc..ab4ec629a 100644 --- a/server/src/services/config.js +++ b/server/src/services/config.js @@ -1,5 +1,4 @@ /* eslint-disable import/no-dynamic-require */ -/* eslint-disable global-require */ /* eslint-disable no-console */ process.env.NODE_CONFIG_DIR = `${__dirname}/../configs` diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 46d9ad7a1..281782510 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -83,7 +83,7 @@ export default function Map({ const [lc] = useState( L.control.locate({ position: 'bottomright', - icon: 'fas fa-location-arrow', + icon: 'fas fa-crosshairs', keepCurrentZoomLevel: true, setView: 'untilPan', }), diff --git a/src/components/layout/dialogs/tutorial/data.json b/src/components/layout/dialogs/tutorial/data.json index 32130fb1a..02fc2eef6 100644 --- a/src/components/layout/dialogs/tutorial/data.json +++ b/src/components/layout/dialogs/tutorial/data.json @@ -93,121 +93,46 @@ "xlKarp": true, "xsRat": true, "ivOr": { - "great": [ - 1, - 100 - ], - "ultra": [ - 1, - 100 - ], - "iv": [ - 0, - 100 - ], - "level": [ - 1, - 35 - ] + "great": [1, 100], + "ultra": [1, 100], + "iv": [0, 100], + "level": [1, 35] }, "standard": { "enabled": false, "size": "md", - "iv": [ - 0, - 100 - ], - "atk_iv": [ - 0, - 15 - ], - "def_iv": [ - 0, - 15 - ], - "sta_iv": [ - 0, - 15 - ], - "level": [ - 1, - 35 - ], - "great": [ - 1, - 100 - ], - "ultra": [ - 1, - 100 - ] + "iv": [0, 100], + "atk_iv": [0, 15], + "def_iv": [0, 15], + "sta_iv": [0, 15], + "level": [1, 35], + "great": [1, 100], + "ultra": [1, 100] }, "filter": { "1-163": { "enabled": false, "size": "md", "adv": "", - "iv": [ - 0, - 100 - ], - "atk_iv": [ - 0, - 15 - ], - "def_iv": [ - 0, - 15 - ], - "sta_iv": [ - 0, - 15 - ], - "level": [ - 1, - 35 - ], - "great": [ - 1, - 100 - ], - "ultra": [ - 1, - 100 - ] + "iv": [0, 100], + "atk_iv": [0, 15], + "def_iv": [0, 15], + "sta_iv": [0, 15], + "level": [1, 35], + "great": [1, 100], + "ultra": [1, 100] }, "1-897": { "enabled": false, "size": "md", "adv": "", - "iv": [ - 0, - 100 - ], - "atk_iv": [ - 0, - 15 - ], - "def_iv": [ - 0, - 15 - ], - "sta_iv": [ - 0, - 15 - ], - "level": [ - 1, - 35 - ], - "great": [ - 1, - 100 - ], - "ultra": [ - 1, - 100 - ] + "iv": [0, 100], + "atk_iv": [0, 15], + "def_iv": [0, 15], + "sta_iv": [0, 15], + "level": [1, 35], + "great": [1, 100], + "ultra": [1, 100] } } }, @@ -347,4 +272,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/components/layout/dialogs/webhooks/Location.jsx b/src/components/layout/dialogs/webhooks/Location.jsx index 2a2960d40..639a679e9 100644 --- a/src/components/layout/dialogs/webhooks/Location.jsx +++ b/src/components/layout/dialogs/webhooks/Location.jsx @@ -7,7 +7,7 @@ import { CircularProgress, } from '@material-ui/core' import { Autocomplete } from '@material-ui/lab' -import { LocationOn } from '@material-ui/icons' +import { LocationOn, MyLocation } from '@material-ui/icons' import { useLazyQuery } from '@apollo/client' import { useMapEvents } from 'react-leaflet' @@ -93,7 +93,7 @@ const Location = ({ variant="contained" color="secondary" onClick={() => lc._onClick()} - startIcon={} + startIcon={} > {t('my_location')} diff --git a/src/services/queries/user.js b/src/services/queries/user.js index c31cd43c7..89a40e255 100644 --- a/src/services/queries/user.js +++ b/src/services/queries/user.js @@ -28,4 +28,4 @@ export const setExtraFields = gql` mutation SetExtraFields($key: String, $value: String) { setExtraFields(key: $key, value: $value) } -` \ No newline at end of file +` From bcaaa2c647c075cb5c2bb49e63bf253f0a743e90 Mon Sep 17 00:00:00 2001 From: TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com> Date: Fri, 7 Oct 2022 19:56:26 -0400 Subject: [PATCH 04/40] Fix logout issue --- server/src/routes/rootRouter.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/routes/rootRouter.js b/server/src/routes/rootRouter.js index 6b474ab7f..91a86b68e 100644 --- a/server/src/routes/rootRouter.js +++ b/server/src/routes/rootRouter.js @@ -20,7 +20,9 @@ rootRouter.use('/auth', authRouter) rootRouter.use('/api', apiRouter) rootRouter.get('/logout', (req, res) => { - req.logout() + req.logout((err) => { + if (err) console.error('[AUTH] Unable to logout', err) + }) req.session.destroy() res.redirect('/') }) From d11a767383bf43e222d36b4e26997ac9f8171cc1 Mon Sep 17 00:00:00 2001 From: TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com> Date: Fri, 7 Oct 2022 22:14:59 -0400 Subject: [PATCH 05/40] scanNext fixes - Move scan dialog into its own component - Move constants out of ScanNextTarget fn - Wrap a lot of state changes in useEffects to control renders --- .../layout/dialogs/scanner/ScanDialog.jsx | 40 +++++++ .../layout/dialogs/scanner/ScanNext.jsx | 102 ++++++++---------- .../layout/dialogs/scanner/ScanNextTarget.jsx | 55 +++++----- 3 files changed, 113 insertions(+), 84 deletions(-) create mode 100644 src/components/layout/dialogs/scanner/ScanDialog.jsx diff --git a/src/components/layout/dialogs/scanner/ScanDialog.jsx b/src/components/layout/dialogs/scanner/ScanDialog.jsx new file mode 100644 index 000000000..e99d9ed99 --- /dev/null +++ b/src/components/layout/dialogs/scanner/ScanDialog.jsx @@ -0,0 +1,40 @@ +import * as React from 'react' +import { Dialog, DialogContent, Grid, Typography } from '@material-ui/core' +import { useTranslation } from 'react-i18next' +import Header from '@components/layout/general/Header' +import Footer from '@components/layout/general/Footer' + +export default function ScanDialog({ scanNextMode, setScanNextMode }) { + const { t } = useTranslation() + + return ( + setScanNextMode(false)} + open={['confirmed', 'loading', 'error'].includes(scanNextMode)} + maxWidth="xs" + > +
setScanNextMode(false)} + /> + + + + {t(`scan_${scanNextMode}`)} + + + +
setScanNextMode(false), + }, + ]} + /> +
+ ) +} diff --git a/src/components/layout/dialogs/scanner/ScanNext.jsx b/src/components/layout/dialogs/scanner/ScanNext.jsx index 51d92cc0a..9d974f57d 100644 --- a/src/components/layout/dialogs/scanner/ScanNext.jsx +++ b/src/components/layout/dialogs/scanner/ScanNext.jsx @@ -1,19 +1,9 @@ import React, { useEffect, useState } from 'react' import { useQuery, useLazyQuery } from '@apollo/client' -import { useTranslation } from 'react-i18next' -import { - Dialog, - DialogContent, - DialogTitle, - DialogActions, - Button, - Grid, - Typography, -} from '@material-ui/core' - import { useStatic, useStore } from '@hooks/useStore' import Query from '@services/Query' import ScanNextTarget from './ScanNextTarget' +import ScanDialog from './ScanDialog' export default function ScanNext({ map, @@ -26,16 +16,16 @@ export default function ScanNext({ scanNextAreaRestriction, }, }) { - const { data: scanAreas } = scanNextAreaRestriction?.length - ? useQuery(Query.scanAreas()) - : { data: null } const { loggedIn } = useStatic((state) => state.auth) - const { t } = useTranslation() + const location = useStore((s) => s.location) + const [queue, setQueue] = useState('init') const [scanNextLocation, setScanNextLocation] = useState(location) const [scanNextCoords, setScanNextCoords] = useState([location]) const [scanNextType, setScanNextType] = useState('S') + + const { data: scanAreas } = useQuery(Query.scanAreas()) const [scanNext, { error: scannerError, data: scannerResponse }] = useLazyQuery(Query.scanner(), { variables: { @@ -68,39 +58,48 @@ export default function ScanNext({ }, ) - if (scanNextMode === 'sendCoords') { - scanNext() - setScanNextMode('loading') - } - if (scannerError) { - setScanNextMode('error') - } - if (scannerResponse) { - if (scannerResponse.scanner?.status === 'ok') { - setScanNextMode('confirmed') - } else { - setScanNextMode('error') + useEffect(() => { + if (scanNextMode === 'sendCoords') { + scanNext() + setScanNextMode('loading') } - } + }, [scanNextMode]) - if (scanNextShowScanQueue) { - if (queue === 'init') { - getQueue() - setQueue('...') + useEffect(() => { + if (scannerError) { + setScanNextMode('error') } - useEffect(() => { - const timer = setInterval(() => { + if (scannerResponse) { + if (scannerResponse?.scanner?.status === 'ok') { + setScanNextMode('confirmed') + } else { + setScanNextMode('error') + } + } + }, [scannerError, !!scannerResponse]) + + useEffect(() => { + let timer + if (scanNextShowScanQueue) { + if (queue === 'init') { + getQueue() + setQueue('...') + } + timer = setInterval(() => { if (scanNextMode === 'setLocation') { getQueue() } }, 2000) - return () => clearInterval(timer) - }) - } - if (scannerQueueResponse && scannerQueueResponse.scanner?.status === 'ok') { - setQueue(scannerQueueResponse.scanner.message) - scannerQueueResponse.scanner = {} - } + } + return () => timer ? clearInterval(timer) : null + }) + + useEffect(() => { + if (scannerQueueResponse?.scanner?.status === 'ok') { + setQueue(scannerQueueResponse.scanner.message) + scannerQueueResponse.scanner = {} + } + }, [!!scannerQueueResponse?.scanner]) return ( <> @@ -122,23 +121,10 @@ export default function ScanNext({ scanAreas={scanAreas ? scanAreas.scanAreas[0]?.features : null} /> )} - setScanNextMode(false)} - open={['confirmed', 'loading', 'error'].includes(scanNextMode)} - maxWidth="xs" - > - {t(`scan_${scanNextMode}_title`)} - - - - {t(`scan_${scanNextMode}`)} - - - - - - - + ) } diff --git a/src/components/layout/dialogs/scanner/ScanNextTarget.jsx b/src/components/layout/dialogs/scanner/ScanNextTarget.jsx index cbcf04a69..0dc4512c1 100644 --- a/src/components/layout/dialogs/scanner/ScanNextTarget.jsx +++ b/src/components/layout/dialogs/scanner/ScanNextTarget.jsx @@ -6,6 +6,31 @@ import booleanPointInPolygon from '@turf/boolean-point-in-polygon' import { Circle, Marker, Popup } from 'react-leaflet' import { useTranslation } from 'react-i18next' +const RADIUS_POKEMON = 70 +const RADIUS_GYM = 750 +const DISTANCE = { + M: RADIUS_POKEMON * 1.732, + XL: RADIUS_GYM * 1.732, +} + +const calcScanNextCoords = (center, type) => { + const coords = [center] + if (type === 'S') return coords + const start = point([center[1], center[0]]) + const options = { units: 'kilometers' } + return coords.concat( + [0, 60, 120, 180, 240, 300].map((bearing) => { + const [lon, lat] = destination( + start, + DISTANCE[type] / 1000, + bearing, + options, + ).geometry.coordinates + return [lat, lon] + }), + ) +} + export default function ScanNextTarget({ map, scannerType, @@ -23,29 +48,7 @@ export default function ScanNextTarget({ scanAreas, }) { const [position, setPosition] = useState(scanNextLocation) - const radiusPokemon = 70 - const radiusGym = 750 - const calcScanNextCoords = (center, type) => { - const coords = [center] - if (type === 'S') return coords - const start = point([center[1], center[0]]) - const distance = { - M: radiusPokemon * 1.732, - XL: radiusGym * 1.732, - } - const options = { units: 'kilometers' } - return coords.concat( - [0, 60, 120, 180, 240, 300].map((bearing) => { - const [lon, lat] = destination( - start, - distance[type] / 1000, - bearing, - options, - ).geometry.coordinates - return [lat, lon] - }), - ) - } + const checkAreaValidity = (center) => { if (!scanNextAreaRestriction?.length || !scanAreas?.length) return true let isValid = false @@ -188,7 +191,7 @@ export default function ScanNextTarget({ {scanNextCoords.map((coords) => ( ( Date: Fri, 7 Oct 2022 22:32:19 -0400 Subject: [PATCH 06/40] dry up a lot of code - Consolidate repeat functions - Consolidate repeat dialog --- .../layout/dialogs/scanner/ScanDialog.jsx | 14 +- .../layout/dialogs/scanner/ScanNext.jsx | 7 +- .../layout/dialogs/scanner/ScanNextTarget.jsx | 34 +---- .../layout/dialogs/scanner/ScanZone.jsx | 38 +----- .../layout/dialogs/scanner/ScanZoneTarget.jsx | 129 ++++++++---------- src/services/Utility.js | 5 + src/services/functions/checkAreaValidity.js | 26 ++++ 7 files changed, 113 insertions(+), 140 deletions(-) create mode 100644 src/services/functions/checkAreaValidity.js diff --git a/src/components/layout/dialogs/scanner/ScanDialog.jsx b/src/components/layout/dialogs/scanner/ScanDialog.jsx index e99d9ed99..7b866f7ab 100644 --- a/src/components/layout/dialogs/scanner/ScanDialog.jsx +++ b/src/components/layout/dialogs/scanner/ScanDialog.jsx @@ -4,23 +4,23 @@ import { useTranslation } from 'react-i18next' import Header from '@components/layout/general/Header' import Footer from '@components/layout/general/Footer' -export default function ScanDialog({ scanNextMode, setScanNextMode }) { +export default function ScanDialog({ scanMode, setScanMode }) { const { t } = useTranslation() return ( setScanNextMode(false)} - open={['confirmed', 'loading', 'error'].includes(scanNextMode)} + onClose={() => setScanMode(false)} + open={['confirmed', 'loading', 'error'].includes(scanMode)} maxWidth="xs" >
setScanNextMode(false)} + titles={[`scan_${scanMode}_title`]} + action={() => setScanMode(false)} /> - {t(`scan_${scanNextMode}`)} + {t(`scan_${scanMode}`)} @@ -31,7 +31,7 @@ export default function ScanDialog({ scanNextMode, setScanNextMode }) { icon: 'Clear', color: 'primary', align: 'right', - action: () => setScanNextMode(false), + action: () => setScanMode(false), }, ]} /> diff --git a/src/components/layout/dialogs/scanner/ScanNext.jsx b/src/components/layout/dialogs/scanner/ScanNext.jsx index 9d974f57d..ea3f50a8a 100644 --- a/src/components/layout/dialogs/scanner/ScanNext.jsx +++ b/src/components/layout/dialogs/scanner/ScanNext.jsx @@ -91,7 +91,7 @@ export default function ScanNext({ } }, 2000) } - return () => timer ? clearInterval(timer) : null + return () => (timer ? clearInterval(timer) : null) }) useEffect(() => { @@ -121,10 +121,7 @@ export default function ScanNext({ scanAreas={scanAreas ? scanAreas.scanAreas[0]?.features : null} /> )} - + ) } diff --git a/src/components/layout/dialogs/scanner/ScanNextTarget.jsx b/src/components/layout/dialogs/scanner/ScanNextTarget.jsx index 0dc4512c1..159e1dbb3 100644 --- a/src/components/layout/dialogs/scanner/ScanNextTarget.jsx +++ b/src/components/layout/dialogs/scanner/ScanNextTarget.jsx @@ -1,10 +1,10 @@ import React, { useState, useRef, useMemo, useEffect } from 'react' import { Grid, Button, ButtonGroup, Typography } from '@material-ui/core' -import { point, polygon } from '@turf/helpers' +import { point } from '@turf/helpers' import destination from '@turf/destination' -import booleanPointInPolygon from '@turf/boolean-point-in-polygon' import { Circle, Marker, Popup } from 'react-leaflet' import { useTranslation } from 'react-i18next' +import Utility from '@services/Utility' const RADIUS_POKEMON = 70 const RADIUS_GYM = 750 @@ -49,30 +49,6 @@ export default function ScanNextTarget({ }) { const [position, setPosition] = useState(scanNextLocation) - const checkAreaValidity = (center) => { - if (!scanNextAreaRestriction?.length || !scanAreas?.length) return true - let isValid = false - if (scanNextAreaRestriction?.length && scanAreas?.length) { - const testPoint = point([center[1], center[0]]) - scanNextAreaRestriction.map((area) => { - if ( - scanAreas.some( - (scanArea) => - scanArea.properties.name === area && - booleanPointInPolygon( - testPoint, - polygon(scanArea.geometry.coordinates), - ), - ) - ) { - isValid = true - } - return true - }) - } - return isValid - } - const { t } = useTranslation() const scanMarkerRef = useRef(null) const scanPopupRef = useRef(null) @@ -103,7 +79,11 @@ export default function ScanNextTarget({ } }, []) - const isInAllowedArea = checkAreaValidity(position) + const isInAllowedArea = Utility.checkAreaValidity( + position, + scanNextAreaRestriction, + scanAreas, + ) return ( <> diff --git a/src/components/layout/dialogs/scanner/ScanZone.jsx b/src/components/layout/dialogs/scanner/ScanZone.jsx index 1fec1acef..dadedcb80 100644 --- a/src/components/layout/dialogs/scanner/ScanZone.jsx +++ b/src/components/layout/dialogs/scanner/ScanZone.jsx @@ -1,19 +1,9 @@ import React, { useEffect, useState } from 'react' import { useQuery, useLazyQuery } from '@apollo/client' -import { useTranslation } from 'react-i18next' -import { - Dialog, - DialogContent, - DialogTitle, - DialogActions, - Button, - Grid, - Typography, -} from '@material-ui/core' - import { useStatic, useStore } from '@hooks/useStore' import Query from '@services/Query' import ScanZoneTarget from './ScanZoneTarget' +import ScanDialog from './ScanDialog' export default function ScanZone({ map, @@ -31,16 +21,16 @@ export default function ScanZone({ scanZoneAreaRestriction, }, }) { - const { data: scanAreas } = scanZoneAreaRestriction?.length - ? useQuery(Query.scanAreas()) - : { data: null } const { loggedIn } = useStatic((state) => state.auth) - const { t } = useTranslation() + const location = useStore((s) => s.location) + const [queue, setQueue] = useState('init') const [scanZoneLocation, setScanZoneLocation] = useState(location) const [scanZoneCoords, setScanZoneCoords] = useState([location]) const [scanZoneSize, setScanZoneSize] = useState(1) + + const { data: scanAreas } = useQuery(Query.scanAreas()) const [scanZone, { error: scannerError, data: scannerResponse }] = useLazyQuery(Query.scanner(), { variables: { @@ -132,23 +122,7 @@ export default function ScanZone({ scanAreas={scanAreas ? scanAreas.scanAreas[0]?.features : null} /> )} - setScanZoneMode(false)} - open={['confirmed', 'loading', 'error'].includes(scanZoneMode)} - maxWidth="xs" - > - {t(`scan_${scanZoneMode}_title`)} - - - - {t(`scan_${scanZoneMode}`)} - - - - - - - + ) } diff --git a/src/components/layout/dialogs/scanner/ScanZoneTarget.jsx b/src/components/layout/dialogs/scanner/ScanZoneTarget.jsx index 272d94ca4..7df4de954 100644 --- a/src/components/layout/dialogs/scanner/ScanZoneTarget.jsx +++ b/src/components/layout/dialogs/scanner/ScanZoneTarget.jsx @@ -1,11 +1,46 @@ import React, { useState, useRef, useMemo, useEffect } from 'react' import { Grid, Button, Box, Slider, Typography } from '@material-ui/core' -import { point, polygon } from '@turf/helpers' +import { point } from '@turf/helpers' import destination from '@turf/destination' -import booleanPointInPolygon from '@turf/boolean-point-in-polygon' import { Circle, Marker, Popup } from 'react-leaflet' import { useTranslation } from 'react-i18next' import AdvancedAccordion from '@components/layout/custom/AdvancedAccordion' +import Utility from '@services/Utility' + +const calcScanZoneCoords = (center, radius, spacing, scanZoneSize) => { + let coords = [center] + let currentPoint = point([center[1], center[0]]) + const distance = radius * 2 * Math.cos(30 * (Math.PI / 180)) + const bearings = { + 1: 30, + 2: 90, + 3: 150, + 4: 210, + 5: 270, + 6: 330, + } + for (let i = 1; i < scanZoneSize + 1; i += 1) { + let quadrant = 1 + let step = 1 + while (step < 6 * i + 1) { + currentPoint = destination( + currentPoint, + (distance * spacing) / 1000, + step === 1 ? 330 : bearings[quadrant], + { units: 'kilometers' }, + ) + coords = coords.concat([ + [ + currentPoint.geometry.coordinates[1], + currentPoint.geometry.coordinates[0], + ], + ]) + quadrant = Math.floor(step / i) + 1 + step += 1 + } + } + return coords +} export default function ScanZoneTarget({ map, @@ -31,64 +66,6 @@ export default function ScanZoneTarget({ const [position, setPosition] = useState(scanZoneLocation) const [spacing, setSpacing] = useState(scanZoneSpacing) const [radius, setRadius] = useState(scanZoneRadius.pokemon) - const calcScanZoneCoords = (center) => { - let coords = [center] - let currentPoint = point([center[1], center[0]]) - const distance = radius * 2 * Math.cos(30 * (Math.PI / 180)) - const bearings = { - 1: 30, - 2: 90, - 3: 150, - 4: 210, - 5: 270, - 6: 330, - } - for (let i = 1; i < scanZoneSize + 1; i += 1) { - let quadrant = 1 - let step = 1 - while (step < 6 * i + 1) { - currentPoint = destination( - currentPoint, - (distance * spacing) / 1000, - step === 1 ? 330 : bearings[quadrant], - { units: 'kilometers' }, - ) - coords = coords.concat([ - [ - currentPoint.geometry.coordinates[1], - currentPoint.geometry.coordinates[0], - ], - ]) - quadrant = Math.floor(step / i) + 1 - step += 1 - } - } - return coords - } - - const checkAreaValidity = (center) => { - if (!scanZoneAreaRestriction?.length || !scanAreas?.length) return true - let isValid = false - if (scanZoneAreaRestriction?.length && scanAreas?.length) { - const testPoint = point([center[1], center[0]]) - scanZoneAreaRestriction.map((area) => { - if ( - scanAreas.some( - (scanArea) => - scanArea.properties.name === area && - booleanPointInPolygon( - testPoint, - polygon(scanArea.geometry.coordinates), - ), - ) - ) { - isValid = true - } - return true - }) - } - return isValid - } const { t } = useTranslation() const scanMarkerRef = useRef(null) @@ -102,7 +79,9 @@ export default function ScanZoneTarget({ map.flyTo([lat, lng]) setPosition([lat, lng]) setScanZoneLocation([lat, lng]) - setScanZoneCoords(calcScanZoneCoords([lat, lng])) + setScanZoneCoords( + calcScanZoneCoords([lat, lng], radius, spacing, scanZoneSize), + ) const popup = scanPopupRef.current if (popup) { popup.openOn(map) @@ -120,19 +99,25 @@ export default function ScanZoneTarget({ } }, []) - const handleSizeChange = (event, newSize) => { + const handleSizeChange = (_event, newSize) => { setScanZoneSize(newSize) - setScanZoneCoords(calcScanZoneCoords(position)) + setScanZoneCoords( + calcScanZoneCoords(position, radius, spacing, scanZoneSize), + ) } - const handleSpacingChange = (event, newSpacing) => { + const handleSpacingChange = (_event, newSpacing) => { setSpacing(newSpacing) - setScanZoneCoords(calcScanZoneCoords(position)) + setScanZoneCoords( + calcScanZoneCoords(position, radius, spacing, scanZoneSize), + ) } - const handleRadiusChange = (event, newRadius) => { + const handleRadiusChange = (_event, newRadius) => { setRadius(newRadius) - setScanZoneCoords(calcScanZoneCoords(position)) + setScanZoneCoords( + calcScanZoneCoords(position, radius, spacing, scanZoneSize), + ) } const rangeMarks = [ @@ -140,10 +125,16 @@ export default function ScanZoneTarget({ { value: scanZoneRadius.gym, label: t('gym') }, ] - const isInAllowedArea = checkAreaValidity(position) + const isInAllowedArea = Utility.checkAreaValidity( + position, + scanZoneAreaRestriction, + scanAreas, + ) if (scanZoneCoords.length === 1) { - setScanZoneCoords(calcScanZoneCoords(scanZoneLocation)) + setScanZoneCoords( + calcScanZoneCoords(scanZoneLocation, radius, spacing, scanZoneSize), + ) } const advancedMenu = ( diff --git a/src/services/Utility.js b/src/services/Utility.js index 9c053e564..58bece40a 100644 --- a/src/services/Utility.js +++ b/src/services/Utility.js @@ -9,6 +9,7 @@ import dayCheck from './functions/dayCheck' import parseQuestConditions from './functions/parseConditions' import formatter from './functions/formatter' import getRewardInfo from './functions/getRewardInfo' +import checkAreaValidity from './functions/checkAreaValidity' export default class Utility { static getProperName(word) { @@ -148,4 +149,8 @@ export default class Utility { ? content[localStorage.getItem('i18nextLng')] || Object.values(content)[0] : '' } + + static checkAreaValidity(...args) { + return checkAreaValidity(...args) + } } diff --git a/src/services/functions/checkAreaValidity.js b/src/services/functions/checkAreaValidity.js new file mode 100644 index 000000000..86cc38ce7 --- /dev/null +++ b/src/services/functions/checkAreaValidity.js @@ -0,0 +1,26 @@ +import { point, polygon } from '@turf/helpers' +import booleanPointInPolygon from '@turf/boolean-point-in-polygon' + +export default function checkAreaValidity(center, areaRestrictions, scanAreas) { + if (!areaRestrictions?.length || !scanAreas?.length) return true + let isValid = false + if (areaRestrictions?.length && scanAreas?.length) { + const testPoint = point([center[1], center[0]]) + areaRestrictions.map((area) => { + if ( + scanAreas.some( + (scanArea) => + scanArea.properties.name === area && + booleanPointInPolygon( + testPoint, + polygon(scanArea.geometry.coordinates), + ), + ) + ) { + isValid = true + } + return true + }) + } + return isValid +} From cdc783ada49d868c5767bdb947e27fb168eb027c Mon Sep 17 00:00:00 2001 From: TurtIeSocks <58572875+TurtIeSocks@users.noreply.github.com> Date: Fri, 7 Oct 2022 23:00:37 -0400 Subject: [PATCH 07/40] Consolidate ScanNext & ScanZone components - Consolidate ScanNext and ScanZone components into one reusable component - Cleanup backend code to use more general variables - --- server/src/services/api/scannerApi.js | 16 +- src/components/Map.jsx | 18 +- .../layout/dialogs/scanner/ScanNext.jsx | 127 ------------- .../layout/dialogs/scanner/ScanOnDemand.jsx | 167 ++++++++++++++++++ .../layout/dialogs/scanner/ScanZone.jsx | 128 -------------- 5 files changed, 185 insertions(+), 271 deletions(-) delete mode 100644 src/components/layout/dialogs/scanner/ScanNext.jsx create mode 100644 src/components/layout/dialogs/scanner/ScanOnDemand.jsx delete mode 100644 src/components/layout/dialogs/scanner/ScanZone.jsx diff --git a/server/src/services/api/scannerApi.js b/server/src/services/api/scannerApi.js index dce481ab8..56d0943ab 100644 --- a/server/src/services/api/scannerApi.js +++ b/server/src/services/api/scannerApi.js @@ -37,17 +37,17 @@ module.exports = async function scannerApi(category, method, data = null) { console.log( `[scannerApi] Request to scan new location by ${data.username}${ data.userId ? ` (${data.userId})` : '' - } - type ${data.scanNextType}: ${data.scanNextLocation[0].toFixed( + } - type ${data.scanNextType}: ${data.scanLocation[0].toFixed( 5, - )},${data.scanNextLocation[1].toFixed(5)}`, + )},${data.scanLocation[1].toFixed(5)}`, ) const coords = config.scanner.backendConfig.platform === 'mad' ? `${parseFloat( - data.scanNextCoords[0][0].toFixed(5), - )},${parseFloat(data.scanNextCoords[0][1].toFixed(5))}` + data.scanCoords[0][0].toFixed(5), + )},${parseFloat(data.scanCoords[0][1].toFixed(5))}` : JSON.stringify( - data.scanNextCoords.map((coord) => ({ + data.scanCoords.map((coord) => ({ lat: parseFloat(coord[0].toFixed(5)), lon: parseFloat(coord[1].toFixed(5)), })), @@ -76,12 +76,12 @@ module.exports = async function scannerApi(category, method, data = null) { console.log( `[scannerApi] Request to scan new zone by ${data.username}${ data.userId ? ` (${data.userId})` : '' - } - size ${data.scanZoneSize}: ${data.scanZoneLocation[0].toFixed( + } - size ${data.scanZoneSize}: ${data.scanLocation[0].toFixed( 5, - )},${data.scanZoneLocation[1].toFixed(5)}`, + )},${data.scanLocation[1].toFixed(5)}`, ) const coords = JSON.stringify( - data.scanZoneCoords.map((coord) => ({ + data.scanCoords.map((coord) => ({ lat: parseFloat(coord[0].toFixed(5)), lon: parseFloat(coord[1].toFixed(5)), })), diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 46d9ad7a1..997a07748 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -10,8 +10,7 @@ import { useStatic, useStore } from '@hooks/useStore' import Nav from './layout/Nav' import QueryData from './QueryData' import Webhook from './layout/dialogs/webhooks/Webhook' -import ScanNext from './layout/dialogs/scanner/ScanNext' -import ScanZone from './layout/dialogs/scanner/ScanZone' +import ScanOnDemand from './layout/dialogs/scanner/ScanOnDemand' import ClientError from './layout/dialogs/ClientError' const userSettingsCategory = (category) => { @@ -275,20 +274,23 @@ export default function Map({ ) )} {scanNextMode && ( - )} {scanZoneMode && ( - )}