diff --git a/src/servers/helpers/DuplicatedServersModal.tsx b/src/servers/helpers/DuplicatedServersModal.tsx index 858e7993..e1333272 100644 --- a/src/servers/helpers/DuplicatedServersModal.tsx +++ b/src/servers/helpers/DuplicatedServersModal.tsx @@ -33,7 +33,7 @@ export const DuplicatedServersModal: FC = ( - + diff --git a/src/servers/helpers/ImportServersBtn.scss b/src/servers/helpers/ImportServersBtn.scss deleted file mode 100644 index 32b254a8..00000000 --- a/src/servers/helpers/ImportServersBtn.scss +++ /dev/null @@ -1,5 +0,0 @@ -.import-servers-btn__csv-select { - position: absolute; - left: -9999px; - top: -9999px; -} diff --git a/src/servers/helpers/ImportServersBtn.tsx b/src/servers/helpers/ImportServersBtn.tsx index d7a52ef7..a2c54e4c 100644 --- a/src/servers/helpers/ImportServersBtn.tsx +++ b/src/servers/helpers/ImportServersBtn.tsx @@ -1,14 +1,13 @@ import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useElementRef, useToggle } from '@shlinkio/shlink-frontend-kit'; -import { complement, pipe } from 'ramda'; +import { complement } from 'ramda'; import type { ChangeEvent, FC, PropsWithChildren } from 'react'; -import { useEffect, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { Button, UncontrolledTooltip } from 'reactstrap'; import type { ServerData, ServersMap } from '../data'; import type { ServersImporter } from '../services/ServersImporter'; import { DuplicatedServersModal } from './DuplicatedServersModal'; -import './ImportServersBtn.scss'; export type ImportServersBtnProps = PropsWithChildren<{ onImport?: () => void; @@ -25,7 +24,7 @@ interface ImportServersBtnConnectProps extends ImportServersBtnProps { const serversFiltering = (servers: ServerData[]) => ({ url, apiKey }: ServerData) => servers.some((server) => server.url === url && server.apiKey === apiKey); -export const ImportServersBtn = ({ importServersFromFile }: ServersImporter): FC => ({ +export const ImportServersBtn = (serversImporter: ServersImporter): FC => ({ createServers, servers, children, @@ -35,36 +34,43 @@ export const ImportServersBtn = ({ importServersFromFile }: ServersImporter): FC className = '', }) => { const ref = useElementRef(); - const [serversToCreate, setServersToCreate] = useState(); const [duplicatedServers, setDuplicatedServers] = useState([]); const [isModalOpen,, showModal, hideModal] = useToggle(); - const create = pipe(createServers, onImport); - const createAllServers = pipe(() => create(serversToCreate ?? []), hideModal); - const createNonDuplicatedServers = pipe( - () => create((serversToCreate ?? []).filter(complement(serversFiltering(duplicatedServers)))), - hideModal, + + const serversToCreate = useRef([]); + const create = useCallback((serversData: ServerData[]) => { + createServers(serversData); + onImport(); + }, [createServers, onImport]); + const onFile = useCallback( + async ({ target }: ChangeEvent) => + serversImporter.importServersFromFile(target.files?.[0]) + .then((newServers) => { + serversToCreate.current = newServers; + + const existingServers = Object.values(servers); + const dupServers = newServers.filter(serversFiltering(existingServers)); + const hasDuplicatedServers = !!dupServers.length; + + !hasDuplicatedServers ? create(newServers) : setDuplicatedServers(dupServers); + hasDuplicatedServers && showModal(); + }) + .then(() => { + // Reset input after processing file + (target as { value: string | null }).value = null; // eslint-disable-line no-param-reassign + }) + .catch(onImportError), + [create, onImportError, servers, showModal], ); - const onFile = async ({ target }: ChangeEvent) => - importServersFromFile(target.files?.[0]) - .then(setServersToCreate) - .then(() => { - // Reset input after processing file - (target as { value: string | null }).value = null; // eslint-disable-line no-param-reassign - }) - .catch(onImportError); - useEffect(() => { - if (!serversToCreate) { - return; - } - - const existingServers = Object.values(servers); - const dupServers = serversToCreate.filter(serversFiltering(existingServers)); - const hasDuplicatedServers = !!dupServers.length; - - !hasDuplicatedServers ? create(serversToCreate) : setDuplicatedServers(dupServers); - hasDuplicatedServers && showModal(); - }, [serversToCreate]); + const createAllServers = useCallback(() => { + create(serversToCreate.current); + hideModal(); + }, [create, hideModal, serversToCreate]); + const createNonDuplicatedServers = () => { + create(serversToCreate.current.filter(complement(serversFiltering(duplicatedServers)))); + hideModal(); + }; return ( <> @@ -72,16 +78,10 @@ export const ImportServersBtn = ({ importServersFromFile }: ServersImporter): FC {children ?? 'Import from file'} - You can create servers by importing a CSV file with columns name, apiKey and url. + You can create servers by importing a CSV file with name, apiKey and url columns. - + ', () => { header: 'Duplicated servers', firstParagraph: 'The next servers already exist:', lastParagraph: 'Do you want to ignore duplicated servers?', - discardBtn: 'Ignore duplicated', + discardBtn: 'Ignore duplicates', }, ], ])('renders expected texts based on amount of servers', (duplicatedServers, assertions) => {