From 381a94063123db5d62ab1295c307f2e3674bed82 Mon Sep 17 00:00:00 2001 From: Derick M <58572875+TurtIeSocks@users.noreply.github.com> Date: Mon, 15 Jan 2024 13:46:47 -0500 Subject: [PATCH 1/4] refactor: scan areas menu --- packages/types/lib/general.d.ts | 3 + server/src/graphql/resolvers.js | 24 +-- server/src/services/areas.js | 12 +- src/components/layout/drawer/Actions.jsx | 123 ++++++------- src/components/layout/drawer/Areas.jsx | 174 ------------------ src/components/layout/drawer/Section.jsx | 42 ++--- .../layout/drawer/areas/AreaTable.jsx | 111 +++++++++++ .../drawer/{AreaTile.jsx => areas/Child.jsx} | 69 +++---- src/components/layout/drawer/areas/Parent.jsx | 52 ++++++ src/components/layout/drawer/areas/index.jsx | 30 +++ .../layout/general/BasicListButton.jsx | 22 +++ src/hooks/useStorage.js | 4 +- 12 files changed, 353 insertions(+), 313 deletions(-) delete mode 100644 src/components/layout/drawer/Areas.jsx create mode 100644 src/components/layout/drawer/areas/AreaTable.jsx rename src/components/layout/drawer/{AreaTile.jsx => areas/Child.jsx} (67%) create mode 100644 src/components/layout/drawer/areas/Parent.jsx create mode 100644 src/components/layout/drawer/areas/index.jsx create mode 100644 src/components/layout/general/BasicListButton.jsx diff --git a/packages/types/lib/general.d.ts b/packages/types/lib/general.d.ts index 0ba326875..0bd1a0c62 100644 --- a/packages/types/lib/general.d.ts +++ b/packages/types/lib/general.d.ts @@ -15,6 +15,9 @@ export type RMFeature = Feature< hidden?: boolean parent?: string manual?: boolean + formattedName?: string + zoom?: number + center?: [number, number] key: string } > diff --git a/server/src/graphql/resolvers.js b/server/src/graphql/resolvers.js index 394fb9595..60ee0841a 100644 --- a/server/src/graphql/resolvers.js +++ b/server/src/graphql/resolvers.js @@ -324,18 +324,18 @@ const resolvers = { })) .filter((parent) => parent.children.length) - // Adds new blanks to account for area restrictions trimming some - filtered.forEach(({ children }) => { - if (children.length % 2 === 1) { - children.push({ - type: 'Feature', - properties: { - name: '', - manual: !!config.getSafe('manualAreas.length'), - }, - }) - } - }) + // // Adds new blanks to account for area restrictions trimming some + // filtered.forEach(({ children }) => { + // if (children.length % 2 === 1) { + // children.push({ + // type: 'Feature', + // properties: { + // name: '', + // manual: !!config.getSafe('manualAreas.length'), + // }, + // }) + // } + // }) return filtered } return scanAreas.filter((parent) => parent.children.length) diff --git a/server/src/services/areas.js b/server/src/services/areas.js index be7abedfd..37767de27 100644 --- a/server/src/services/areas.js +++ b/server/src/services/areas.js @@ -255,7 +255,17 @@ const getAreas = async () => { const scanAreasMenu = Object.fromEntries( Object.entries(scanAreas).map(([domain, featureCollection]) => { - const parents = { '': { children: [], name: '' } } + const parents = { + '': { + children: + /** @type {Pick[]} */ ([]), + name: '', + details: + /** @type {Pick | null} */ ( + null + ), + }, + } const noHiddenFeatures = { ...featureCollection, diff --git a/src/components/layout/drawer/Actions.jsx b/src/components/layout/drawer/Actions.jsx index f4ad00227..babc6f6d4 100644 --- a/src/components/layout/drawer/Actions.jsx +++ b/src/components/layout/drawer/Actions.jsx @@ -1,13 +1,9 @@ -import React, { forwardRef } from 'react' +import * as React from 'react' import List from '@mui/material/List' -import ListItemButton from '@mui/material/ListItemButton' -import ListItemIcon from '@mui/material/ListItemIcon' -import ListItemText from '@mui/material/ListItemText' import Divider from '@mui/material/Divider' import MuiLink from '@mui/material/Link' import { Link } from 'react-router-dom' -import { useTranslation } from 'react-i18next' import AccountBoxIcon from '@mui/icons-material/AccountBox' import ExitToAppIcon from '@mui/icons-material/ExitToApp' import HelpOutlineIcon from '@mui/icons-material/HelpOutline' @@ -22,7 +18,9 @@ import { useMemory } from '@hooks/useMemory' import { useLayoutStore } from '@hooks/useLayoutStore' import { useStorage } from '@hooks/useStorage' import { I } from '../general/I' +import { BasicListButton } from '../general/BasicListButton' +/** @type {React.ChangeEventHandler} */ const importSettings = (e) => { const file = e.target.files[0] if (!file) { @@ -32,7 +30,7 @@ const importSettings = (e) => { reader.onload = function parse(newSettings) { const contents = newSettings.target.result localStorage.clear() - localStorage.setItem('local-state', contents) + localStorage.setItem('local-state', contents.toString()) } reader.readAsText(file) setTimeout(() => window.location.reload(), 1500) @@ -53,12 +51,11 @@ const exportSettings = () => { document.body.removeChild(el) } -const renderLink = forwardRef(({ to, ...itemProps }, ref) => ( +const renderLink = React.forwardRef(({ to, ...itemProps }, ref) => ( )) export default function DrawerActions() { - const { t } = useTranslation() const { auth: { loggedIn, methods }, config, @@ -67,22 +64,20 @@ export default function DrawerActions() { return ( {config.misc.enableUserProfile && ( - useLayoutStore.setState({ userProfile: true })} + label="profile" > - - - - - + + )} {config.misc.enableTutorial && ( - useStorage.setState({ tutorial: true })}> - - - - - + useStorage.setState({ tutorial: true })} + label="tutorial" + > + + )} - - - - - - - - - - - - - + + + + + + + + useLayoutStore.setState({ resetFilters: true })} + label="reset_filters" > - - - - - + + + {!!methods.length && ( - - - - - - + + )} {!config.misc.rude && ( - - - - - - + + )} {config.links.statsLink && ( - - - - - - + + )} {config.links.feedbackLink && ( - useLayoutStore.setState({ feedback: true })} + label="feedback" > - - - - - + + )} {config.links.discordLink && ( - - - - - - + + )} ) diff --git a/src/components/layout/drawer/Areas.jsx b/src/components/layout/drawer/Areas.jsx deleted file mode 100644 index c2f67d093..000000000 --- a/src/components/layout/drawer/Areas.jsx +++ /dev/null @@ -1,174 +0,0 @@ -/* eslint-disable react/no-array-index-key */ -import * as React from 'react' -import { useMap } from 'react-leaflet' -import { useQuery } from '@apollo/client' -import { - ListItemButton, - ListItemIcon, - ListItemText, - ListItem, - TableContainer, - Table, - TableBody, - TableRow, - Collapse, - TableCell, - Paper, -} from '@mui/material' -import { useTranslation } from 'react-i18next' -import RestartAltIcon from '@mui/icons-material/RestartAlt' - -import Query from '@services/Query' -import { useMemory } from '@hooks/useMemory' -import { useStorage } from '@hooks/useStorage' -import AreaTile from './AreaTile' -import { GenericSearch } from './ItemSearch' - -function AreaDropDown() { - const { data, loading, error } = useQuery(Query.scanAreasMenu()) - const { t } = useTranslation() - const filters = useStorage((s) => s.filters) - const { setAreas } = useStorage.getState() - const { config } = useMemory.getState() - const map = useMap() - const [open, setOpen] = React.useState('') - - const allAreas = React.useMemo( - () => - data?.scanAreasMenu.flatMap((parent) => - parent.children - .filter((child) => !child.properties.manual) - .map((child) => child.properties.key), - ) || [], - [data], - ) - - if (loading || error) return null - - return ( - <> - setAreas()}> - - - - - - - - - - - ({ - borderTop: 1, - borderColor: - theme.palette.grey[theme.palette.mode === 'dark' ? 800 : 200], - })} - > - - {data?.scanAreasMenu - ?.map((area) => ({ - ...area, - children: area.children.filter( - (feature) => - filters.scanAreas?.filter?.search === '' || - feature.properties?.key - ?.toLowerCase() - ?.includes( - filters?.scanAreas?.filter?.search?.toLowerCase(), - ), - ), - })) - .map(({ name, details, children }) => { - if (!children.length) return null - const rows = [] - for (let i = 0; i < children.length; i += 2) { - const newRow = [] - if (children[i]) newRow.push(children[i]) - if (children[i + 1]) newRow.push(children[i + 1]) - rows.push(newRow) - } - return ( - - {name && ( - - - - )} - - - -
- - {rows.map((row, i) => ( - - {row.map((feature, j) => ( - - ))} - - ))} - -
- - - - - ) - })} - - -
-
- - ) -} - -export default React.memo(AreaDropDown) diff --git a/src/components/layout/drawer/Section.jsx b/src/components/layout/drawer/Section.jsx index 8daa9dc86..82b88d12c 100644 --- a/src/components/layout/drawer/Section.jsx +++ b/src/components/layout/drawer/Section.jsx @@ -3,16 +3,11 @@ import * as React from 'react' import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import SettingsIcon from '@mui/icons-material/Settings' import TuneIcon from '@mui/icons-material/Tune' -import { - Typography, - Accordion, - AccordionSummary, - AccordionDetails, - ListItemButton, - List, - ListItemIcon, - ListItemText, -} from '@mui/material' +import Typography from '@mui/material/Typography' +import Accordion from '@mui/material/Accordion' +import AccordionSummary from '@mui/material/AccordionSummary' +import AccordionDetails from '@mui/material/AccordionDetails' +import List from '@mui/material/List' import { useTranslation } from 'react-i18next' @@ -23,9 +18,10 @@ import Utility from '@services/Utility' import SettingsMenu from './Settings' import { PokemonDrawerMemo } from './Pokemon' -import Areas from './Areas' +import Areas from './areas' import Extras from './Extras' import { BoolToggle } from './BoolToggle' +import { BasicListButton } from '../general/BasicListButton' const ADV_CATEGORIES = new Set(['pokemon', 'gyms', 'pokestops', 'nests']) @@ -92,20 +88,20 @@ const DrawerSection = ({ category }) => { }) )} {staticUserSettings && ( - - - - - - + + + )} {ADV_CATEGORIES.has(category) && ( - - - - - - + + + )} {category === 'scanAreas' && } diff --git a/src/components/layout/drawer/areas/AreaTable.jsx b/src/components/layout/drawer/areas/AreaTable.jsx new file mode 100644 index 000000000..83568683a --- /dev/null +++ b/src/components/layout/drawer/areas/AreaTable.jsx @@ -0,0 +1,111 @@ +// @ts-check +import * as React from 'react' +import { useQuery } from '@apollo/client' +import { + TableContainer, + Table, + TableBody, + TableRow, + Paper, +} from '@mui/material' + +import Query from '@services/Query' +import { useMemory } from '@hooks/useMemory' +import { useStorage } from '@hooks/useStorage' + +import { AreaParent } from './Parent' +import { AreaChild } from './Child' + +export function ScanAreasTable() { + /** @type {import('@apollo/client').QueryResult<{ scanAreasMenu: import('@rm/types').Config['areas']['scanAreasMenu'][string] }>} */ + const { data, loading, error } = useQuery(Query.scanAreasMenu()) + const search = useStorage( + (s) => s.filters.scanAreas?.filter?.search?.toLowerCase() || '', + ) + const { misc } = useMemory.getState().config + + /** @type {string[]} */ + const allAreas = React.useMemo( + () => + data?.scanAreasMenu.flatMap((parent) => + parent.children + .filter((child) => !child.properties.manual) + .map((child) => child.properties.key), + ) || [], + [data], + ) + + if (loading || error) return null + + return ( + + ({ + borderTop: 1, + borderColor: + theme.palette.grey[theme.palette.mode === 'dark' ? 800 : 200], + })} + > + + {data?.scanAreasMenu + ?.map((area) => ({ + ...area, + children: area.children.filter( + (feature) => + search === '' || + feature.properties?.key?.toLowerCase()?.includes(search), + ), + })) + .map(({ name, details, children }) => { + if (!children.length) return null + const rows = [] + for (let i = 0; i < children.length; i += 1) { + const newRow = [] + if (children[i]) newRow.push(children[i]) + if ( + children[i + 1] && + (children[i]?.properties?.name?.length || 0) + + (children[i + 1]?.properties?.name?.length || 0) < + 40 + ) + // eslint-disable-next-line no-plusplus + newRow.push(children[++i]) + rows.push(newRow) + } + return ( + + {name && ( + + + + )} + + + + + ) + })} + +
+
+ ) +} diff --git a/src/components/layout/drawer/AreaTile.jsx b/src/components/layout/drawer/areas/Child.jsx similarity index 67% rename from src/components/layout/drawer/AreaTile.jsx rename to src/components/layout/drawer/areas/Child.jsx index 5f7d16ba7..ac6315d3b 100644 --- a/src/components/layout/drawer/AreaTile.jsx +++ b/src/components/layout/drawer/areas/Child.jsx @@ -1,42 +1,52 @@ +// @ts-check import * as React from 'react' -import { - Typography, - Checkbox, - TableCell, - Button, - IconButton, -} from '@mui/material' +import Typography from '@mui/material/Typography' +import TableCell from '@mui/material/TableCell' +import Checkbox from '@mui/material/Checkbox' +import IconButton from '@mui/material/IconButton' +import Button from '@mui/material/Button' +import Grid2 from '@mui/material/Unstable_Grid2' import ExpandMoreIcon from '@mui/icons-material/ExpandMore' -import Utility from '@services/Utility' -import Grid2 from '@mui/material/Unstable_Grid2' +import { useDeepStore, useStorage } from '@hooks/useStorage' +import { useMemory } from '@hooks/useMemory' +import { useMap } from 'react-leaflet' -export default function AreaTile({ +/** + * @param {{ + * name?: string + * feature?: Pick + * allAreas?: string[] + * childAreas?: Pick[] + * borderRight?: boolean + * colSpan?: number + * }} props + */ +export function AreaChild({ name, feature, childAreas, - scanAreasZoom, allAreas, - map, borderRight, - scanAreas, - setAreas, colSpan = 1, - open, - setOpen, }) { + const scanAreas = useStorage((s) => s.filters?.scanAreas?.filter?.areas) + const zoom = useMemory((s) => s.config.general.scanAreasZoom) + const expandAllScanAreas = useMemory((s) => s.config.misc.expandAllScanAreas) + const map = useMap() + + const { setAreas } = useStorage.getState() + const [open, setOpen] = useDeepStore('scanAreasMenu', '') + if (!scanAreas) return null const hasAll = childAreas && childAreas.every( - (c) => - c.properties.manual || - scanAreas.filter.areas.includes(c.properties.key), + (c) => c.properties.manual || scanAreas.includes(c.properties.key), ) const hasSome = - childAreas && - childAreas.some((c) => scanAreas.filter.areas.includes(c.properties.key)) + childAreas && childAreas.some((c) => scanAreas.includes(c.properties.key)) const hasManual = feature?.properties?.manual || childAreas.every((c) => c.properties.manual) const color = @@ -46,6 +56,7 @@ export default function AreaTile({ const nameProp = name || feature?.properties?.formattedName || feature?.properties?.name + const hasExpand = name && !expandAllScanAreas return ( - {!name && hasManual ? null : ( + {!hasExpand && hasManual ? null : ( e.stopPropagation()} onChange={() => setAreas( @@ -126,11 +133,9 @@ export default function AreaTile({ align="center" style={{ whiteSpace: 'pre-wrap', flexGrow: 1 }} > - {feature?.properties?.reactMapFormat && nameProp - ? Utility.getProperName(nameProp) - : nameProp || <> } + {nameProp || <> } - {name && setOpen && ( + {hasExpand && ( [][] + * childAreas: Pick[] + * allAreas: string[] + * }} props + */ +export function AreaParent({ name, rows, childAreas, allAreas }) { + const search = useStorage((s) => s.filters?.scanAreas?.filter?.search || '') + const expandAllScanAreas = useMemory((s) => s.config.misc.expandAllScanAreas) + const open = useStorage((s) => s.scanAreasMenu === name) + + return ( + + + + + {rows.map((row, i) => ( + + {row.map((feature, j) => ( + + ))} + + ))} + +
+
+
+ ) +} diff --git a/src/components/layout/drawer/areas/index.jsx b/src/components/layout/drawer/areas/index.jsx new file mode 100644 index 000000000..8642da9b4 --- /dev/null +++ b/src/components/layout/drawer/areas/index.jsx @@ -0,0 +1,30 @@ +// @ts-check +import * as React from 'react' +import ListItem from '@mui/material/ListItem' +import RestartAltIcon from '@mui/icons-material/RestartAlt' + +import { useStorage } from '@hooks/useStorage' +import { BasicListButton } from '@components/layout/general/BasicListButton' + +import { GenericSearch } from '../ItemSearch' +import { ScanAreasTable } from './AreaTable' + +const onClick = () => useStorage.getState().setAreas() + +function AreaDropDown() { + return ( + <> + + + + + + + + + + + ) +} + +export default React.memo(AreaDropDown) diff --git a/src/components/layout/general/BasicListButton.jsx b/src/components/layout/general/BasicListButton.jsx new file mode 100644 index 000000000..6f38e971b --- /dev/null +++ b/src/components/layout/general/BasicListButton.jsx @@ -0,0 +1,22 @@ +// @ts-check +import * as React from 'react' +import ListItem from '@mui/material/ListItem' +import ListItemButton from '@mui/material/ListItemButton' +import ListItemIcon from '@mui/material/ListItemIcon' +import ListItemText from '@mui/material/ListItemText' +import { useTranslation } from 'react-i18next' + +/** + * @param {{ label?: string } & import('@mui/material').ListItemButtonProps} props + */ +export function BasicListButton({ children, label, ...props }) { + const { t } = useTranslation() + return ( + + + {children && {children}} + {label && } + + + ) +} diff --git a/src/hooks/useStorage.js b/src/hooks/useStorage.js index bf4fd6e20..0d8eda4da 100644 --- a/src/hooks/useStorage.js +++ b/src/hooks/useStorage.js @@ -12,6 +12,7 @@ import { persist, createJSONStorage } from 'zustand/middleware' * popups: Record, * zoom: number, * sidebar: string, + * scanAreasMenu: string, * selectedWebhook: string, * settings: { * navigationControls: 'react' | 'leaflet' @@ -33,7 +34,7 @@ import { persist, createJSONStorage } from 'zustand/middleware' * profiling: boolean * stateLogging: boolean * desktopNotifications: boolean - * setAreas: (areas: string | string[], validAreas: string[], unselectAll?: boolean) => void, + * setAreas: (areas?: string | string[], validAreas?: string[], unselectAll?: boolean) => void, * setPokemonFilterMode: (legacyFilter: boolean, easyMode: boolean) => void, * getPokemonFilterMode: () => 'basic' | 'intermediate' | 'expert', * }} UseStorage @@ -118,6 +119,7 @@ export const useStorage = create( menus: {}, tutorial: true, sidebar: '', + scanAreasMenu: '', advMenu: { pokemon: 'others', gyms: 'categories', From f18f5c39955eded57785d75a8b73a2f33507501b Mon Sep 17 00:00:00 2001 From: Derick M <58572875+TurtIeSocks@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:00:59 -0500 Subject: [PATCH 2/4] refactor: more memoizing/cleanup --- .../layout/drawer/areas/AreaTable.jsx | 107 +++++++++++------- src/components/layout/drawer/areas/Parent.jsx | 28 +---- 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/components/layout/drawer/areas/AreaTable.jsx b/src/components/layout/drawer/areas/AreaTable.jsx index 83568683a..75e372354 100644 --- a/src/components/layout/drawer/areas/AreaTable.jsx +++ b/src/components/layout/drawer/areas/AreaTable.jsx @@ -35,6 +35,38 @@ export function ScanAreasTable() { [data], ) + const allRows = React.useMemo( + () => + (data?.scanAreasMenu || []) + .map((area) => ({ + ...area, + children: area.children.filter( + (feature) => + search === '' || + feature.properties?.key?.toLowerCase()?.includes(search), + ), + })) + .map(({ children, ...rest }) => { + const rows = [] + for (let i = 0; i < children.length; i += 1) { + const newRow = [] + if (children[i]) newRow.push(children[i]) + if ( + children[i + 1] && + (children[i]?.properties?.name?.length || 0) + + (children[i + 1]?.properties?.name?.length || 0) < + 40 + ) + // eslint-disable-next-line no-plusplus + newRow.push(children[++i]) + rows.push(newRow) + } + return { ...rest, children, rows } + }), + [data, search], + ) + + console.log(allRows) if (loading || error) return null return ( @@ -55,55 +87,44 @@ export function ScanAreasTable() { })} > - {data?.scanAreasMenu - ?.map((area) => ({ - ...area, - children: area.children.filter( - (feature) => - search === '' || - feature.properties?.key?.toLowerCase()?.includes(search), - ), - })) - .map(({ name, details, children }) => { - if (!children.length) return null - const rows = [] - for (let i = 0; i < children.length; i += 1) { - const newRow = [] - if (children[i]) newRow.push(children[i]) - if ( - children[i + 1] && - (children[i]?.properties?.name?.length || 0) + - (children[i + 1]?.properties?.name?.length || 0) < - 40 - ) - // eslint-disable-next-line no-plusplus - newRow.push(children[++i]) - rows.push(newRow) - } - return ( - - {name && ( - - - - )} + {allRows.map(({ name, details, children, rows }) => { + if (!children.length) return null + return ( + + {name && ( - - - ) - })} + )} + + + {rows.map((row, i) => ( + + {row.map((feature, j) => ( + + ))} + + ))} + + + + ) + })} diff --git a/src/components/layout/drawer/areas/Parent.jsx b/src/components/layout/drawer/areas/Parent.jsx index f097c3d15..d22545501 100644 --- a/src/components/layout/drawer/areas/Parent.jsx +++ b/src/components/layout/drawer/areas/Parent.jsx @@ -1,19 +1,16 @@ // @ts-check import * as React from 'react' -import { Table, TableBody, TableRow, Collapse, TableCell } from '@mui/material' +import { Table, TableBody, Collapse, TableCell } from '@mui/material' import { useMemory } from '@hooks/useMemory' import { useStorage } from '@hooks/useStorage' -import { AreaChild } from './Child' /** * @param {{ * name: string - * rows: Pick[][] - * childAreas: Pick[] - * allAreas: string[] + * children: React.ReactNode * }} props */ -export function AreaParent({ name, rows, childAreas, allAreas }) { +export function AreaParent({ name, children }) { const search = useStorage((s) => s.filters?.scanAreas?.filter?.search || '') const expandAllScanAreas = useMemory((s) => s.config.misc.expandAllScanAreas) const open = useStorage((s) => s.scanAreasMenu === name) @@ -27,24 +24,7 @@ export function AreaParent({ name, rows, childAreas, allAreas }) { sx={{ width: '100%' }} > - - {rows.map((row, i) => ( - - {row.map((feature, j) => ( - - ))} - - ))} - + {children}
From e572b3b6f405d87cfd553797f8b081cbd6812b2d Mon Sep 17 00:00:00 2001 From: Derick M <58572875+TurtIeSocks@users.noreply.github.com> Date: Mon, 15 Jan 2024 14:01:14 -0500 Subject: [PATCH 3/4] fix: remove log --- src/components/layout/drawer/areas/AreaTable.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/layout/drawer/areas/AreaTable.jsx b/src/components/layout/drawer/areas/AreaTable.jsx index 75e372354..b68e08a0c 100644 --- a/src/components/layout/drawer/areas/AreaTable.jsx +++ b/src/components/layout/drawer/areas/AreaTable.jsx @@ -66,7 +66,6 @@ export function ScanAreasTable() { [data, search], ) - console.log(allRows) if (loading || error) return null return ( From 315508d841d2289c5096581c8d5afe0582516ff2 Mon Sep 17 00:00:00 2001 From: Derick M <58572875+TurtIeSocks@users.noreply.github.com> Date: Mon, 15 Jan 2024 17:49:51 -0500 Subject: [PATCH 4/4] fix: export settings --- src/components/layout/drawer/Actions.jsx | 16 ++-------------- src/services/functions/downloadJson.js | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/src/components/layout/drawer/Actions.jsx b/src/components/layout/drawer/Actions.jsx index babc6f6d4..f35eca7fc 100644 --- a/src/components/layout/drawer/Actions.jsx +++ b/src/components/layout/drawer/Actions.jsx @@ -36,20 +36,8 @@ const importSettings = (e) => { setTimeout(() => window.location.reload(), 1500) } -const exportSettings = () => { - const json = localStorage.getItem('local-state') - downloadJson(json, 'settings.json') - const el = document.createElement('a') - el.setAttribute( - 'href', - `data:application/json;chartset=utf-8,${encodeURIComponent(json)}`, - ) - el.setAttribute('download', 'settings.json') - el.style.display = 'none' - document.body.appendChild(el) - el.click() - document.body.removeChild(el) -} +const exportSettings = () => + downloadJson(localStorage.getItem('local-state'), 'settings.json') const renderLink = React.forwardRef(({ to, ...itemProps }, ref) => ( diff --git a/src/services/functions/downloadJson.js b/src/services/functions/downloadJson.js index 26e3f7d71..9cbb135e6 100644 --- a/src/services/functions/downloadJson.js +++ b/src/services/functions/downloadJson.js @@ -5,14 +5,16 @@ * @param {`${string}.json`} fileName */ export function downloadJson(json, fileName) { - const el = document.createElement('a') - el.setAttribute( - 'href', - `data:application/json;chartset=utf-8,${encodeURIComponent(json)}`, - ) - el.setAttribute('download', fileName) - el.style.display = 'none' - document.body.appendChild(el) - el.click() - document.body.removeChild(el) + if (json) { + const el = document.createElement('a') + el.setAttribute( + 'href', + `data:application/json;charset=utf-8,${encodeURIComponent(json)}`, + ) + el.setAttribute('download', fileName) + el.style.display = 'none' + document.body.appendChild(el) + el.click() + document.body.removeChild(el) + } }