From 5e98e35e9202f3d904e5843592fb1f911e811da8 Mon Sep 17 00:00:00 2001 From: Derick M <58572875+TurtIeSocks@users.noreply.github.com> Date: Tue, 27 Aug 2024 11:00:49 -0400 Subject: [PATCH 01/94] refactor: more client type refining --- .gitignore | 3 + package.json | 4 +- packages/masterfile/lib/index.d.ts | 4 +- packages/types/lib/augmentations.d.ts | 13 ---- packages/types/lib/client.d.ts | 72 +++++++++++++++++- packages/types/lib/config.d.ts | 15 +++- packages/types/lib/models.d.ts | 1 + packages/types/lib/utility.d.ts | 76 +++++-------------- packages/vite-plugins/lib/favicon.js | 24 ++++-- server/src/utils/getServerSettings.js | 11 +-- src/assets/constants.js | 2 + src/assets/fallbackMarker.js | 5 +- src/assets/theme.js | 1 + src/components/Config.jsx | 19 +++-- src/components/I.jsx | 4 + src/components/Menu.jsx | 4 +- src/components/StatusIcon.jsx | 3 +- src/components/auth/Discord.jsx | 3 + src/components/auth/Local.jsx | 6 +- src/components/dialogs/UserOptions.jsx | 3 + src/components/inputs/BasicListButton.jsx | 3 +- src/components/inputs/FCSelect.jsx | 66 ++++++++-------- src/components/inputs/GenericSearch.jsx | 6 +- src/components/inputs/LocaleSelection.jsx | 6 +- src/components/virtual/ColoredTile.jsx | 2 + src/components/virtual/Table.jsx | 39 +++++----- src/components/virtual/VirtualGrid.jsx | 4 +- .../builder/components/CustomButton.jsx | 3 + src/features/builder/components/Generator.jsx | 1 + src/features/builder/index.js | 2 + src/features/device/deviceMarker.js | 7 ++ src/features/device/index.js | 2 + src/features/drawer/S2Cells.jsx | 22 +++--- src/features/drawer/components/Actions.jsx | 67 +++++++++------- src/features/drawer/nests/ActiveNests.jsx | 2 + src/features/drawer/pokemon/ModeSelector.jsx | 6 +- src/features/drawer/settings/General.jsx | 5 ++ src/features/gym/index.js | 2 + src/features/gym/useWebhook.js | 6 ++ src/features/nest/index.js | 2 + src/features/pokemon/index.js | 2 + src/features/pokemon/pokemonMarker.js | 14 ++++ src/features/pokestop/index.js | 2 + src/features/portal/index.js | 2 + src/features/profile/index.jsx | 2 + src/features/route/index.js | 2 + src/features/s2cell/index.js | 2 + src/features/scanArea/index.js | 2 + src/features/scanCell/index.js | 2 + src/features/scanner/hooks/store.js | 2 + src/features/scanner/index.js | 2 + src/features/spawnpoint/index.js | 2 + src/features/tutorial/Advanced.jsx | 5 ++ src/features/tutorial/Popups.jsx | 5 ++ src/features/tutorial/Sidebar.jsx | 2 + src/features/tutorial/Sliders.jsx | 3 + src/features/tutorial/Welcome.jsx | 3 + src/features/tutorial/data.js | 6 +- src/features/tutorial/index.jsx | 4 +- src/features/wayfarer/index.js | 2 + src/features/weather/index.js | 2 + src/features/weather/weatherMarker.js | 6 ++ src/features/webhooks/WebhookAdv.jsx | 18 +++-- src/features/webhooks/human/status/index.jsx | 2 + src/features/webhooks/index.js | 2 + src/features/webhooks/tiles/TrackedTile.jsx | 8 +- src/hooks/useFilter.js | 10 ++- src/hooks/useHideElement.js | 2 + src/hooks/useLocation.js | 26 ++++++- src/pages/data/components/Notification.jsx | 17 +++-- src/pages/locales/components/AllSwitch.jsx | 2 + src/pages/map/components/Effects.jsx | 12 ++- src/pages/map/hooks/usePermCheck.js | 6 ++ src/pages/map/tileObject.js | 2 + src/services/Assets.js | 5 +- src/services/fetches.js | 16 +++- src/services/queries/available.js | 2 + src/services/queries/config.js | 2 + src/services/queries/device.js | 2 + src/services/queries/geocoder.js | 2 + src/services/queries/gym.js | 2 + src/services/queries/nest.js | 2 + src/services/queries/pokemon.js | 2 + src/services/queries/pokestop.js | 2 + src/services/queries/portal.js | 2 + src/services/queries/route.js | 2 + src/services/queries/s2cell.js | 2 + src/services/queries/scanAreas.js | 2 + src/services/queries/scanCell.js | 2 + src/services/queries/scanner.js | 2 + src/services/queries/search.js | 2 + src/services/queries/spawnpoint.js | 2 + src/services/queries/submissionCells.js | 2 + src/services/queries/user.js | 2 + src/services/queries/weather.js | 2 + src/services/queries/webhook.js | 2 + src/store/useMapStore.js | 2 + src/store/useMemory.js | 38 ++++++---- src/store/useStorage.js | 61 ++++++++------- src/utils/analytics.js | 2 + src/utils/applyToAll.js | 2 +- vite.config.js | 4 + yarn.lock | 16 ++-- 103 files changed, 616 insertions(+), 277 deletions(-) diff --git a/.gitignore b/.gitignore index 727e4d5a7..dbbc19e20 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,6 @@ server/src/models/queries/* # Cache server/.cache/* !/server/.cache/.gitkeep + +# Misc +ts-check.js diff --git a/package.json b/package.json index 83bab35aa..d669a4807 100644 --- a/package.json +++ b/package.json @@ -172,12 +172,12 @@ "react-i18next": "15.0.1", "react-leaflet": "4.2.1", "react-router-dom": "^6.15.0", - "react-virtuoso": "^4.6.2", + "react-virtuoso": "4.10.1", "rtree": "^1.4.2", "source-map": "^0.7.4", "suncalc": "^1.9.0", "supercluster": "^8.0.1", - "uicons.js": "^1.1.4", + "uicons.js": "1.2.0", "zustand": "4.4.6" }, "devDependencies": { diff --git a/packages/masterfile/lib/index.d.ts b/packages/masterfile/lib/index.d.ts index 842591da6..6614cabb1 100644 --- a/packages/masterfile/lib/index.d.ts +++ b/packages/masterfile/lib/index.d.ts @@ -76,8 +76,8 @@ export interface Masterfile { moves: Record invasions: Record weather: Record - teams: mfjson['teams'] - raids: mfjson['raids'] + teams: (typeof mfjson)['teams'] + raids: (typeof mfjson)['raids'] } export declare function generate( diff --git a/packages/types/lib/augmentations.d.ts b/packages/types/lib/augmentations.d.ts index f775d0f40..120cef498 100644 --- a/packages/types/lib/augmentations.d.ts +++ b/packages/types/lib/augmentations.d.ts @@ -69,16 +69,3 @@ declare module 'http' { bodySize?: number } } - -// declare module '@apollo/server' { -// interface GraphQLInProgressResponse { -// __sentry_transaction?: string -// } -// } - -// TODO -// declare module '@mui/material/Button' { -// interface ExtendButtonTypeMap { -// bgcolor?: string -// } -// } diff --git a/packages/types/lib/client.d.ts b/packages/types/lib/client.d.ts index fcbb7c9f0..f5ff4867e 100644 --- a/packages/types/lib/client.d.ts +++ b/packages/types/lib/client.d.ts @@ -1,10 +1,25 @@ import * as React from 'react' -import type { ButtonProps, SxProps, Theme } from '@mui/material' +import type { + ButtonProps, + FormControlProps, + SxProps, + Theme, + SelectProps, +} from '@mui/material' +import type { SelectInputProps } from '@mui/material/Select/SelectInput' import { SystemStyleObject } from '@mui/system' import { UAssets } from '@services/Assets' import { Config } from './config' -import { AdvCategories, Permissions } from '@rm/types' +import { + AdvCategories, + BaseFilter, + Categories, + ObjectPathValue, + Permissions, + PokemonFilter, +} from '@rm/types' +import { UseStorage, UseStoragePaths } from '@store/useStorage' declare global { declare const CONFIG: Config @@ -25,7 +40,7 @@ export type Theme = 'light' | 'dark' export type TileLayer = { name: string - style: import('@rm/types').Theme + style?: import('@rm/types').Theme attribution?: string url?: string background?: string @@ -86,3 +101,54 @@ export interface FilterObj { } export type ClientFilterObj = Record> + +export type useGetDeepStore = ( + field: T, + defaultValue?: ObjectPathValue, +) => ObjectPathValue + +export type useSetDeepStore = ( + field: T, + value: ObjectPathValue, +) => void + +export type useDeepStore = < + T extends UseStoragePaths, + U extends ObjectPathValue, + V extends U | ((prevValue: U) => U) | (U extends object ? keyof U : never), +>( + field: T, + defaultValue?: ObjectPathValue, +) => [ + U, + (arg1: V, ...rest: V extends keyof U ? [arg2: U[V]] : [arg2?: never]) => void, +] + +export interface FCSelectProps extends SelectProps { + fcSx?: SxProps + setWidth?: (width: number) => void +} + +export interface FCSelectListItemProps + extends FCSelectProps { + icon?: React.ReactElement +} + +export type ClientFilters = T extends 'pokemon' + ? ClassToObjectType + : ClassToObjectType + +type ClassToObjectType = Partial< + Omit< + { + [K in keyof T]: T[K] + }, + keyof T extends infer K + ? K extends keyof T + ? T[K] extends Function + ? K + : never + : never + : never + > +> diff --git a/packages/types/lib/config.d.ts b/packages/types/lib/config.d.ts index be7d9bb3f..a6dce9935 100644 --- a/packages/types/lib/config.d.ts +++ b/packages/types/lib/config.d.ts @@ -17,6 +17,7 @@ import { ObjectPathValue, } from './utility' import { Strategy } from './general' +import { TileLayer } from './client' type BaseConfig = typeof config type ExampleConfig = typeof example @@ -40,10 +41,16 @@ export type Config = DeepMerge< } : never webhooks: Webhook[] + tileServers: TileLayer[] devOptions: { logLevel: LogLevelNames skipUpdateCheck?: boolean } + defaultFilters: { + s2cells: { + cells: number[] + } + } areas: ConfigAreas authentication: { areaRestrictions: { roles: string[]; areas: string[] }[] @@ -53,7 +60,7 @@ export type Config = DeepMerge< excludeFromTutorial: string[] alwaysEnabledPerms: string[] aliases: { role: string; name: string }[] - methods: string[] + methods: Strategy[] strategies: { type: Strategy trialPeriod: { @@ -65,6 +72,12 @@ export type Config = DeepMerge< blockedGuilds: string[] allowedUsers: string[] }[] + perms: { + [K in keyof BaseConfig['authentication']['perms']]: Omit< + BaseConfig['authentication']['perms'][K], + 'roles' + > & { roles: string[] } + } } api: { pvp: { diff --git a/packages/types/lib/models.d.ts b/packages/types/lib/models.d.ts index 3185af533..5e56ac527 100644 --- a/packages/types/lib/models.d.ts +++ b/packages/types/lib/models.d.ts @@ -29,6 +29,7 @@ export interface User { strategy?: Strategy data?: string | object selectedWebhook?: string + tutorial?: boolean } export type FullUser = FullModel diff --git a/packages/types/lib/utility.d.ts b/packages/types/lib/utility.d.ts index bc8277df8..d3b18f7b8 100644 --- a/packages/types/lib/utility.d.ts +++ b/packages/types/lib/utility.d.ts @@ -80,67 +80,27 @@ export type ComparisonReport = } : boolean -export type DeepKeys = { - [K in keyof T]-?: K extends string - ? P extends '' - ? `${K}` | `${K}.${DeepKeys}` - : `${P}.${K}.${DeepKeys}` - : never -}[keyof T] - -export type PathValue = P extends `${infer K}.${infer Rest}` - ? K extends keyof T - ? Rest extends DeepKeys - ? PathValue - : never - : never - : P extends keyof T - ? T[P] - : never - -export type ObjectPathValue> = PathValue< - T, - P -> - export type Join = K extends string | number ? P extends string | number ? `${K}${'' extends P ? '' : '.'}${P}` : never : never -export type Prev = [ - never, - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - ...0[], -] - -export type Paths = [D] extends [never] - ? never - : T extends object - ? { - [K in keyof T]-?: K extends string | number - ? `${K}` | Join> - : never - }[keyof T] - : '' +export type Paths = T extends object + ? { + [K in keyof T]-?: K extends string | number + ? `${K}` | Join> + : never + }[keyof T] + : '' + +export type ObjectPathValue< + O, + P extends string, +> = P extends `${infer K}.${infer Rest}` + ? K extends keyof O + ? ObjectPathValue + : never + : P extends keyof O + ? O[P] + : never diff --git a/packages/vite-plugins/lib/favicon.js b/packages/vite-plugins/lib/favicon.js index 37fe21d13..53fb5513c 100644 --- a/packages/vite-plugins/lib/favicon.js +++ b/packages/vite-plugins/lib/favicon.js @@ -1,5 +1,5 @@ // @ts-check -const { resolve } = require('path') +const path = require('path') const fs = require('fs') const { log, TAGS } = require('@rm/logger') @@ -9,15 +9,24 @@ const { log, TAGS } = require('@rm/logger') * @returns {import('vite').Plugin} */ const faviconPlugin = (isDevelopment) => { - const basePath = resolve(__dirname, '../../../public/favicon') - const fallback = resolve(basePath, `fallback.ico`) + const basePath = path.join(__dirname, '../../../public/favicon') + const markerPath = path.join( + __dirname, + '../../../node_modules/leaflet/dist/images/marker-icon.png', + ) + const fallback = path.join(basePath, `fallback.ico`) const custom = process.env.NODE_CONFIG_ENV - ? resolve(basePath, `${process.env.NODE_CONFIG_ENV}.ico`) - : resolve(basePath, `favicon.ico`) + ? path.join(basePath, `${process.env.NODE_CONFIG_ENV}.ico`) + : path.join(basePath, `favicon.ico`) const favicon = fs.existsSync(custom) ? custom : fallback return { name: 'vite-plugin-favicon', generateBundle() { + this.emitFile({ + type: 'asset', + fileName: 'images/fallback-marker.png', + source: fs.readFileSync(markerPath), + }) if (isDevelopment) return try { this.emitFile({ @@ -36,6 +45,11 @@ const faviconPlugin = (isDevelopment) => { res.end(fs.readFileSync(favicon)) return } + if (req.url === '/images/fallback-marker.png') { + res.writeHead(200, { 'Content-Type': 'image/png' }) + res.end(fs.readFileSync(markerPath)) + return + } next() }) }, diff --git a/server/src/utils/getServerSettings.js b/server/src/utils/getServerSettings.js index 0761721e8..52d68715e 100644 --- a/server/src/utils/getServerSettings.js +++ b/server/src/utils/getServerSettings.js @@ -10,11 +10,12 @@ const generateUi = require('../ui/primary') * @param {import("express").Request} req */ function getServerSettings(req) { - const user = { - ...(req.user ? req.user : req.session), - loggedIn: !!req.user, - cooldown: req.session?.cooldown || 0, - } + const user = + /** @type {import('@rm/types').ExpressUser & { loggedIn: boolean; cooldown: number }} */ ({ + ...(req.user ? req.user : req.session), + loggedIn: !!req.user, + cooldown: req.session?.cooldown || 0, + }) const { clientValues, clientMenus } = clientOptions(user.perms) diff --git a/src/assets/constants.js b/src/assets/constants.js index 2842a3bee..a08f9d884 100644 --- a/src/assets/constants.js +++ b/src/assets/constants.js @@ -1,3 +1,5 @@ +// @ts-check + export const ICON_SIZES = /** @type {const} */ (['sm', 'md', 'lg', 'xl']) export const XXS_XXL = /** @type {const} */ (['xxs', 'xxl']) diff --git a/src/assets/fallbackMarker.js b/src/assets/fallbackMarker.js index 2ef3d56a3..885ec0d20 100644 --- a/src/assets/fallbackMarker.js +++ b/src/assets/fallbackMarker.js @@ -1,8 +1,9 @@ -import markerIconPng from 'leaflet/dist/images/marker-icon.png' +// @ts-check + import { Icon } from 'leaflet' export const fallbackMarker = new Icon({ - iconUrl: markerIconPng, + iconUrl: '/images/fallback-marker.png', iconSize: [25, 41], iconAnchor: [12, 35], popupAnchor: [1, -30], diff --git a/src/assets/theme.js b/src/assets/theme.js index 280cfc9b5..5222a9600 100644 --- a/src/assets/theme.js +++ b/src/assets/theme.js @@ -181,6 +181,7 @@ export function useCustomTheme() { main: '#2AB5F6', contrastText: '#fff', }, + // TODO: Augment Mui Types discord: { main: '#5865F2', green: '#57F287', diff --git a/src/components/Config.jsx b/src/components/Config.jsx index 2fce99826..c140e48f0 100644 --- a/src/components/Config.jsx +++ b/src/components/Config.jsx @@ -1,3 +1,5 @@ +// @ts-check + import * as React from 'react' import { useTranslation } from 'react-i18next' import { setUser } from '@sentry/react' @@ -28,9 +30,8 @@ export function Config({ children }) { const getServerSettings = async () => { const data = await getSettings() - if (data) { + if (data && !('error' in data)) { document.title = data?.map?.general.headerTitle || document.title - analytics( 'User', data.user ? `${data.user.username} (${data.user.id})` : 'Not Logged In', @@ -66,10 +67,12 @@ export function Config({ children }) { data.map.general.startLat, data.map.general.startLon, ]) - const location = updatePositionState(defaultLocation, 'location').map( - (x, i) => - x || - (i === 0 ? data.map.general.startLat : data.map.general.startLon), + const location = /** @type {[number, number]} */ ( + updatePositionState(defaultLocation, 'location').map( + (x, i) => + x || + (i === 0 ? data.map.general.startLat : data.map.general.startLon), + ) ) const zoom = updatePositionState(data.map.general.startZoom, 'zoom') @@ -113,7 +116,7 @@ export function Config({ children }) { ? JSON.parse(data.user?.data) : data.user?.data : {}, - counts: data.authReferences || {}, + counts: data.authReferences, userBackupLimits: data.database.settings.userBackupLimits || 0, excludeList: data.authentication.excludeList || [], }, @@ -127,12 +130,12 @@ export function Config({ children }) { timeOfDay: timeCheck(...location), config: { ...data.map, + loginPage: null, holidayEffects: (data.map.holidayEffects || []).filter(checkHoliday), }, polling: data.api.polling, settings, gymValidDataLimit: data.api.gymValidDataLimit, - tutorialExcludeList: data.authentication.excludeFromTutorial || [], }) useStorage.setState((prev) => ({ diff --git a/src/components/I.jsx b/src/components/I.jsx index da04124e1..7987605ff 100644 --- a/src/components/I.jsx +++ b/src/components/I.jsx @@ -1,5 +1,9 @@ +// @ts-check + import { styled } from '@mui/material/styles' +// TODO: Figure out how to type this + /** @type {import('react').FC} */ export const I = styled('i', { shouldForwardProp: (prop) => prop !== 'color' && prop !== 'size', diff --git a/src/components/Menu.jsx b/src/components/Menu.jsx index f4216f506..ccf139208 100644 --- a/src/components/Menu.jsx +++ b/src/components/Menu.jsx @@ -1,3 +1,5 @@ +// @ts-check + import * as React from 'react' import Box from '@mui/material/Box' import DialogContent from '@mui/material/DialogContent' @@ -26,7 +28,7 @@ import { GenericSearch } from './inputs/GenericSearch' * category: T * webhookCategory?: string * children: (index: number, key: string) => React.ReactNode - * categories?: import('@rm/types').Available[] + * categories?: (keyof import('@rm/types').Available)[] * extraButtons?: import('@components/dialogs/Footer').FooterButton[] * }} props */ diff --git a/src/components/StatusIcon.jsx b/src/components/StatusIcon.jsx index 648e244d1..f20a20503 100644 --- a/src/components/StatusIcon.jsx +++ b/src/components/StatusIcon.jsx @@ -13,10 +13,9 @@ import RuleIcon from '@mui/icons-material/Rule' * } & import('@mui/material').SvgIconProps} StatusIconProps */ -/** @type {React.ForwardRefExoticComponent} */ export const StatusIcon = React.forwardRef( ( - { + /** @type {StatusIconProps} */ { status, color, partialColor = color || 'info', diff --git a/src/components/auth/Discord.jsx b/src/components/auth/Discord.jsx index 954c4071d..af4168db6 100644 --- a/src/components/auth/Discord.jsx +++ b/src/components/auth/Discord.jsx @@ -1,3 +1,5 @@ +// @ts-check + import * as React from 'react' import Button from '@mui/material/Button' import { useTranslation } from 'react-i18next' @@ -19,6 +21,7 @@ export function DiscordButton({ const { t } = useTranslation() return ( + // TODO: Augment Mui Types